Created
October 18, 2013 02:08
-
-
Save 6174/7035418 to your computer and use it in GitHub Desktop.
kissy seed.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright 2013, KISSY UI Library v1.40dev | |
MIT Licensed | |
build time: Aug 13 19:05 | |
*/ | |
/** | |
* @ignore | |
* A seed where KISSY grows up from, KISS Yeah ! | |
* @author https://github.com/kissyteam?tab=members | |
*/ | |
/** | |
* The KISSY global namespace object. you can use | |
* | |
* for example: | |
* @example | |
* KISSY.each/mix | |
* | |
* to do basic operation. or | |
* | |
* for example: | |
* @example | |
* KISSY.use('overlay,node', function(S, Overlay, Node){ | |
* // | |
* }); | |
* | |
* to do complex task with modules. | |
* @singleton | |
* @class KISSY | |
*/ | |
var KISSY = (function (undefined) { | |
var host = this, | |
S, | |
guid = 0, | |
EMPTY = ''; | |
S = { | |
/** | |
* The build time of the library. | |
* NOTICE: '20130813190516' will replace with current timestamp when compressing. | |
* @private | |
* @type {String} | |
*/ | |
__BUILD_TIME: '20130813190516', | |
/** | |
* KISSY Environment. | |
* @private | |
* @type {Object} | |
*/ | |
Env: { | |
host: host | |
}, | |
/** | |
* KISSY Config. | |
* If load kissy.js, Config.debug defaults to true. | |
* Else If load kissy-min.js, Config.debug defaults to false. | |
* @private | |
* @property {Object} Config | |
* @property {Boolean} Config.debug | |
* @member KISSY | |
*/ | |
Config: { | |
debug: '@DEBUG@', | |
fns: {} | |
}, | |
/** | |
* The version of the library. | |
* NOTICE: '1.40dev' will replace with current version when compressing. | |
* @type {String} | |
*/ | |
version: '1.40dev', | |
/** | |
* set KISSY configuration | |
* @param {Object|String} configName Config object or config key. | |
* @param {String} configName.base KISSY 's base path. Default: get from kissy(-min).js or seed(-min).js | |
* @param {String} configName.tag KISSY 's timestamp for native module. Default: KISSY 's build time. | |
* @param {Boolean} configName.debug whether to enable debug mod. | |
* @param {Boolean} configName.combine whether to enable combo. | |
* @param {Object} configName.packages Packages definition with package name as the key. | |
* @param {String} configName.packages.base Package base path. | |
* @param {String} configName.packages.tag Timestamp for this package's module file. | |
* @param {String} configName.packages.debug Whether force debug mode for current package. | |
* @param {String} configName.packages.combine Whether allow combine for current package modules. | |
* @param {String} [configName.packages.ignorePackageNameInUri=false] whether remove packageName from module request uri, | |
* can only be used in production mode. | |
* @param {Array[]} configName.map file map File url map configs. | |
* @param {Array[]} configName.map.0 A single map rule. | |
* @param {RegExp} configName.map.0.0 A regular expression to match url. | |
* @param {String|Function} configName.map.0.1 Replacement for String.replace. | |
* @param [configValue] config value. | |
* | |
* for example: | |
* @example | |
* KISSY.config({ | |
* combine: true, | |
* base: '', | |
* packages: { | |
* 'gallery': { | |
* base: 'http://a.tbcdn.cn/s/kissy/gallery/' | |
* } | |
* }, | |
* modules: { | |
* 'gallery/x/y': { | |
* requires: ['gallery/x/z'] | |
* } | |
* } | |
* }); | |
*/ | |
config: function (configName, configValue) { | |
var cfg, | |
r, | |
self = this, | |
fn, | |
Config = S.Config, | |
configFns = Config.fns; | |
if (S.isObject(configName)) { | |
S.each(configName, function (configValue, p) { | |
fn = configFns[p]; | |
if (fn) { | |
fn.call(self, configValue); | |
} else { | |
Config[p] = configValue; | |
} | |
}); | |
} else { | |
cfg = configFns[configName]; | |
if (configValue === undefined) { | |
if (cfg) { | |
r = cfg.call(self); | |
} else { | |
r = Config[configName]; | |
} | |
} else { | |
if (cfg) { | |
r = cfg.call(self, configValue); | |
} else { | |
Config[configName] = configValue; | |
} | |
} | |
} | |
return r; | |
}, | |
/** | |
* Prints debug info. | |
* @param msg {String} the message to log. | |
* @param {String} [cat] the log category for the message. Default | |
* categories are 'info', 'warn', 'error', 'time' etc. | |
* @param {String} [src] the source of the the message (opt) | |
*/ | |
log: function (msg, cat, src) { | |
if (S.Config.debug) { | |
if (src) { | |
msg = src + ': ' + msg; | |
} | |
if (host['console'] !== undefined && console.log) { | |
console[cat && console[cat] ? cat : 'log'](msg); | |
} | |
} | |
}, | |
/** | |
* Throws error message. | |
*/ | |
error: function (msg) { | |
if (S.Config.debug) { | |
// with stack info! | |
throw msg instanceof Error ? msg : new Error(msg); | |
} | |
}, | |
/* | |
* Generate a global unique id. | |
* @param {String} [pre] guid prefix | |
* @return {String} the guid | |
*/ | |
guid: function (pre) { | |
return (pre || EMPTY) + guid++; | |
} | |
}; | |
return S; | |
})();/** | |
* @ignore | |
* object utilities of lang | |
* @author [email protected] | |
* | |
*/ | |
(function (S, undefined) { | |
var MIX_CIRCULAR_DETECTION = '__MIX_CIRCULAR', | |
STAMP_MARKER = '__~ks_stamped', | |
host = this, | |
TRUE = true, | |
EMPTY = '', | |
ObjectCreate = Object.create, | |
// error in native ie678, not in simulated ie9 | |
hasEnumBug = !({toString: 1}['propertyIsEnumerable']('toString')), | |
enumProperties = [ | |
'constructor', | |
'hasOwnProperty', | |
'isPrototypeOf', | |
'propertyIsEnumerable', | |
'toString', | |
'toLocaleString', | |
'valueOf' | |
]; | |
mix(S, { | |
/** | |
* stamp a object by guid | |
* @param {Object} o object needed to be stamped | |
* @param {Boolean} [readOnly] while set marker on o if marker does not exist | |
* @param {String} [marker] the marker will be set on Object | |
* @return {String} guid associated with this object | |
* @member KISSY | |
*/ | |
stamp: function (o, readOnly, marker) { | |
marker = marker || STAMP_MARKER; | |
var guid = o[marker]; | |
if (guid) { | |
return guid; | |
} else if (!readOnly) { | |
try { | |
guid = o[marker] = S.guid(marker); | |
} | |
catch (e) { | |
guid = undefined; | |
} | |
} | |
return guid; | |
}, | |
/** | |
* Get all the property names of o as array | |
* @param {Object} o | |
* @return {Array} | |
* @member KISSY | |
*/ | |
keys: function (o) { | |
var result = [], p, i; | |
for (p in o) { | |
result.push(p); | |
} | |
if (hasEnumBug) { | |
for (i = enumProperties.length - 1; i >= 0; i--) { | |
p = enumProperties[i]; | |
if (o.hasOwnProperty(p)) { | |
result.push(p); | |
} | |
} | |
} | |
return result; | |
}, | |
/** | |
* Copies all the properties of s to r. | |
* @method | |
* @param {Object} r the augmented object | |
* @param {Object} s the object need to augment | |
* @param {Boolean|Object} [ov=TRUE] whether overwrite existing property or config. | |
* @param {Boolean} [ov.overwrite=TRUE] whether overwrite existing property. | |
* @param {String[]|Function} [ov.whitelist] array of white-list properties | |
* @param {Boolean}[ov.deep=false] whether recursive mix if encounter object. | |
* @param {String[]|Function} [wl] array of white-list properties | |
* @param [deep=false] {Boolean} whether recursive mix if encounter object. | |
* @return {Object} the augmented object | |
* @member KISSY | |
* | |
* for example: | |
* @example | |
* var t = {}; | |
* S.mix({x: {y: 2, z: 4}}, {x: {y: 3, a: t}}, {deep: TRUE}) => {x: {y: 3, z: 4, a: {}}}, a !== t | |
* S.mix({x: {y: 2, z: 4}}, {x: {y: 3, a: t}}, {deep: TRUE, overwrite: false}) => {x: {y: 2, z: 4, a: {}}}, a !== t | |
* S.mix({x: {y: 2, z: 4}}, {x: {y: 3, a: t}}, 1) => {x: {y: 3, a: t}} | |
*/ | |
mix: function (r, s, ov, wl, deep) { | |
if (typeof ov === 'object') { | |
wl = /** | |
@ignore | |
@type {String[]|Function} | |
*/ov['whitelist']; | |
deep = ov['deep']; | |
ov = ov['overwrite']; | |
} | |
if (wl && (typeof wl !== 'function')) { | |
var originalWl = wl; | |
wl = function (name, val) { | |
return S.inArray(name, originalWl) ? val : undefined; | |
}; | |
} | |
if (ov === undefined) { | |
ov = TRUE; | |
} | |
var cache = [], | |
c, | |
i = 0; | |
mixInternal(r, s, ov, wl, deep, cache); | |
while (c = cache[i++]) { | |
delete c[MIX_CIRCULAR_DETECTION]; | |
} | |
return r; | |
}, | |
/** | |
* Returns a new object containing all of the properties of | |
* all the supplied objects. The properties from later objects | |
* will overwrite those in earlier objects. Passing in a | |
* single object will create a shallow copy of it. | |
* @param {...Object} var_args objects need to be merged | |
* @return {Object} the new merged object | |
* @member KISSY | |
*/ | |
merge: function (var_args) { | |
var_args = S.makeArray(arguments); | |
var o = {}, | |
i, | |
l = var_args.length; | |
for (i = 0; i < l; i++) { | |
S.mix(o, var_args[i]); | |
} | |
return o; | |
}, | |
/** | |
* Applies prototype properties from the supplier to the receiver. | |
* @param {Object} r received object | |
* @param {...Object} var_args object need to augment | |
* {Boolean} [ov=TRUE] whether overwrite existing property | |
* {String[]} [wl] array of white-list properties | |
* @return {Object} the augmented object | |
* @member KISSY | |
*/ | |
augment: function (r, var_args) { | |
var args = S.makeArray(arguments), | |
len = args.length - 2, | |
i = 1, | |
p, | |
proto, | |
arg, | |
ov = args[len], | |
wl = args[len + 1]; | |
if (!S.isArray(wl)) { | |
ov = wl; | |
wl = undefined; | |
len++; | |
} | |
if (typeof ov !== 'boolean') { | |
ov = undefined; | |
len++; | |
} | |
for (; i < len; i++) { | |
arg = args[i]; | |
if (proto = arg.prototype) { | |
arg = {}; | |
var protoArray = S.keys(proto); | |
var protoLen = protoArray.length; | |
for (var j = 0; j < protoLen; j++) { | |
p = protoArray[j]; | |
if (p != 'constructor') { | |
arg[p] = proto[p]; | |
} | |
} | |
} | |
S.mix(r.prototype, arg, ov, wl); | |
} | |
return r; | |
}, | |
/** | |
* Utility to set up the prototype, constructor and superclass properties to | |
* support an inheritance strategy that can chain constructors and methods. | |
* Static members will not be inherited. | |
* @param r {Function} the object to modify | |
* @param s {Function} the object to inherit | |
* @param {Object} [px] prototype properties to add/override | |
* @param {Object} [sx] static properties to add/override | |
* @return r {Object} | |
* @member KISSY | |
*/ | |
extend: function (r, s, px, sx) { | |
if (!s || !r) { | |
return r; | |
} | |
var sp = s.prototype, | |
rp; | |
// in case parent does not set constructor | |
// eg: parent.prototype={}; | |
sp.constructor = s; | |
// add prototype chain | |
rp = createObject(sp, r); | |
r.prototype = S.mix(rp, r.prototype); | |
r.superclass = sp; | |
// add prototype overrides | |
if (px) { | |
S.mix(rp, px); | |
} | |
// add object overrides | |
if (sx) { | |
S.mix(r, sx); | |
} | |
return r; | |
}, | |
/** | |
* Returns the namespace specified and creates it if it doesn't exist. Be careful | |
* when naming packages. Reserved words may work in some browsers and not others. | |
* | |
* for example: | |
* @example | |
* S.namespace('KISSY.app'); // returns KISSY.app | |
* S.namespace('app.Shop'); // returns KISSY.app.Shop | |
* S.namespace('TB.app.Shop', TRUE); // returns TB.app.Shop | |
* | |
* @return {Object} A reference to the last namespace object created | |
* @member KISSY | |
*/ | |
namespace: function () { | |
var args = S.makeArray(arguments), | |
l = args.length, | |
o = null, i, j, p, | |
global = (args[l - 1] === TRUE && l--); | |
for (i = 0; i < l; i++) { | |
p = (EMPTY + args[i]).split('.'); | |
o = global ? host : this; | |
for (j = (host[p[0]] === o) ? 1 : 0; j < p.length; ++j) { | |
o = o[p[j]] = o[p[j]] || { }; | |
} | |
} | |
return o; | |
} | |
}); | |
function Empty() { | |
} | |
function createObject(proto, constructor) { | |
var newProto; | |
if (ObjectCreate) { | |
newProto = ObjectCreate(proto); | |
} else { | |
Empty.prototype = proto; | |
newProto = new Empty(); | |
} | |
newProto.constructor = constructor; | |
return newProto; | |
} | |
function mix(r, s) { | |
for (var i in s) { | |
r[i] = s[i]; | |
} | |
} | |
function mixInternal(r, s, ov, wl, deep, cache) { | |
if (!s || !r) { | |
return r; | |
} | |
var i, p, keys, len; | |
// 记录循环标志 | |
s[MIX_CIRCULAR_DETECTION] = r; | |
// 记录被记录了循环标志的对像 | |
cache.push(s); | |
// mix all properties | |
keys = S.keys(s); | |
len = keys.length; | |
for (i = 0; i < len; i++) { | |
p = keys[i]; | |
if (p != MIX_CIRCULAR_DETECTION) { | |
// no hasOwnProperty judge! | |
_mix(p, r, s, ov, wl, deep, cache); | |
} | |
} | |
return r; | |
} | |
function _mix(p, r, s, ov, wl, deep, cache) { | |
// 要求覆盖 | |
// 或者目的不存在 | |
// 或者深度mix | |
if (ov || !(p in r) || deep) { | |
var target = r[p], | |
src = s[p]; | |
// prevent never-end loop | |
if (target === src) { | |
// S.mix({},{x:undefined}) | |
if (target === undefined) { | |
r[p] = target; | |
} | |
return; | |
} | |
if (wl) { | |
src = wl.call(s, p, src); | |
} | |
// 来源是数组和对象,并且要求深度 mix | |
if (deep && src && (S.isArray(src) || S.isPlainObject(src))) { | |
if (src[MIX_CIRCULAR_DETECTION]) { | |
r[p] = src[MIX_CIRCULAR_DETECTION]; | |
} else { | |
// 目标值为对象或数组,直接 mix | |
// 否则 新建一个和源值类型一样的空数组/对象,递归 mix | |
var clone = target && (S.isArray(target) || S.isPlainObject(target)) ? | |
target : | |
(S.isArray(src) ? [] : {}); | |
r[p] = clone; | |
mixInternal(clone, src, ov, wl, TRUE, cache); | |
} | |
} else if (src !== undefined && (ov || !(p in r))) { | |
r[p] = src; | |
} | |
} | |
} | |
})(KISSY);/** | |
* @ignore | |
* array utilities of lang | |
* @author [email protected] | |
* | |
*/ | |
(function (S, undefined) { | |
var TRUE = true, | |
AP = Array.prototype, | |
indexOf = AP.indexOf, | |
lastIndexOf = AP.lastIndexOf, | |
filter = AP.filter, | |
every = AP.every, | |
some = AP.some, | |
map = AP.map, | |
FALSE = false; | |
S.mix(S, { | |
/** | |
* Executes the supplied function on each item in the array. | |
* @param object {Object} the object to iterate | |
* @param fn {Function} the function to execute on each item. The function | |
* receives three arguments: the value, the index, the full array. | |
* @param {Object} [context] | |
* @member KISSY | |
*/ | |
each: function (object, fn, context) { | |
if (object) { | |
var key, | |
val, | |
keys, | |
i = 0, | |
length = object && object.length, | |
isObj = length === undefined || S.type(object) === 'function'; | |
context = context || null; | |
if (isObj) { | |
keys = S.keys(object); | |
for (; i < keys.length; i++) { | |
key = keys[i]; | |
// can not use hasOwnProperty | |
if (fn.call(context, object[key], key, object) === FALSE) { | |
break; | |
} | |
} | |
} else { | |
for (val = object[0]; | |
i < length && fn.call(context, val, i, object) !== FALSE; val = object[++i]) { | |
} | |
} | |
} | |
return object; | |
}, | |
/** | |
* Search for a specified value within an array. | |
* @param item individual item to be searched | |
* @method | |
* @member KISSY | |
* @param {Array} arr the array of items where item will be search | |
* @return {number} item's index in array | |
*/ | |
indexOf: indexOf ? | |
function (item, arr) { | |
return indexOf.call(arr, item); | |
} : | |
function (item, arr) { | |
for (var i = 0, len = arr.length; i < len; ++i) { | |
if (arr[i] === item) { | |
return i; | |
} | |
} | |
return -1; | |
}, | |
/** | |
* Returns the index of the last item in the array | |
* that contains the specified value, -1 if the | |
* value isn't found. | |
* @method | |
* @param item individual item to be searched | |
* @param {Array} arr the array of items where item will be search | |
* @return {number} item's last index in array | |
* @member KISSY | |
*/ | |
lastIndexOf: (lastIndexOf) ? | |
function (item, arr) { | |
return lastIndexOf.call(arr, item); | |
} : | |
function (item, arr) { | |
for (var i = arr.length - 1; i >= 0; i--) { | |
if (arr[i] === item) { | |
break; | |
} | |
} | |
return i; | |
}, | |
/** | |
* Returns a copy of the array with the duplicate entries removed | |
* @param a {Array} the array to find the subset of unique for | |
* @param [override] {Boolean} if override is TRUE, S.unique([a, b, a]) => [b, a]. | |
* if override is FALSE, S.unique([a, b, a]) => [a, b] | |
* @return {Array} a copy of the array with duplicate entries removed | |
* @member KISSY | |
*/ | |
unique: function (a, override) { | |
var b = a.slice(); | |
if (override) { | |
b.reverse(); | |
} | |
var i = 0, | |
n, | |
item; | |
while (i < b.length) { | |
item = b[i]; | |
while ((n = S.lastIndexOf(item, b)) !== i) { | |
b.splice(n, 1); | |
} | |
i += 1; | |
} | |
if (override) { | |
b.reverse(); | |
} | |
return b; | |
}, | |
/** | |
* Search for a specified value index within an array. | |
* @param item individual item to be searched | |
* @param {Array} arr the array of items where item will be search | |
* @return {Boolean} the item exists in arr | |
* @member KISSY | |
*/ | |
inArray: function (item, arr) { | |
return S.indexOf(item, arr) > -1; | |
}, | |
/** | |
* Executes the supplied function on each item in the array. | |
* Returns a new array containing the items that the supplied | |
* function returned TRUE for. | |
* @member KISSY | |
* @method | |
* @param arr {Array} the array to iterate | |
* @param fn {Function} the function to execute on each item | |
* @param [context] {Object} optional context object | |
* @return {Array} The items on which the supplied function returned TRUE. | |
* If no items matched an empty array is returned. | |
* @member KISSY | |
*/ | |
filter: filter ? | |
function (arr, fn, context) { | |
return filter.call(arr, fn, context || this); | |
} : | |
function (arr, fn, context) { | |
var ret = []; | |
S.each(arr, function (item, i, arr) { | |
if (fn.call(context || this, item, i, arr)) { | |
ret.push(item); | |
} | |
}); | |
return ret; | |
}, | |
/** | |
* Executes the supplied function on each item in the array. | |
* Returns a new array containing the items that the supplied | |
* function returned for. | |
* @method | |
* @param arr {Array} the array to iterate | |
* @param fn {Function} the function to execute on each item | |
* @param [context] {Object} optional context object | |
* refer: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map | |
* @return {Array} The items on which the supplied function returned | |
* @member KISSY | |
*/ | |
map: map ? | |
function (arr, fn, context) { | |
return map.call(arr, fn, context || this); | |
} : | |
function (arr, fn, context) { | |
var len = arr.length, | |
res = new Array(len); | |
for (var i = 0; i < len; i++) { | |
var el = typeof arr == 'string' ? arr.charAt(i) : arr[i]; | |
if (el | |
|| | |
//ie<9 in invalid when typeof arr == string | |
i in arr) { | |
res[i] = fn.call(context || this, el, i, arr); | |
} | |
} | |
return res; | |
}, | |
/** | |
* Executes the supplied function on each item in the array. | |
* Returns a value which is accumulation of the value that the supplied | |
* function returned. | |
* | |
* @param arr {Array} the array to iterate | |
* @param callback {Function} the function to execute on each item | |
* @param initialValue {number} optional context object | |
* refer: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/reduce | |
* @return {Array} The items on which the supplied function returned | |
* @member KISSY | |
*/ | |
reduce: /* | |
NaN ? | |
reduce ? function(arr, callback, initialValue) { | |
return arr.reduce(callback, initialValue); | |
} : */function (arr, callback, initialValue) { | |
var len = arr.length; | |
if (typeof callback !== 'function') { | |
throw new TypeError('callback is not function!'); | |
} | |
// no value to return if no initial value and an empty array | |
if (len === 0 && arguments.length == 2) { | |
throw new TypeError('arguments invalid'); | |
} | |
var k = 0; | |
var accumulator; | |
if (arguments.length >= 3) { | |
accumulator = arguments[2]; | |
} | |
else { | |
do { | |
if (k in arr) { | |
accumulator = arr[k++]; | |
break; | |
} | |
// if array contains no values, no initial value to return | |
k += 1; | |
if (k >= len) { | |
throw new TypeError(); | |
} | |
} | |
while (TRUE); | |
} | |
while (k < len) { | |
if (k in arr) { | |
accumulator = callback.call(undefined, accumulator, arr[k], k, arr); | |
} | |
k++; | |
} | |
return accumulator; | |
}, | |
/** | |
* Tests whether all elements in the array pass the test implemented by the provided function. | |
* @method | |
* @param arr {Array} the array to iterate | |
* @param callback {Function} the function to execute on each item | |
* @param [context] {Object} optional context object | |
* @member KISSY | |
* @return {Boolean} whether all elements in the array pass the test implemented by the provided function. | |
*/ | |
every: every ? | |
function (arr, fn, context) { | |
return every.call(arr, fn, context || this); | |
} : | |
function (arr, fn, context) { | |
var len = arr && arr.length || 0; | |
for (var i = 0; i < len; i++) { | |
if (i in arr && !fn.call(context, arr[i], i, arr)) { | |
return FALSE; | |
} | |
} | |
return TRUE; | |
}, | |
/** | |
* Tests whether some element in the array passes the test implemented by the provided function. | |
* @method | |
* @param arr {Array} the array to iterate | |
* @param callback {Function} the function to execute on each item | |
* @param [context] {Object} optional context object | |
* @member KISSY | |
* @return {Boolean} whether some element in the array passes the test implemented by the provided function. | |
*/ | |
some: some ? | |
function (arr, fn, context) { | |
return some.call(arr, fn, context || this); | |
} : | |
function (arr, fn, context) { | |
var len = arr && arr.length || 0; | |
for (var i = 0; i < len; i++) { | |
if (i in arr && fn.call(context, arr[i], i, arr)) { | |
return TRUE; | |
} | |
} | |
return FALSE; | |
}, | |
/** | |
* Converts object to a TRUE array. | |
* @param o {object|Array} array like object or array | |
* @return {Array} native Array | |
* @member KISSY | |
*/ | |
makeArray: function (o) { | |
if (o == null) { | |
return []; | |
} | |
if (S.isArray(o)) { | |
return o; | |
} | |
var lengthType = typeof o.length, | |
oType = typeof o; | |
// The strings and functions also have 'length' | |
if (lengthType != 'number' || | |
// form.elements in ie78 has nodeName 'form' | |
// then caution select | |
// o.nodeName | |
// window | |
o.alert || | |
oType == 'string' || | |
// https://github.com/ariya/phantomjs/issues/11478 | |
(oType == 'function' && !( 'item' in o && lengthType == 'number'))) { | |
return [o]; | |
} | |
var ret = []; | |
for (var i = 0, l = o.length; i < l; i++) { | |
ret[i] = o[i]; | |
} | |
return ret; | |
} | |
}); | |
})(KISSY);/** | |
* @ignore | |
* escape of lang | |
* @author [email protected] | |
* | |
*/ | |
(function (S, undefined) { | |
// IE doesn't include non-breaking-space (0xa0) in their \s character | |
// class (as required by section 7.2 of the ECMAScript spec), we explicitly | |
// include it in the regexp to enforce consistent cross-browser behavior. | |
var SEP = '&', | |
EMPTY = '', | |
EQ = '=', | |
TRUE = true, | |
// FALSE = false, | |
HEX_BASE = 16, | |
// http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet | |
// http://wonko.com/post/html-escaping | |
htmlEntities = { | |
'&': '&', | |
'>': '>', | |
'<': '<', | |
'`': '`', | |
'/': '/', | |
'"': '"', | |
''': "'" | |
}, | |
reverseEntities = {}, | |
escapeReg, | |
unEscapeReg, | |
// - # $ ^ * ( ) + [ ] { } | \ , . ? | |
escapeRegExp = /[\-#$\^*()+\[\]{}|\\,.?\s]/g; | |
(function () { | |
for (var k in htmlEntities) { | |
reverseEntities[htmlEntities[k]] = k; | |
} | |
})(); | |
function isValidParamValue(val) { | |
var t = typeof val; | |
// If the type of val is null, undefined, number, string, boolean, return TRUE. | |
return val == null || (t !== 'object' && t !== 'function'); | |
} | |
function getEscapeReg() { | |
if (escapeReg) { | |
return escapeReg | |
} | |
var str = EMPTY; | |
S.each(htmlEntities, function (entity) { | |
str += entity + '|'; | |
}); | |
str = str.slice(0, -1); | |
return escapeReg = new RegExp(str, 'g'); | |
} | |
function getUnEscapeReg() { | |
if (unEscapeReg) { | |
return unEscapeReg | |
} | |
var str = EMPTY; | |
S.each(reverseEntities, function (entity) { | |
str += entity + '|'; | |
}); | |
str += '&#(\\d{1,5});'; | |
return unEscapeReg = new RegExp(str, 'g'); | |
} | |
S.mix(S, { | |
/** | |
* Call encodeURIComponent to encode a url component | |
* @param {String} s part of url to be encoded. | |
* @return {String} encoded url part string. | |
* @member KISSY | |
*/ | |
urlEncode: function (s) { | |
return encodeURIComponent(String(s)); | |
}, | |
/** | |
* Call decodeURIComponent to decode a url component | |
* and replace '+' with space. | |
* @param {String} s part of url to be decoded. | |
* @return {String} decoded url part string. | |
* @member KISSY | |
*/ | |
urlDecode: function (s) { | |
return decodeURIComponent(s.replace(/\+/g, ' ')); | |
}, | |
/** | |
* frequently used in taobao cookie about nick | |
* @member KISSY | |
* @return {String} un-unicode string. | |
*/ | |
fromUnicode: function (str) { | |
return str.replace(/\\u([a-f\d]{4})/ig, function (m, u) { | |
return String.fromCharCode(parseInt(u, HEX_BASE)); | |
}); | |
}, | |
/** | |
* get escaped string from html. | |
* only escape | |
* & > < ` / " ' | |
* refer: | |
* | |
* [http://yiminghe.javaeye.com/blog/788929](http://yiminghe.javaeye.com/blog/788929) | |
* | |
* [http://wonko.com/post/html-escaping](http://wonko.com/post/html-escaping) | |
* @param str {string} text2html show | |
* @member KISSY | |
* @return {String} escaped html | |
*/ | |
escapeHtml: function (str) { | |
return (str + '').replace(getEscapeReg(), function (m) { | |
return reverseEntities[m]; | |
}); | |
}, | |
/** | |
* get escaped regexp string for construct regexp. | |
* @param str | |
* @member KISSY | |
* @return {String} escaped regexp | |
*/ | |
escapeRegExp: function (str) { | |
return str.replace(escapeRegExp, '\\$&'); | |
}, | |
/** | |
* un-escape html to string. | |
* only unescape | |
* & < > ` / " ' &#\d{1,5} | |
* @param str {string} html2text | |
* @member KISSY | |
* @return {String} un-escaped html | |
*/ | |
unEscapeHtml: function (str) { | |
return str.replace(getUnEscapeReg(), function (m, n) { | |
return htmlEntities[m] || String.fromCharCode(+n); | |
}); | |
}, | |
/** | |
* Creates a serialized string of an array or object. | |
* | |
* for example: | |
* @example | |
* {foo: 1, bar: 2} // -> 'foo=1&bar=2' | |
* {foo: 1, bar: [2, 3]} // -> 'foo=1&bar=2&bar=3' | |
* {foo: '', bar: 2} // -> 'foo=&bar=2' | |
* {foo: undefined, bar: 2} // -> 'foo=undefined&bar=2' | |
* {foo: TRUE, bar: 2} // -> 'foo=TRUE&bar=2' | |
* | |
* @param {Object} o json data | |
* @param {String} [sep='&'] separator between each pair of data | |
* @param {String} [eq='='] separator between key and value of data | |
* @param {Boolean} [serializeArray=true] whether add '[]' to array key of data | |
* @return {String} | |
* @member KISSY | |
*/ | |
param: function (o, sep, eq, serializeArray) { | |
sep = sep || SEP; | |
eq = eq || EQ; | |
if (serializeArray === undefined) { | |
serializeArray = TRUE; | |
} | |
var buf = [], key, i, v, len, val, | |
encode = S.urlEncode; | |
for (key in o) { | |
val = o[key]; | |
key = encode(key); | |
// val is valid non-array value | |
if (isValidParamValue(val)) { | |
buf.push(key); | |
if (val !== undefined) { | |
buf.push(eq, encode(val + EMPTY)); | |
} | |
buf.push(sep); | |
} | |
// val is not empty array | |
else if (S.isArray(val) && val.length) { | |
for (i = 0, len = val.length; i < len; ++i) { | |
v = val[i]; | |
if (isValidParamValue(v)) { | |
buf.push(key, (serializeArray ? encode('[]') : EMPTY)); | |
if (v !== undefined) { | |
buf.push(eq, encode(v + EMPTY)); | |
} | |
buf.push(sep); | |
} | |
} | |
} | |
// ignore other cases, including empty array, Function, RegExp, Date etc. | |
} | |
buf.pop(); | |
return buf.join(EMPTY); | |
}, | |
/** | |
* Parses a URI-like query string and returns an object composed of parameter/value pairs. | |
* | |
* for example: | |
* @example | |
* 'section=blog&id=45' // -> {section: 'blog', id: '45'} | |
* 'section=blog&tag=js&tag=doc' // -> {section: 'blog', tag: ['js', 'doc']} | |
* 'tag=ruby%20on%20rails' // -> {tag: 'ruby on rails'} | |
* 'id=45&raw' // -> {id: '45', raw: ''} | |
* @param {String} str param string | |
* @param {String} [sep='&'] separator between each pair of data | |
* @param {String} [eq='='] separator between key and value of data | |
* @return {Object} json data | |
* @member KISSY | |
*/ | |
unparam: function (str, sep, eq) { | |
if (typeof str != 'string' || !(str = S.trim(str))) { | |
return {}; | |
} | |
sep = sep || SEP; | |
eq = eq || EQ; | |
var ret = {}, | |
eqIndex, | |
decode = S.urlDecode, | |
pairs = str.split(sep), | |
key, val, | |
i = 0, len = pairs.length; | |
for (; i < len; ++i) { | |
eqIndex = pairs[i].indexOf(eq); | |
if (eqIndex == -1) { | |
key = decode(pairs[i]); | |
val = undefined; | |
} else { | |
// remember to decode key! | |
key = decode(pairs[i].substring(0, eqIndex)); | |
val = pairs[i].substring(eqIndex + 1); | |
try { | |
val = decode(val); | |
} catch (e) { | |
S.log(e + 'decodeURIComponent error : ' + val, 'error'); | |
} | |
if (S.endsWith(key, '[]')) { | |
key = key.substring(0, key.length - 2); | |
} | |
} | |
if (key in ret) { | |
if (S.isArray(ret[key])) { | |
ret[key].push(val); | |
} else { | |
ret[key] = [ret[key], val]; | |
} | |
} else { | |
ret[key] = val; | |
} | |
} | |
return ret; | |
} | |
}); | |
S.escapeHTML = S.escapeHtml; | |
S.unEscapeHTML = S.unEscapeHtml; | |
})(KISSY);/** | |
* @ignore | |
* function utilities of lang | |
* @author [email protected] | |
* | |
*/ | |
(function (S, undefined) { | |
function bindFn(r, fn, obj) { | |
var slice = [].slice, | |
args = slice.call(arguments, 3), | |
fNOP = function () { | |
}, | |
bound = function () { | |
var inArgs = slice.call(arguments); | |
return fn.apply( | |
this instanceof fNOP ? this : obj, | |
(r ? inArgs.concat(args) : args.concat(inArgs)) | |
); | |
}; | |
fNOP.prototype = fn.prototype; | |
bound.prototype = new fNOP(); | |
return bound; | |
} | |
S.mix(S, { | |
/** | |
* empty function | |
* @member KISSY | |
*/ | |
noop: function () { | |
}, | |
/** | |
* Creates a new function that, when called, itself calls this function in the context of the provided this value, | |
* with a given sequence of arguments preceding any provided when the new function was called. | |
* refer: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind | |
* @param {Function} fn internal called function | |
* @param {Object} obj context in which fn runs | |
* @param {*...} var_args extra arguments | |
* @member KISSY | |
* @return {Function} new function with context and arguments | |
*/ | |
bind: bindFn(0, bindFn, null, 0), | |
/** | |
* Creates a new function that, when called, itself calls this function in the context of the provided this value, | |
* with a given sequence of arguments preceding any provided when the new function was called. | |
* refer: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind | |
* @param {Function} fn internal called function | |
* @param {Object} obj context in which fn runs | |
* @param {*...} var_args extra arguments | |
* @member KISSY | |
* @return {Function} new function with context and arguments | |
*/ | |
rbind: bindFn(0, bindFn, null, 1), | |
/** | |
* Executes the supplied function in the context of the supplied | |
* object 'when' milliseconds later. Executes the function a | |
* single time unless periodic is set to true. | |
* | |
* @param fn {Function|String} the function to execute or the name of the method in | |
* the 'o' object to execute. | |
* | |
* @param [when=0] {Number} the number of milliseconds to wait until the fn is executed. | |
* | |
* @param {Boolean} [periodic] if true, executes continuously at supplied interval | |
* until canceled. | |
* | |
* @param {Object} [context] the context object. | |
* | |
* @param [data] that is provided to the function. This accepts either a single | |
* item or an array. If an array is provided, the function is executed with | |
* one parameter for each array item. If you need to pass a single array | |
* parameter, it needs to be wrapped in an array. | |
* | |
* @return {Object} a timer object. Call the cancel() method on this object to stop | |
* the timer. | |
* | |
* @member KISSY | |
*/ | |
later: function (fn, when, periodic, context, data) { | |
when = when || 0; | |
var m = fn, | |
d = S.makeArray(data), | |
f, | |
r; | |
if (typeof fn == 'string') { | |
m = context[fn]; | |
} | |
if (!m) { | |
S.error('method undefined'); | |
} | |
f = function () { | |
m.apply(context, d); | |
}; | |
r = (periodic) ? setInterval(f, when) : setTimeout(f, when); | |
return { | |
id: r, | |
interval: periodic, | |
cancel: function () { | |
if (this.interval) { | |
clearInterval(r); | |
} else { | |
clearTimeout(r); | |
} | |
} | |
}; | |
}, | |
/** | |
* Throttles a call to a method based on the time between calls. | |
* @param {Function} fn The function call to throttle. | |
* @param {Object} [context] context fn to run | |
* @param {Number} [ms] The number of milliseconds to throttle the method call. | |
* Passing a -1 will disable the throttle. Defaults to 150. | |
* @return {Function} Returns a wrapped function that calls fn throttled. | |
* @member KISSY | |
*/ | |
throttle: function (fn, ms, context) { | |
ms = ms || 150; | |
if (ms === -1) { | |
return (function () { | |
fn.apply(context || this, arguments); | |
}); | |
} | |
var last = S.now(); | |
return (function () { | |
var now = S.now(); | |
if (now - last > ms) { | |
last = now; | |
fn.apply(context || this, arguments); | |
} | |
}); | |
}, | |
/** | |
* buffers a call between a fixed time | |
* @param {Function} fn | |
* @param {Number} ms | |
* @param {Object} [context] | |
* @return {Function} Returns a wrapped function that calls fn buffered. | |
* @member KISSY | |
*/ | |
buffer: function (fn, ms, context) { | |
ms = ms || 150; | |
if (ms === -1) { | |
return function () { | |
fn.apply(context || this, arguments); | |
}; | |
} | |
var bufferTimer = null; | |
function f() { | |
f.stop(); | |
bufferTimer = S.later(fn, ms, 0, context || this, arguments); | |
} | |
f.stop = function () { | |
if (bufferTimer) { | |
bufferTimer.cancel(); | |
bufferTimer = 0; | |
} | |
}; | |
return f; | |
} | |
}); | |
})(KISSY);/** | |
* @ignore | |
* lang | |
* @author [email protected], [email protected] | |
* | |
*/ | |
(function (S, undefined) { | |
var TRUE = true, | |
FALSE = false, | |
CLONE_MARKER = '__~ks_cloned', | |
COMPARE_MARKER = '__~ks_compared'; | |
S.mix(S, { | |
/** | |
* Checks to see whether two object are equals. | |
* @param a 比较目标1 | |
* @param b 比较目标2 | |
* @param [mismatchKeys] internal usage | |
* @param [mismatchValues] internal usage | |
* @return {Boolean} a.equals(b) | |
* @member KISSY | |
*/ | |
equals: function (a, b, /*internal use*/mismatchKeys, /*internal use*/mismatchValues) { | |
// inspired by jasmine | |
mismatchKeys = mismatchKeys || []; | |
mismatchValues = mismatchValues || []; | |
if (a === b) { | |
return TRUE; | |
} | |
if (a === undefined || a === null || b === undefined || b === null) { | |
// need type coercion | |
return a == null && b == null; | |
} | |
if (a instanceof Date && b instanceof Date) { | |
return a.getTime() == b.getTime(); | |
} | |
if (typeof a == 'string' && typeof b == 'string') { | |
return (a == b); | |
} | |
if (typeof a==='number' && typeof b==='number') { | |
return (a == b); | |
} | |
if (typeof a === 'object' && typeof b === 'object') { | |
return compareObjects(a, b, mismatchKeys, mismatchValues); | |
} | |
// Straight check | |
return (a === b); | |
}, | |
/** | |
* Creates a deep copy of a plain object or array. Others are returned untouched. | |
* @param input | |
* @member KISSY | |
* @param {Function} [filter] filter function | |
* @return {Object} the new cloned object | |
* refer: http://www.w3.org/TR/html5/common-dom-interfaces.html#safe-passing-of-structured-data | |
*/ | |
clone: function (input, filter) { | |
// 稍微改改就和规范一样了 :) | |
// Let memory be an association list of pairs of objects, | |
// initially empty. This is used to handle duplicate references. | |
// In each pair of objects, one is called the source object | |
// and the other the destination object. | |
var memory = {}, | |
ret = cloneInternal(input, filter, memory); | |
S.each(memory, function (v) { | |
// 清理在源对象上做的标记 | |
v = v.input; | |
if (v[CLONE_MARKER]) { | |
try { | |
delete v[CLONE_MARKER]; | |
} catch (e) { | |
// S.log('delete CLONE_MARKER error : '); | |
v[CLONE_MARKER] = undefined; | |
} | |
} | |
}); | |
memory = null; | |
return ret; | |
}, | |
/** | |
* Gets current date in milliseconds. | |
* @method | |
* refer: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/now | |
* http://j-query.blogspot.com/2011/02/timing-ecmascript-5-datenow-function.html | |
* http://kangax.github.com/es5-compat-table/ | |
* @member KISSY | |
* @return {Number} current time | |
*/ | |
now: Date.now || function () { | |
return +new Date(); | |
} | |
}); | |
function cloneInternal(input, f, memory) { | |
var destination = input, | |
isArray, | |
isPlainObject, | |
k, | |
stamp; | |
if (!input) { | |
return destination; | |
} | |
// If input is the source object of a pair of objects in memory, | |
// then return the destination object in that pair of objects . | |
// and abort these steps. | |
if (input[CLONE_MARKER]) { | |
// 对应的克隆后对象 | |
return memory[input[CLONE_MARKER]].destination; | |
} else if (typeof input === 'object') { | |
// 引用类型要先记录 | |
var constructor = input.constructor; | |
if (S.inArray(constructor, [Boolean, String, Number, Date, RegExp])) { | |
destination = new constructor(input.valueOf()); | |
} | |
// ImageData , File, Blob , FileList .. etc | |
else if (isArray = S.isArray(input)) { | |
destination = f ? S.filter(input, f) : input.concat(); | |
} else if (isPlainObject = S.isPlainObject(input)) { | |
destination = {}; | |
} | |
// Add a mapping from input (the source object) | |
// to output (the destination object) to memory. | |
// 做标记 | |
input[CLONE_MARKER] = (stamp = S.guid()); | |
// 存储源对象以及克隆后的对象 | |
memory[stamp] = {destination: destination, input: input}; | |
} | |
// If input is an Array object or an Object object, | |
// then, for each enumerable property in input, | |
// add a new property to output having the same name, | |
// and having a value created from invoking the internal structured cloning algorithm recursively | |
// with the value of the property as the 'input' argument and memory as the 'memory' argument. | |
// The order of the properties in the input and output objects must be the same. | |
// clone it | |
if (isArray) { | |
for (var i = 0; i < destination.length; i++) { | |
destination[i] = cloneInternal(destination[i], f, memory); | |
} | |
} else if (isPlainObject) { | |
for (k in input) { | |
if (k !== CLONE_MARKER && | |
(!f || (f.call(input, input[k], k, input) !== FALSE))) { | |
destination[k] = cloneInternal(input[k], f, memory); | |
} | |
} | |
} | |
return destination; | |
} | |
function compareObjects(a, b, mismatchKeys, mismatchValues) { | |
// 两个比较过了,无需再比较,防止循环比较 | |
if (a[COMPARE_MARKER] === b && b[COMPARE_MARKER] === a) { | |
return TRUE; | |
} | |
a[COMPARE_MARKER] = b; | |
b[COMPARE_MARKER] = a; | |
var hasKey = function (obj, keyName) { | |
return (obj !== null && obj !== undefined) && obj[keyName] !== undefined; | |
}; | |
for (var property in b) { | |
if (!hasKey(a, property) && hasKey(b, property)) { | |
mismatchKeys.push("expected has key '" + property + "', but missing from actual."); | |
} | |
} | |
for (property in a) { | |
if (!hasKey(b, property) && hasKey(a, property)) { | |
mismatchKeys.push("expected missing key '" + property + "', but present in actual."); | |
} | |
} | |
for (property in b) { | |
if (property == COMPARE_MARKER) { | |
continue; | |
} | |
if (!S.equals(a[property], b[property], mismatchKeys, mismatchValues)) { | |
mismatchValues.push("'" + property + "' was '" + (b[property] ? (b[property].toString()) : b[property]) | |
+ "' in expected, but was '" + | |
(a[property] ? (a[property].toString()) : a[property]) + "' in actual."); | |
} | |
} | |
if (S.isArray(a) && S.isArray(b) && a.length != b.length) { | |
mismatchValues.push('arrays were not the same length'); | |
} | |
delete a[COMPARE_MARKER]; | |
delete b[COMPARE_MARKER]; | |
return (mismatchKeys.length === 0 && mismatchValues.length === 0); | |
} | |
})(KISSY); | |
/** | |
* @ignore | |
* string utilities of lang | |
* @author [email protected] | |
* | |
*/ | |
(function (S, undefined) { | |
// IE doesn't include non-breaking-space (0xa0) in their \s character | |
// class (as required by section 7.2 of the ECMAScript spec), we explicitly | |
// include it in the regexp to enforce consistent cross-browser behavior. | |
var RE_TRIM = /^[\s\xa0]+|[\s\xa0]+$/g, | |
trim = String.prototype.trim, | |
SUBSTITUTE_REG = /\\?\{([^{}]+)\}/g, | |
EMPTY = ''; | |
S.mix(S, { | |
/** | |
* Removes the whitespace from the beginning and end of a string. | |
* @method | |
* @member KISSY | |
*/ | |
trim: trim ? | |
function (str) { | |
return str == null ? EMPTY : trim.call(str); | |
} : | |
function (str) { | |
return str == null ? EMPTY : (str + '').replace(RE_TRIM, EMPTY); | |
}, | |
/** | |
* Substitutes keywords in a string using an object/array. | |
* Removes undefined keywords and ignores escaped keywords. | |
* @param {String} str template string | |
* @param {Object} o json data | |
* @member KISSY | |
* @param {RegExp} [regexp] to match a piece of template string | |
*/ | |
substitute: function (str, o, regexp) { | |
if (typeof str != 'string' || !o) { | |
return str; | |
} | |
return str.replace(regexp || SUBSTITUTE_REG, function (match, name) { | |
if (match.charAt(0) === '\\') { | |
return match.slice(1); | |
} | |
return (o[name] === undefined) ? EMPTY : o[name]; | |
}); | |
}, | |
/** uppercase first character. | |
* @member KISSY | |
* @param s | |
* @return {String} | |
*/ | |
ucfirst: function (s) { | |
s += ''; | |
return s.charAt(0).toUpperCase() + s.substring(1); | |
}, | |
/** | |
* test whether a string start with a specified substring | |
* @param {String} str the whole string | |
* @param {String} prefix a specified substring | |
* @return {Boolean} whether str start with prefix | |
* @member KISSY | |
*/ | |
startsWith: function (str, prefix) { | |
return str.lastIndexOf(prefix, 0) === 0; | |
}, | |
/** | |
* test whether a string end with a specified substring | |
* @param {String} str the whole string | |
* @param {String} suffix a specified substring | |
* @return {Boolean} whether str end with suffix | |
* @member KISSY | |
*/ | |
endsWith: function (str, suffix) { | |
var ind = str.length - suffix.length; | |
return ind >= 0 && str.indexOf(suffix, ind) == ind; | |
} | |
}); | |
})(KISSY);/** | |
* @ignore | |
* type of land | |
* @author [email protected], [email protected] | |
* | |
*/ | |
(function (S, undefined) { | |
// [[Class]] -> type pairs | |
var class2type = {}, | |
FALSE = false, | |
OP = Object.prototype, | |
toString = OP.toString; | |
function hasOwnProperty(o, p) { | |
return OP.hasOwnProperty.call(o, p); | |
} | |
S.mix(S, | |
{ | |
/** | |
* test whether o is boolean | |
* @method | |
* @param o | |
* @return {Boolean} | |
* @member KISSY | |
*/ | |
isBoolean: 0, | |
/** | |
* test whether o is number | |
* @method | |
* @param o | |
* @return {Boolean} | |
* @member KISSY | |
*/ | |
isNumber: 0, | |
/** | |
* test whether o is String | |
* @method | |
* @param o | |
* @return {Boolean} | |
* @member KISSY | |
*/ | |
isString: 0, | |
/** | |
* test whether o is function | |
* @method | |
* @param o | |
* @return {Boolean} | |
* @member KISSY | |
*/ | |
isFunction: 0, | |
/** | |
* test whether o is Array | |
* @method | |
* @param o | |
* @return {Boolean} | |
* @member KISSY | |
*/ | |
isArray: 0, | |
/** | |
* test whether o is Date | |
* @method | |
* @param o | |
* @return {Boolean} | |
* @member KISSY | |
*/ | |
isDate: 0, | |
/** | |
* test whether o is RegExp | |
* @method | |
* @param o | |
* @return {Boolean} | |
* @member KISSY | |
*/ | |
isRegExp: 0, | |
/** | |
* test whether o is Object | |
* @method | |
* @param o | |
* @return {Boolean} | |
* @member KISSY | |
*/ | |
isObject: 0, | |
/** | |
* Determine the internal JavaScript [[Class]] of an object. | |
* @member KISSY | |
*/ | |
type: function (o) { | |
return o == null ? | |
String(o) : | |
class2type[toString.call(o)] || 'object'; | |
}, | |
/** | |
* whether o === null | |
* @param o | |
* @member KISSY | |
*/ | |
isNull: function (o) { | |
return o === null; | |
}, | |
/** | |
* whether o === undefined | |
* @param o | |
* @member KISSY | |
*/ | |
isUndefined: function (o) { | |
return o === undefined; | |
}, | |
/** | |
* Checks to see if an object is empty. | |
* @member KISSY | |
*/ | |
isEmptyObject: function (o) { | |
for (var p in o) { | |
if (p !== undefined) { | |
return FALSE; | |
} | |
} | |
return true; | |
}, | |
/** | |
* Checks to see if an object is a plain object (created using '{}' | |
* or 'new Object()' but not 'new FunctionClass()'). | |
* @member KISSY | |
*/ | |
isPlainObject: function (obj) { | |
// credits to jq | |
// Must be an Object. | |
// Because of IE, we also have to check the presence of the constructor property. | |
// Make sure that Dom nodes and window objects don't pass through, as well | |
if (!obj || S.type(obj) !== "object" || obj.nodeType || obj.window == obj) { | |
return FALSE; | |
} | |
var key, objConstructor; | |
try { | |
// Not own constructor property must be Object | |
if ((objConstructor = obj.constructor) && !hasOwnProperty(obj, "constructor") && !hasOwnProperty(objConstructor.prototype, "isPrototypeOf")) { | |
return FALSE; | |
} | |
} catch (e) { | |
// IE8,9 Will throw exceptions on certain host objects | |
return FALSE; | |
} | |
// Own properties are enumerated firstly, so to speed up, | |
// if last one is own, then all properties are own. | |
for (key in obj) { | |
} | |
return key === undefined || hasOwnProperty(obj, key); | |
} | |
}); | |
S.each('Boolean Number String Function Array Date RegExp Object'.split(' '), | |
function (name, lc) { | |
// populate the class2type map | |
class2type['[object ' + name + ']'] = (lc = name.toLowerCase()); | |
// add isBoolean/isNumber/... | |
S['is' + name] = function (o) { | |
return S.type(o) == lc; | |
} | |
}); | |
})(KISSY);/** | |
* @ignore | |
* implement Promise specification by KISSY | |
* @author [email protected] | |
*/ | |
(function (S, undefined) { | |
var PROMISE_VALUE = '__promise_value', | |
PROMISE_PENDINGS = '__promise_pendings'; | |
/* | |
two effects: | |
1. call fulfilled with immediate value | |
2. push fulfilled in right promise | |
*/ | |
function promiseWhen(promise, fulfilled, rejected) { | |
// simply call rejected | |
if (promise instanceof Reject) { | |
// if there is a rejected , should always has! see when() | |
if (!rejected) { | |
S.error('no rejected callback!'); | |
} | |
return rejected(promise[PROMISE_VALUE]); | |
} | |
var v = promise[PROMISE_VALUE], | |
pendings = promise[PROMISE_PENDINGS]; | |
// unresolved | |
// pushed to pending list | |
if (pendings) { | |
pendings.push([fulfilled, rejected]); | |
} | |
// rejected or nested promise | |
else if (isPromise(v)) { | |
promiseWhen(v, fulfilled, rejected); | |
} else { | |
// fulfilled value | |
// normal value represents ok | |
// need return user's return value | |
// if return promise then forward | |
return fulfilled && fulfilled(v); | |
} | |
return undefined; | |
} | |
/** | |
* @class KISSY.Defer | |
* Defer constructor For KISSY,implement Promise specification. | |
*/ | |
function Defer(promise) { | |
var self = this; | |
if (!(self instanceof Defer)) { | |
return new Defer(promise); | |
} | |
// http://en.wikipedia.org/wiki/Object-capability_model | |
// principal of least authority | |
/** | |
* defer object's promise | |
* @type {KISSY.Promise} | |
*/ | |
self.promise = promise || new Promise(); | |
} | |
Defer.prototype = { | |
constructor: Defer, | |
/** | |
* fulfill defer object's promise | |
* note: can only be called once | |
* @param value defer object's value | |
* @return {KISSY.Promise} defer object's promise | |
*/ | |
resolve: function (value) { | |
var promise = this.promise, | |
pendings; | |
if (!(pendings = promise[PROMISE_PENDINGS])) { | |
return null; | |
} | |
// set current promise 's resolved value | |
// maybe a promise or instant value | |
promise[PROMISE_VALUE] = value; | |
pendings = [].concat(pendings); | |
promise[PROMISE_PENDINGS] = undefined; | |
S.each(pendings, function (p) { | |
promiseWhen(promise, p[0], p[1]); | |
}); | |
return value; | |
}, | |
/** | |
* reject defer object's promise | |
* @param reason | |
* @return {KISSY.Promise} defer object's promise | |
*/ | |
reject: function (reason) { | |
return this.resolve(new Reject(reason)); | |
} | |
}; | |
function isPromise(obj) { | |
return obj && obj instanceof Promise; | |
} | |
/** | |
* @class KISSY.Promise | |
* Promise constructor. | |
* This class should not be instantiated manually. | |
* Instances will be created and returned as needed by {@link KISSY.Defer#promise} | |
* @param [v] promise 's resolved value | |
*/ | |
function Promise(v) { | |
var self = this; | |
// maybe internal value is also a promise | |
self[PROMISE_VALUE] = v; | |
if (v === undefined) { | |
// unresolved | |
self[PROMISE_PENDINGS] = []; | |
} | |
} | |
Promise.prototype = { | |
constructor: Promise, | |
/** | |
* register callbacks when this promise object is resolved | |
* @param {Function} fulfilled called when resolved successfully,pass a resolved value to this function and | |
* return a value (could be promise object) for the new promise 's resolved value. | |
* @param {Function} [rejected] called when error occurs,pass error reason to this function and | |
* return a new reason for the new promise 's error reason | |
* @return {KISSY.Promise} a new promise object | |
*/ | |
then: function (fulfilled, rejected) { | |
return when(this, fulfilled, rejected); | |
}, | |
/** | |
* call rejected callback when this promise object is rejected | |
* @param {Function} rejected called with rejected reason | |
* @return {KISSY.Promise} a new promise object | |
*/ | |
fail: function (rejected) { | |
return when(this, 0, rejected); | |
}, | |
/** | |
* call callback when this promise object is rejected or resolved | |
* @param {Function} callback the second parameter is | |
* true when resolved and false when rejected | |
* @@return {KISSY.Promise} a new promise object | |
*/ | |
fin: function (callback) { | |
return when(this, function (value) { | |
return callback(value, true); | |
}, function (reason) { | |
return callback(reason, false); | |
}); | |
}, | |
/** | |
* register callbacks when this promise object is resolved, | |
* and throw error at next event loop if promise | |
* (current instance if no fulfilled and rejected parameter or | |
* new instance caused by call this.then(fulfilled, rejected)) | |
* fails. | |
* @param {Function} [fulfilled] called when resolved successfully,pass a resolved value to this function and | |
* return a value (could be promise object) for the new promise 's resolved value. | |
* @param {Function} [rejected] called when error occurs,pass error reason to this function and | |
* return a new reason for the new promise 's error reason | |
*/ | |
done: function (fulfilled, rejected) { | |
var self = this, | |
onUnhandledError = function (error) { | |
setTimeout(function () { | |
throw error; | |
}, 0); | |
}, | |
promiseToHandle = fulfilled || rejected ? | |
self.then(fulfilled, rejected) : | |
self; | |
promiseToHandle.fail(onUnhandledError); | |
}, | |
/** | |
* whether the given object is a resolved promise | |
* if it is resolved with another promise, | |
* then that promise needs to be resolved as well. | |
* @member KISSY.Promise | |
*/ | |
isResolved: function () { | |
return isResolved(this); | |
}, | |
/** | |
* whether the given object is a rejected promise | |
*/ | |
isRejected: function () { | |
return isRejected(this); | |
} | |
}; | |
function Reject(reason) { | |
if (reason instanceof Reject) { | |
return reason; | |
} | |
var self = this; | |
Promise.apply(self, arguments); | |
if (self[PROMISE_VALUE] instanceof Promise) { | |
S.error('assert.not(this.__promise_value instanceof promise) in Reject constructor'); | |
} | |
return self; | |
} | |
S.extend(Reject, Promise); | |
/** | |
* wrap for promiseWhen | |
* @param value | |
* @ignore | |
* @param fulfilled | |
* @param [rejected] | |
*/ | |
function when(value, fulfilled, rejected) { | |
var defer = new Defer(), | |
done = 0; | |
// wrap user's callback to catch exception | |
function _fulfilled(value) { | |
try { | |
return fulfilled ? fulfilled(value) : | |
// propagate | |
value; | |
} catch (e) { | |
// print stack info for firefox/chrome | |
S.log(e.stack || e, 'error'); | |
return new Reject(e); | |
} | |
} | |
function _rejected(reason) { | |
try { | |
return rejected ? | |
// error recovery | |
rejected(reason) : | |
// propagate | |
new Reject(reason); | |
} catch (e) { | |
// print stack info for firefox/chrome | |
S.log(e.stack || e, 'error'); | |
return new Reject(e); | |
} | |
} | |
function finalFulfill(value) { | |
if (done) { | |
S.error('already done at fulfilled'); | |
return; | |
} | |
if (value instanceof Promise) { | |
S.error('assert.not(value instanceof Promise) in when') | |
} | |
done = 1; | |
defer.resolve(_fulfilled(value)); | |
} | |
if (value instanceof Promise) { | |
promiseWhen(value, finalFulfill, function (reason) { | |
if (done) { | |
S.error('already done at rejected'); | |
return; | |
} | |
done = 1; | |
// _reject may return non-Reject object for error recovery | |
defer.resolve(_rejected(reason)); | |
}); | |
} else { | |
finalFulfill(value); | |
} | |
// chained and leveled | |
// wait for value's resolve | |
return defer.promise; | |
} | |
function isResolved(obj) { | |
// exclude Reject at first | |
return !isRejected(obj) && | |
isPromise(obj) && | |
// self is resolved | |
(obj[PROMISE_PENDINGS] === undefined) && | |
// value is a resolved promise or value is immediate value | |
( | |
// immediate value | |
!isPromise(obj[PROMISE_VALUE]) || | |
// resolved with a resolved promise !!! :) | |
// Reject.__promise_value is string | |
isResolved(obj[PROMISE_VALUE]) | |
); | |
} | |
function isRejected(obj) { | |
return isPromise(obj) && | |
(obj[PROMISE_PENDINGS] === undefined) && | |
(obj[PROMISE_VALUE] instanceof Reject); | |
} | |
KISSY.Defer = Defer; | |
KISSY.Promise = Promise; | |
Promise.Defer = Defer; | |
S.mix(Promise, | |
/** | |
* @class KISSY.PromiseMix | |
* @override KISSY.Promise | |
*/ | |
{ | |
/** | |
* register callbacks when obj as a promise is resolved | |
* or call fulfilled callback directly when obj is not a promise object | |
* @param {KISSY.Promise|*} obj a promise object or value of any type | |
* @param {Function} fulfilled called when obj resolved successfully,pass a resolved value to this function and | |
* return a value (could be promise object) for the new promise 's resolved value. | |
* @param {Function} [rejected] called when error occurs in obj,pass error reason to this function and | |
* return a new reason for the new promise 's error reason | |
* @return {KISSY.Promise} a new promise object | |
* | |
* for example: | |
* @example | |
* function check(p) { | |
* S.Promise.when(p, function(v){ | |
* alert(v === 1); | |
* }); | |
* } | |
* | |
* var defer = S.Defer(); | |
* defer.resolve(1); | |
* | |
* check(1); // => alert(true) | |
* | |
* check(defer.promise); //=> alert(true); | |
* | |
* @static | |
* @method | |
*/ | |
when: when, | |
/** | |
* whether the given object is a promise | |
* @method | |
* @static | |
* @param obj the tested object | |
* @return {Boolean} | |
*/ | |
isPromise: isPromise, | |
/** | |
* whether the given object is a resolved promise | |
* @method | |
* @static | |
* @param obj the tested object | |
* @return {Boolean} | |
*/ | |
isResolved: isResolved, | |
/** | |
* whether the given object is a rejected promise | |
* @method | |
* @static | |
* @param obj the tested object | |
* @return {Boolean} | |
*/ | |
isRejected: isRejected, | |
/** | |
* return a new promise | |
* which is resolved when all promises is resolved | |
* and rejected when any one of promises is rejected | |
* @param {KISSY.Promise[]} promises list of promises | |
* @static | |
* @return {KISSY.Promise} | |
*/ | |
all: function (promises) { | |
var count = promises.length; | |
if (!count) { | |
return null; | |
} | |
var defer = Defer(); | |
for (var i = 0; i < promises.length; i++) { | |
(function (promise, i) { | |
when(promise, function (value) { | |
promises[i] = value; | |
if (--count === 0) { | |
// if all is resolved | |
// then resolve final returned promise with all value | |
defer.resolve(promises); | |
} | |
}, function (r) { | |
// if any one is rejected | |
// then reject final return promise with first reason | |
defer.reject(r); | |
}); | |
})(promises[i], i); | |
} | |
return defer.promise; | |
} | |
}); | |
})(KISSY); | |
/* | |
refer: | |
- http://wiki.commonjs.org/wiki/Promises | |
- http://en.wikipedia.org/wiki/Futures_and_promises#Read-only_views | |
- http://en.wikipedia.org/wiki/Object-capability_model | |
- https://github.com/kriskowal/q | |
- http://www.sitepen.com/blog/2010/05/03/robust-promises-with-dojo-deferred-1-5/ | |
- http://dojotoolkit.org/documentation/tutorials/1.6/deferreds/ | |
*//** | |
* @ignore | |
* Port Node Utils For KISSY. | |
* Note: Only posix mode. | |
* @author [email protected] | |
*/ | |
(function (S) { | |
// [root, dir, basename, ext] | |
var splitPathRe = /^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/; | |
/** | |
* Remove .. and . in path array | |
* @ignore | |
* @param parts | |
* @param allowAboveRoot | |
* @return {*} | |
*/ | |
function normalizeArray(parts, allowAboveRoot) { | |
// level above root | |
var up = 0, | |
i = parts.length - 1, | |
// splice costs a lot in ie | |
// use new array instead | |
newParts = [], | |
last; | |
for (; i >= 0; i--) { | |
last = parts[i]; | |
if (last == '.') { | |
} else if (last === '..') { | |
up++; | |
} else if (up) { | |
up--; | |
} else { | |
newParts[newParts.length] = last; | |
} | |
} | |
// if allow above root, has to add .. | |
if (allowAboveRoot) { | |
for (; up--; up) { | |
newParts[newParts.length] = '..'; | |
} | |
} | |
newParts = newParts.reverse(); | |
return newParts; | |
} | |
/** | |
* Path Utils For KISSY. | |
* @class KISSY.Path | |
* @singleton | |
*/ | |
var Path = { | |
/** | |
* resolve([from ...], to) | |
* | |
* @return {String} Resolved path. | |
*/ | |
resolve: function () { | |
var resolvedPath = '', | |
resolvedPathStr, | |
i, | |
args = (arguments), | |
path, | |
absolute = 0; | |
for (i = args.length - 1; i >= 0 && !absolute; i--) { | |
path = args[i]; | |
if (typeof path != 'string' || !path) { | |
continue; | |
} | |
resolvedPath = path + '/' + resolvedPath; | |
absolute = path.charAt(0) == '/'; | |
} | |
resolvedPathStr = normalizeArray(S.filter(resolvedPath.split('/'), function (p) { | |
return !!p; | |
}), !absolute).join('/'); | |
return ((absolute ? '/' : '') + resolvedPathStr) || '.'; | |
}, | |
/** | |
* normalize .. and . in path | |
* @param {String} path Path tobe normalized | |
* | |
* for example: | |
* @example | |
* 'x/y/../z' => 'x/z' | |
* 'x/y/z/../' => 'x/y/' | |
* | |
* @return {String} | |
*/ | |
normalize: function (path) { | |
var absolute = path.charAt(0) == '/', | |
trailingSlash = path.slice(-1) == '/'; | |
path = normalizeArray(S.filter(path.split('/'), function (p) { | |
return !!p; | |
}), !absolute).join('/'); | |
if (!path && !absolute) { | |
path = '.'; | |
} | |
if (path && trailingSlash) { | |
path += '/'; | |
} | |
return (absolute ? '/' : '') + path; | |
}, | |
/** | |
* join([path ...]) and normalize | |
* @return {String} | |
*/ | |
join: function () { | |
var args = S.makeArray(arguments); | |
return Path.normalize(S.filter(args,function (p) { | |
return p && (typeof p == 'string'); | |
}).join('/')); | |
}, | |
/** | |
* Get string which is to relative to from | |
* @param {String} from | |
* @param {String} to | |
* | |
* for example: | |
* @example | |
* relative('x/','x/y/z') => 'y/z' | |
* relative('x/t/z','x/') => '../../' | |
* | |
* @return {String} | |
*/ | |
relative: function (from, to) { | |
from = Path.normalize(from); | |
to = Path.normalize(to); | |
var fromParts = S.filter(from.split('/'), function (p) { | |
return !!p; | |
}), | |
path = [], | |
sameIndex, | |
sameIndex2, | |
toParts = S.filter(to.split('/'), function (p) { | |
return !!p; | |
}), commonLength = Math.min(fromParts.length, toParts.length); | |
for (sameIndex = 0; sameIndex < commonLength; sameIndex++) { | |
if (fromParts[sameIndex] != toParts[sameIndex]) { | |
break; | |
} | |
} | |
sameIndex2 = sameIndex; | |
while (sameIndex < fromParts.length) { | |
path.push('..'); | |
sameIndex++; | |
} | |
path = path.concat(toParts.slice(sameIndex2)); | |
path = path.join('/'); | |
return /**@type String @ignore*/path; | |
}, | |
/** | |
* Get base name of path | |
* @param {String} path | |
* @param {String} [ext] ext to be stripped from result returned. | |
* @return {String} | |
*/ | |
basename: function (path, ext) { | |
var result = path.match(splitPathRe) || [], | |
basename; | |
basename = result[3] || ''; | |
if (ext && basename && basename.slice(-1 * ext.length) == ext) { | |
basename = basename.slice(0, -1 * ext.length); | |
} | |
return basename; | |
}, | |
/** | |
* Get dirname of path | |
* @return {String} | |
*/ | |
dirname: function (path) { | |
var result = path.match(splitPathRe) || [], | |
root = result[1] || '', | |
dir = result[2] || ''; | |
if (!root && !dir) { | |
// No dirname | |
return '.'; | |
} | |
if (dir) { | |
// It has a dirname, strip trailing slash | |
dir = dir.substring(0, dir.length - 1); | |
} | |
return root + dir; | |
}, | |
/** | |
* Get extension name of file in path | |
* @param {String} path | |
* @return {String} | |
*/ | |
extname: function (path) { | |
return (path.match(splitPathRe) || [])[4] || ''; | |
} | |
}; | |
if (Path) { | |
S.Path = Path; | |
} | |
})(KISSY); | |
/* | |
Refer | |
- https://github.com/joyent/node/blob/master/lib/path.js | |
*//** | |
* @ignore | |
* Uri class for KISSY. | |
* @author [email protected] | |
*/ | |
(function (S, undefined) { | |
var reDisallowedInSchemeOrUserInfo = /[#\/\?@]/g, | |
reDisallowedInPathName = /[#\?]/g, | |
// ?? combo of taobao | |
reDisallowedInQuery = /[#@]/g, | |
reDisallowedInFragment = /#/g, | |
URI_SPLIT_REG = new RegExp( | |
'^' + | |
/* | |
Scheme names consist of a sequence of characters beginning with a | |
letter and followed by any combination of letters, digits, plus | |
('+'), period ('.'), or hyphen ('-'). | |
*/ | |
'(?:([\\w\\d+.-]+):)?' + // scheme | |
'(?://' + | |
/* | |
The authority component is preceded by a double slash ('//') and is | |
terminated by the next slash ('/'), question mark ('?'), or number | |
sign ('#') character, or by the end of the URI. | |
*/ | |
'(?:([^/?#@]*)@)?' + // userInfo | |
'(' + | |
'[\\w\\d\\-\\u0100-\\uffff.+%]*' + | |
'|' + | |
// ipv6 | |
'\\[[^\\]]+\\]' + | |
')' + // hostname - restrict to letters, | |
// digits, dashes, dots, percent | |
// escapes, and unicode characters. | |
'(?::([0-9]+))?' + // port | |
')?' + | |
/* | |
The path is terminated | |
by the first question mark ('?') or number sign ('#') character, or | |
by the end of the URI. | |
*/ | |
'([^?#]+)?' + // path. hierarchical part | |
/* | |
The query component is indicated by the first question | |
mark ('?') character and terminated by a number sign ('#') character | |
or by the end of the URI. | |
*/ | |
'(?:\\?([^#]*))?' + // query. non-hierarchical data | |
/* | |
The fragment identifier component of a URI allows indirect | |
identification of a secondary resource by reference to a primary | |
resource and additional identifying information. | |
A | |
fragment identifier component is indicated by the presence of a | |
number sign ('#') character and terminated by the end of the URI. | |
*/ | |
'(?:#(.*))?' + // fragment | |
'$'), | |
Path = S.Path, | |
REG_INFO = { | |
scheme: 1, | |
userInfo: 2, | |
hostname: 3, | |
port: 4, | |
path: 5, | |
query: 6, | |
fragment: 7 | |
}; | |
function parseQuery(self) { | |
if (!self._queryMap) { | |
self._queryMap = S.unparam(self._query); | |
} | |
} | |
/** | |
* @class KISSY.Uri.Query | |
* Query data structure. | |
* @param {String} [query] encoded query string(without question mask). | |
*/ | |
function Query(query) { | |
this._query = query || ''; | |
} | |
Query.prototype = { | |
constructor: Query, | |
/** | |
* Cloned new instance. | |
* @return {KISSY.Uri.Query} | |
*/ | |
clone: function () { | |
return new Query(this.toString()); | |
}, | |
/** | |
* reset to a new query string | |
* @param {String} query | |
* @chainable | |
*/ | |
reset: function (query) { | |
var self = this; | |
self._query = query || ''; | |
self._queryMap = null; | |
return self; | |
}, | |
/** | |
* Parameter count. | |
* @return {Number} | |
*/ | |
count: function () { | |
var self = this, | |
count = 0, | |
_queryMap, | |
k; | |
parseQuery(self); | |
_queryMap = self._queryMap; | |
for (k in _queryMap) { | |
if (S.isArray(_queryMap[k])) { | |
count += _queryMap[k].length; | |
} else { | |
count++; | |
} | |
} | |
return count; | |
}, | |
/** | |
* judge whether has query parameter | |
* @param {String} [key] | |
*/ | |
has: function (key) { | |
var self = this, _queryMap; | |
parseQuery(self); | |
_queryMap = self._queryMap; | |
if (key) { | |
return key in _queryMap; | |
} else { | |
return !S.isEmptyObject(_queryMap); | |
} | |
}, | |
/** | |
* Return parameter value corresponding to current key | |
* @param {String} [key] | |
*/ | |
get: function (key) { | |
var self = this, _queryMap; | |
parseQuery(self); | |
_queryMap = self._queryMap; | |
if (key) { | |
return _queryMap[key]; | |
} else { | |
return _queryMap; | |
} | |
}, | |
/** | |
* Parameter names. | |
* @return {String[]} | |
*/ | |
keys: function () { | |
var self = this; | |
parseQuery(self); | |
return S.keys(self._queryMap); | |
}, | |
/** | |
* Set parameter value corresponding to current key | |
* @param {String} key | |
* @param value | |
* @chainable | |
*/ | |
set: function (key, value) { | |
var self = this, _queryMap; | |
parseQuery(self); | |
_queryMap = self._queryMap; | |
if (typeof key == 'string') { | |
self._queryMap[key] = value; | |
} else { | |
if (key instanceof Query) { | |
key = key.get(); | |
} | |
S.each(key, function (v, k) { | |
_queryMap[k] = v; | |
}); | |
} | |
return self; | |
}, | |
/** | |
* Remove parameter with specified name. | |
* @param {String} key | |
* @chainable | |
*/ | |
remove: function (key) { | |
var self = this; | |
parseQuery(self); | |
if (key) { | |
delete self._queryMap[key]; | |
} else { | |
self._queryMap = {}; | |
} | |
return self; | |
}, | |
/** | |
* Add parameter value corresponding to current key | |
* @param {String} key | |
* @param value | |
* @chainable | |
*/ | |
add: function (key, value) { | |
var self = this, | |
_queryMap, | |
currentValue; | |
if (S.isObject(key)) { | |
if (key instanceof Query) { | |
key = key.get(); | |
} | |
S.each(key, function (v, k) { | |
self.add(k, v); | |
}); | |
} else { | |
parseQuery(self); | |
_queryMap = self._queryMap; | |
currentValue = _queryMap[key]; | |
if (currentValue === undefined) { | |
currentValue = value; | |
} else { | |
currentValue = [].concat(currentValue).concat(value); | |
} | |
_queryMap[key] = currentValue; | |
} | |
return self; | |
}, | |
/** | |
* Serialize query to string. | |
* @param {Boolean} [serializeArray=true] | |
* whether append [] to key name when value 's type is array | |
*/ | |
toString: function (serializeArray) { | |
var self = this; | |
parseQuery(self); | |
return S.param(self._queryMap, undefined, undefined, serializeArray); | |
} | |
}; | |
function padding2(str) { | |
return str.length == 1 ? '0' + str : str; | |
} | |
function equalsIgnoreCase(str1, str2) { | |
return str1.toLowerCase() == str2.toLowerCase(); | |
} | |
// www.ta#bao.com // => www.ta.com/#bao.com | |
// www.ta%23bao.com | |
// Percent-Encoding | |
function encodeSpecialChars(str, specialCharsReg) { | |
// encodeURI( ) is intended to encode complete URIs, | |
// the following ASCII punctuation characters, | |
// which have special meaning in URIs, are not escaped either: | |
// ; / ? : @ & = + $ , # | |
return encodeURI(str).replace(specialCharsReg, function (m) { | |
return '%' + padding2(m.charCodeAt(0).toString(16)); | |
}); | |
} | |
/** | |
* @class KISSY.Uri | |
* Uri class for KISSY. | |
* Most of its interfaces are same with window.location. | |
* @param {String|KISSY.Uri} [uriStr] Encoded uri string. | |
*/ | |
function Uri(uriStr) { | |
if (uriStr instanceof Uri) { | |
return uriStr['clone'](); | |
} | |
var components, self = this; | |
S.mix(self, | |
{ | |
/** | |
* scheme such as 'http:'. aka protocol without colon | |
* @type {String} | |
*/ | |
scheme: '', | |
/** | |
* User credentials such as 'yiminghe:gmail' | |
* @type {String} | |
*/ | |
userInfo: '', | |
/** | |
* hostname such as 'docs.kissyui.com'. aka domain | |
* @type {String} | |
*/ | |
hostname: '', | |
/** | |
* Port such as '8080' | |
* @type {String} | |
*/ | |
port: '', | |
/** | |
* path such as '/index.htm'. aka pathname | |
* @type {String} | |
*/ | |
path: '', | |
/** | |
* Query object for search string. aka search | |
* @type {KISSY.Uri.Query} | |
*/ | |
query: '', | |
/** | |
* fragment such as '#!/test/2'. aka hash | |
*/ | |
fragment: '' | |
}); | |
components = Uri.getComponents(uriStr); | |
S.each(components, function (v, key) { | |
v = v || ''; | |
if (key == 'query') { | |
// need encoded content | |
self.query = new Query(v); | |
} else { | |
// https://github.com/kissyteam/kissy/issues/298 | |
try { | |
v = S.urlDecode(v); | |
} catch (e) { | |
S.log(e + 'urlDecode error : ' + v, 'error'); | |
} | |
// need to decode to get data structure in memory | |
self[key] = v; | |
} | |
}); | |
return self; | |
} | |
Uri.prototype = { | |
constructor: Uri, | |
/** | |
* Return a cloned new instance. | |
* @return {KISSY.Uri} | |
*/ | |
clone: function () { | |
var uri = new Uri(), self = this; | |
S.each(REG_INFO, function (index, key) { | |
uri[key] = self[key]; | |
}); | |
uri.query = uri.query.clone(); | |
return uri; | |
}, | |
/** | |
* The reference resolution algorithm.rfc 5.2 | |
* return a resolved uri corresponding to current uri | |
* @param {KISSY.Uri|String} relativeUri | |
* | |
* for example: | |
* @example | |
* this: 'http://y/yy/z.com?t=1#v=2' | |
* 'https:/y/' => 'https:/y/' | |
* '//foo' => 'http://foo' | |
* 'foo' => 'http://y/yy/foo' | |
* '/foo' => 'http://y/foo' | |
* '?foo' => 'http://y/yy/z.com?foo' | |
* '#foo' => http://y/yy/z.com?t=1#foo' | |
* | |
* @return {KISSY.Uri} | |
*/ | |
resolve: function (relativeUri) { | |
if (typeof relativeUri == 'string') { | |
relativeUri = new Uri(relativeUri); | |
} | |
var self = this, | |
override = 0, | |
lastSlashIndex, | |
order = ['scheme', 'userInfo', 'hostname', 'port', 'path', 'query', 'fragment'], | |
target = self.clone(); | |
S.each(order, function (o) { | |
if (o == 'path') { | |
// relativeUri does not set for scheme/userInfo/hostname/port | |
if (override) { | |
target[o] = relativeUri[o]; | |
} else { | |
var path = relativeUri['path']; | |
if (path) { | |
// force to override target 's query with relative | |
override = 1; | |
if (!S.startsWith(path, '/')) { | |
if (target.hostname && !target.path) { | |
// RFC 3986, section 5.2.3, case 1 | |
path = '/' + path; | |
} else if (target.path) { | |
// RFC 3986, section 5.2.3, case 2 | |
lastSlashIndex = target.path.lastIndexOf('/'); | |
if (lastSlashIndex != -1) { | |
path = target.path.slice(0, lastSlashIndex + 1) + path; | |
} | |
} | |
} | |
// remove .. / . as part of the resolution process | |
target.path = Path.normalize(path); | |
} | |
} | |
} else if (o == 'query') { | |
if (override || relativeUri['query'].toString()) { | |
target.query = relativeUri['query'].clone(); | |
override = 1; | |
} | |
} else if (override || relativeUri[o]) { | |
target[o] = relativeUri[o]; | |
override = 1; | |
} | |
}); | |
return target; | |
}, | |
/** | |
* Get scheme part | |
*/ | |
getScheme: function () { | |
return this.scheme; | |
}, | |
/** | |
* Set scheme part | |
* @param {String} scheme | |
* @chainable | |
*/ | |
setScheme: function (scheme) { | |
this.scheme = scheme; | |
return this; | |
}, | |
/** | |
* Return hostname | |
* @return {String} | |
*/ | |
getHostname: function () { | |
return this.hostname; | |
}, | |
/** | |
* Set hostname | |
* @param {String} hostname | |
* @chainable | |
*/ | |
setHostname: function (hostname) { | |
this.hostname = hostname; | |
return this; | |
}, | |
/** | |
* Set user info | |
* @param {String} userInfo | |
* @chainable | |
*/ | |
'setUserInfo': function (userInfo) { | |
this.userInfo = userInfo; | |
return this; | |
}, | |
/** | |
* Get user info | |
* @return {String} | |
*/ | |
getUserInfo: function () { | |
return this.userInfo; | |
}, | |
/** | |
* Set port | |
* @param {String} port | |
* @chainable | |
*/ | |
'setPort': function (port) { | |
this.port = port; | |
return this; | |
}, | |
/** | |
* Get port | |
* @return {String} | |
*/ | |
'getPort': function () { | |
return this.port; | |
}, | |
/** | |
* Set path | |
* @param {string} path | |
* @chainable | |
*/ | |
setPath: function (path) { | |
this.path = path; | |
return this; | |
}, | |
/** | |
* Get path | |
* @return {String} | |
*/ | |
getPath: function () { | |
return this.path; | |
}, | |
/** | |
* Set query | |
* @param {String|KISSY.Uri.Query} query | |
* @chainable | |
*/ | |
'setQuery': function (query) { | |
if (typeof query == 'string') { | |
if (S.startsWith(query, '?')) { | |
query = query.slice(1); | |
} | |
query = new Query(encodeSpecialChars(query, reDisallowedInQuery)); | |
} | |
this.query = query; | |
return this; | |
}, | |
/** | |
* Get query | |
* @return {KISSY.Uri.Query} | |
*/ | |
getQuery: function () { | |
return this.query; | |
}, | |
/** | |
* Get fragment | |
* @return {String} | |
*/ | |
getFragment: function () { | |
return this.fragment; | |
}, | |
/** | |
* Set fragment | |
* @param {String} fragment | |
* @chainable | |
*/ | |
'setFragment': function (fragment) { | |
var self = this; | |
if (S.startsWith(fragment, '#')) { | |
fragment = fragment.slice(1); | |
} | |
self.fragment = fragment; | |
return self; | |
}, | |
/** | |
* Judge whether two uri has same domain. | |
* @param {KISSY.Uri} other | |
* @return {Boolean} | |
*/ | |
isSameOriginAs: function (other) { | |
var self = this; | |
// port and hostname has to be same | |
return equalsIgnoreCase(self.hostname, other['hostname']) && | |
equalsIgnoreCase(self.scheme, other['scheme']) && | |
equalsIgnoreCase(self.port, other['port']); | |
}, | |
/** | |
* Serialize to string. | |
* See rfc 5.3 Component Recomposition. | |
* But kissy does not differentiate between undefined and empty. | |
* @param {Boolean} [serializeArray=true] | |
* whether append [] to key name when value 's type is array | |
* @return {String} | |
*/ | |
toString: function (serializeArray) { | |
var out = [], | |
self = this, | |
scheme, | |
hostname, | |
path, | |
port, | |
fragment, | |
query, | |
userInfo; | |
if (scheme = self.scheme) { | |
out.push(encodeSpecialChars(scheme, reDisallowedInSchemeOrUserInfo)); | |
out.push(':'); | |
} | |
if (hostname = self.hostname) { | |
out.push('//'); | |
if (userInfo = self.userInfo) { | |
out.push(encodeSpecialChars(userInfo, reDisallowedInSchemeOrUserInfo)); | |
out.push('@'); | |
} | |
out.push(encodeURIComponent(hostname)); | |
if (port = self.port) { | |
out.push(':'); | |
out.push(port); | |
} | |
} | |
if (path = self.path) { | |
if (hostname && !S.startsWith(path, '/')) { | |
path = '/' + path; | |
} | |
path = Path.normalize(path); | |
out.push(encodeSpecialChars(path, reDisallowedInPathName)); | |
} | |
if (query = ( self.query.toString.call(self.query, serializeArray))) { | |
out.push('?'); | |
out.push(query); | |
} | |
if (fragment = self.fragment) { | |
out.push('#'); | |
out.push(encodeSpecialChars(fragment, reDisallowedInFragment)) | |
} | |
return out.join(''); | |
} | |
}; | |
Uri.Query = Query; | |
Uri.getComponents = function (url) { | |
url = url || ""; | |
var m = url.match(URI_SPLIT_REG) || [], | |
ret = {}; | |
S.each(REG_INFO, function (index, key) { | |
ret[key] = m[index]; | |
}); | |
return ret; | |
}; | |
S.Uri = Uri; | |
})(KISSY); | |
/* | |
Refer | |
- application/x-www-form-urlencoded | |
- http://www.ietf.org/rfc/rfc3986.txt | |
- http://en.wikipedia.org/wiki/URI_scheme | |
- http://unixpapa.com/js/querystring.html | |
- http://code.stephenmorley.org/javascript/parsing-query-strings-for-get-data/ | |
- same origin: http://tools.ietf.org/html/rfc6454 | |
*//** | |
* @ignore | |
* ua | |
*/ | |
(function (S, undefined) { | |
var win = S.Env.host, | |
doc = win.document, | |
navigator = win.navigator, | |
ua = navigator && navigator.userAgent || ""; | |
function numberify(s) { | |
var c = 0; | |
// convert '1.2.3.4' to 1.234 | |
return parseFloat(s.replace(/\./g, function () { | |
return (c++ === 0) ? '.' : ''; | |
})); | |
} | |
function setTridentVersion(ua, UA) { | |
var core, m; | |
UA[core = 'trident'] = 0.1; // Trident detected, look for revision | |
// Get the Trident's accurate version | |
if ((m = ua.match(/Trident\/([\d.]*)/)) && m[1]) { | |
UA[core] = numberify(m[1]); | |
} | |
UA.core = core; | |
} | |
function getIEVersion(ua) { | |
var m, v; | |
if ((m = ua.match(/MSIE ([^;]*)|Trident.*; rv(?:\s|:)?([0-9.]+)/)) && | |
(v = (m[1] || m[2]))) { | |
return numberify(v); | |
} | |
return 0; | |
} | |
function getDescriptorFromUserAgent(ua) { | |
var EMPTY = '', | |
os, | |
core = EMPTY, | |
shell = EMPTY, m, | |
IE_DETECT_RANGE = [6, 9], | |
ieVersion, | |
v, | |
end, | |
VERSION_PLACEHOLDER = '{{version}}', | |
IE_DETECT_TPL = '<!--[if IE ' + VERSION_PLACEHOLDER + ']><' + 's></s><![endif]-->', | |
div = doc && doc.createElement('div'), | |
s = []; | |
/** | |
* KISSY UA | |
* @member KISSY | |
* @class KISSY.UA | |
* @singleton | |
*/ | |
var UA = { | |
/** | |
* webkit version | |
* @type undefined|Number | |
* @member KISSY.UA | |
*/ | |
webkit: undefined, | |
/** | |
* trident version | |
* @type undefined|Number | |
* @member KISSY.UA | |
*/ | |
trident: undefined, | |
/** | |
* gecko version | |
* @type undefined|Number | |
* @member KISSY.UA | |
*/ | |
gecko: undefined, | |
/** | |
* presto version | |
* @type undefined|Number | |
* @member KISSY.UA | |
*/ | |
presto: undefined, | |
/** | |
* chrome version | |
* @type undefined|Number | |
* @member KISSY.UA | |
*/ | |
chrome: undefined, | |
/** | |
* safari version | |
* @type undefined|Number | |
* @member KISSY.UA | |
*/ | |
safari: undefined, | |
/** | |
* firefox version | |
* @type undefined|Number | |
* @member KISSY.UA | |
*/ | |
firefox: undefined, | |
/** | |
* ie version | |
* @type undefined|Number | |
* @member KISSY.UA | |
*/ | |
ie: undefined, | |
/** | |
* opera version | |
* @type undefined|Number | |
* @member KISSY.UA | |
*/ | |
opera: undefined, | |
/** | |
* mobile browser. apple, android. | |
* @type String | |
* @member KISSY.UA | |
*/ | |
mobile: undefined, | |
/** | |
* browser render engine name. webkit, trident | |
* @type String | |
* @member KISSY.UA | |
*/ | |
core: undefined, | |
/** | |
* browser shell name. ie, chrome, firefox | |
* @type String | |
* @member KISSY.UA | |
*/ | |
shell: undefined, | |
/** | |
* PhantomJS version number | |
* @type undefined|Number | |
* @member KISSY.UA | |
*/ | |
phantomjs: undefined, | |
/** | |
* operating system. android, ios, linux, windows | |
* @type string | |
* @member KISSY.UA | |
*/ | |
os: undefined, | |
/** | |
* ipad ios version | |
* @type Number | |
* @member KISSY.UA | |
*/ | |
ipad: undefined, | |
/** | |
* iphone ios version | |
* @type Number | |
* @member KISSY.UA | |
*/ | |
iphone: undefined, | |
/** | |
* ipod ios | |
* @type Number | |
* @member KISSY.UA | |
*/ | |
ipod: undefined, | |
/** | |
* ios version | |
* @type Number | |
* @member KISSY.UA | |
*/ | |
ios: undefined, | |
/** | |
* android version | |
* @type Number | |
* @member KISSY.UA | |
*/ | |
android: undefined, | |
/** | |
* nodejs version | |
* @type Number | |
* @member KISSY.UA | |
*/ | |
nodejs: undefined | |
}; | |
if (div) { | |
// try to use IE-Conditional-Comment detect IE more accurately | |
// IE10 doesn't support this method, @ref: http://blogs.msdn.com/b/ie/archive/2011/07/06/html5-parsing-in-ie10.aspx | |
div.innerHTML = IE_DETECT_TPL.replace(VERSION_PLACEHOLDER, ''); | |
s = div.getElementsByTagName('s'); | |
} | |
if (s.length > 0) { | |
setTridentVersion(ua, UA); | |
// Detect the accurate version | |
// 注意: | |
// UA.shell = ie, 表示外壳是 ie | |
// 但 UA.ie = 7, 并不代表外壳是 ie7, 还有可能是 ie8 的兼容模式 | |
// 对于 ie8 的兼容模式,还要通过 documentMode 去判断。但此处不能让 UA.ie = 8, 否则 | |
// 很多脚本判断会失误。因为 ie8 的兼容模式表现行为和 ie7 相同,而不是和 ie8 相同 | |
for (v = IE_DETECT_RANGE[0], end = IE_DETECT_RANGE[1]; v <= end; v++) { | |
div.innerHTML = IE_DETECT_TPL.replace(VERSION_PLACEHOLDER, v); | |
if (s.length > 0) { | |
UA[shell = 'ie'] = v; | |
break; | |
} | |
} | |
// https://github.com/kissyteam/kissy/issues/321 | |
// win8 embed app | |
if (!UA.ie && (ieVersion = getIEVersion(ua))) { | |
UA[shell = 'ie'] = ieVersion; | |
} | |
} else { | |
// WebKit | |
if ((m = ua.match(/AppleWebKit\/([\d.]*)/)) && m[1]) { | |
UA[core = 'webkit'] = numberify(m[1]); | |
if ((m = ua.match(/OPR\/(\d+\.\d+)/)) && m[1]) { | |
UA[shell = 'opera'] = numberify(m[1]); | |
} | |
// Chrome | |
else if ((m = ua.match(/Chrome\/([\d.]*)/)) && m[1]) { | |
UA[shell = 'chrome'] = numberify(m[1]); | |
} | |
// Safari | |
else if ((m = ua.match(/\/([\d.]*) Safari/)) && m[1]) { | |
UA[shell = 'safari'] = numberify(m[1]); | |
} | |
// Apple Mobile | |
if (/ Mobile\//.test(ua) && ua.match(/iPad|iPod|iPhone/)) { | |
UA.mobile = 'apple'; // iPad, iPhone or iPod Touch | |
m = ua.match(/OS ([^\s]*)/); | |
if (m && m[1]) { | |
UA.ios = numberify(m[1].replace('_', '.')); | |
} | |
os = 'ios'; | |
m = ua.match(/iPad|iPod|iPhone/); | |
if (m && m[0]) { | |
UA[m[0].toLowerCase()] = UA.ios; | |
} | |
} else if (/ Android/.test(ua)) { | |
if (/Mobile/.test(ua)) { | |
os = UA.mobile = 'android'; | |
} | |
m = ua.match(/Android ([^\s]*);/); | |
if (m && m[1]) { | |
UA.android = numberify(m[1]); | |
} | |
} | |
// Other WebKit Mobile Browsers | |
else if ((m = ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/))) { | |
UA.mobile = m[0].toLowerCase(); // Nokia N-series, Android, webOS, ex: NokiaN95 | |
} | |
if ((m = ua.match(/PhantomJS\/([^\s]*)/)) && m[1]) { | |
UA.phantomjs = numberify(m[1]); | |
} | |
} | |
// NOT WebKit | |
else { | |
// Presto | |
// ref: http://www.useragentstring.com/pages/useragentstring.php | |
if ((m = ua.match(/Presto\/([\d.]*)/)) && m[1]) { | |
UA[core = 'presto'] = numberify(m[1]); | |
// Opera | |
if ((m = ua.match(/Opera\/([\d.]*)/)) && m[1]) { | |
UA[shell = 'opera'] = numberify(m[1]); // Opera detected, look for revision | |
if ((m = ua.match(/Opera\/.* Version\/([\d.]*)/)) && m[1]) { | |
UA[shell] = numberify(m[1]); | |
} | |
// Opera Mini | |
if ((m = ua.match(/Opera Mini[^;]*/)) && m) { | |
UA.mobile = m[0].toLowerCase(); // ex: Opera Mini/2.0.4509/1316 | |
} | |
// Opera Mobile | |
// ex: Opera/9.80 (Windows NT 6.1; Opera Mobi/49; U; en) Presto/2.4.18 Version/10.00 | |
// issue: 由于 Opera Mobile 有 Version/ 字段,可能会与 Opera 混淆,同时对于 Opera Mobile 的版本号也比较混乱 | |
else if ((m = ua.match(/Opera Mobi[^;]*/)) && m) { | |
UA.mobile = m[0]; | |
} | |
} | |
// NOT WebKit or Presto | |
} else { | |
// MSIE | |
// 由于最开始已经使用了 IE 条件注释判断,因此落到这里的唯一可能性只有 IE10+ | |
// and analysis tools in nodejs | |
if (ieVersion = getIEVersion(ua)) { | |
UA[shell = 'ie'] = ieVersion; | |
setTridentVersion(ua, UA); | |
// NOT WebKit, Presto or IE | |
} else { | |
// Gecko | |
if ((m = ua.match(/Gecko/))) { | |
UA[core = 'gecko'] = 0.1; // Gecko detected, look for revision | |
if ((m = ua.match(/rv:([\d.]*)/)) && m[1]) { | |
UA[core] = numberify(m[1]); | |
if (/Mobile|Tablet/.test(ua)) { | |
o.mobile = "firefox"; | |
} | |
} | |
// Firefox | |
if ((m = ua.match(/Firefox\/([\d.]*)/)) && m[1]) { | |
UA[shell = 'firefox'] = numberify(m[1]); | |
} | |
} | |
} | |
} | |
} | |
} | |
if (!os) { | |
if ((/windows|win32/i).test(ua)) { | |
os = 'windows'; | |
} else if ((/macintosh|mac_powerpc/i).test(ua)) { | |
os = 'macintosh'; | |
} else if ((/linux/i).test(ua)) { | |
os = 'linux'; | |
} else if ((/rhino/i).test(ua)) { | |
os = 'rhino'; | |
} | |
} | |
UA.os = os; | |
UA.core = UA.core || core; | |
UA.shell = shell; | |
return UA; | |
} | |
var UA = KISSY.UA = getDescriptorFromUserAgent(ua); | |
// nodejs | |
if (typeof process === 'object') { | |
var versions, nodeVersion; | |
if ((versions = process.versions) && (nodeVersion = versions.node)) { | |
UA.os = process.platform; | |
UA.nodejs = numberify(nodeVersion); | |
} | |
} | |
// use by analysis tools in nodejs | |
UA.getDescriptorFromUserAgent = getDescriptorFromUserAgent; | |
var o = [ | |
// browser core type | |
'webkit', | |
'trident', | |
'gecko', | |
'presto', | |
// browser type | |
'chrome', | |
'safari', | |
'firefox', | |
'ie', | |
'opera' | |
], | |
documentElement = doc && doc.documentElement, | |
className = ''; | |
if (documentElement) { | |
S.each(o, function (key) { | |
var v = UA[key]; | |
if (v) { | |
className += ' ks-' + key + (parseInt(v) + ''); | |
className += ' ks-' + key; | |
} | |
}); | |
if (S.trim(className)) { | |
documentElement.className = S.trim(documentElement.className + className); | |
} | |
} | |
})(KISSY); | |
/* | |
NOTES: | |
2013.07.08 [email protected] | |
- support ie11 and opera(using blink) | |
2013.01.17 [email protected] | |
- expose getDescriptorFromUserAgent for analysis tool in nodejs | |
2012.11.27 [email protected] | |
- moved to seed for conditional loading and better code share | |
2012.11.21 [email protected] | |
- touch and os support | |
2011.11.08 [email protected] | |
- ie < 10 使用条件注释判断内核,更精确 | |
2010.03 | |
- jQuery, YUI 等类库都推荐用特性探测替代浏览器嗅探。特性探测的好处是能自动适应未来设备和未知设备,比如 | |
if(document.addEventListener) 假设 IE9 支持标准事件,则代码不用修改,就自适应了“未来浏览器”。 | |
对于未知浏览器也是如此。但是,这并不意味着浏览器嗅探就得彻底抛弃。当代码很明确就是针对已知特定浏览器的, | |
同时并非是某个特性探测可以解决时,用浏览器嗅探反而能带来代码的简洁,同时也也不会有什么后患。总之,一切 | |
皆权衡。 | |
- UA.ie && UA.ie < 8 并不意味着浏览器就不是 IE8, 有可能是 IE8 的兼容模式。进一步的判断需要使用 documentMode. | |
*/ | |
/** | |
* @ignore | |
* detect if current browser supports various features. | |
* @author [email protected] | |
*/ | |
(function (S, undefined) { | |
var Env = S.Env, | |
win = Env.host, | |
UA = S.UA, | |
VENDORS = [ | |
'', | |
'Webkit', | |
'Moz', | |
'O', | |
// ms is special .... ! | |
'ms' | |
], | |
// nodejs | |
doc = win.document || {}, | |
documentMode = doc.documentMode, | |
isMsPointerSupported, | |
transitionProperty, | |
transformProperty, | |
transitionPrefix, | |
transformPrefix, | |
documentElement = doc.documentElement, | |
documentElementStyle, | |
isClassListSupportedState = true, | |
isQuerySelectorSupportedState = false, | |
// phantomjs issue: http://code.google.com/p/phantomjs/issues/detail?id=375 | |
isTouchEventSupportedState = ('ontouchstart' in doc) && !(UA.phantomjs), | |
ie = documentMode || UA.ie; | |
if (documentElement) { | |
if (documentElement.querySelector && | |
// broken ie8 | |
ie != 8) { | |
isQuerySelectorSupportedState = true; | |
} | |
documentElementStyle = documentElement.style; | |
S.each(VENDORS, function (val) { | |
var transition = val ? val + 'Transition' : 'transition', | |
transform = val ? val + 'Transform' : 'transform'; | |
if (transitionPrefix === undefined && | |
transition in documentElementStyle) { | |
transitionPrefix = val; | |
transitionProperty = transition; | |
} | |
if (transformPrefix === undefined && | |
transform in documentElementStyle) { | |
transformPrefix = val; | |
transformProperty = transform; | |
} | |
}); | |
isClassListSupportedState = 'classList' in documentElement; | |
isMsPointerSupported = "msPointerEnabled" in (win.navigator || {}); | |
} | |
/** | |
* test browser features | |
* @class KISSY.Features | |
* @private | |
* @singleton | |
*/ | |
S.Features = { | |
// http://blogs.msdn.com/b/ie/archive/2011/09/20/touch-input-for-ie10-and-metro-style-apps.aspx | |
/** | |
* @ignore | |
* whether support win8 pointer event. | |
* @type {Boolean} | |
*/ | |
isMsPointerSupported: function () { | |
return isMsPointerSupported; | |
}, | |
/** | |
* whether support touch event. | |
* @method | |
* @return {Boolean} | |
*/ | |
isTouchEventSupported: function () { | |
return isTouchEventSupportedState; | |
}, | |
isDeviceMotionSupported: function () { | |
return !!win['DeviceMotionEvent']; | |
}, | |
'isHashChangeSupported': function () { | |
// ie8 支持 hashchange | |
// 但 ie8 以上切换浏览器模式到 ie7(兼容模式), | |
// 会导致 'onhashchange' in window === true,但是不触发事件 | |
return ( 'onhashchange' in win) && (!ie || ie > 7); | |
}, | |
'isTransitionSupported': function () { | |
return transitionPrefix !== undefined; | |
}, | |
'isTransformSupported': function () { | |
return transformPrefix !== undefined; | |
}, | |
'isClassListSupported': function () { | |
return isClassListSupportedState | |
}, | |
'isQuerySelectorSupported': function () { | |
// force to use js selector engine | |
return !S.config('dom/selector') && | |
isQuerySelectorSupportedState; | |
}, | |
'isIELessThan': function (v) { | |
return ie && ie < v; | |
}, | |
'getTransitionPrefix': function () { | |
return transitionPrefix; | |
}, | |
'getTransformPrefix': function () { | |
return transformPrefix; | |
}, | |
'getTransitionProperty': function () { | |
return transitionProperty; | |
}, | |
'getTransformProperty': function () { | |
return transformProperty; | |
} | |
}; | |
})(KISSY);/** | |
* @ignore | |
* setup data structure for kissy loader | |
* @author [email protected] | |
*/ | |
(function (S) { | |
var Loader = S.Loader = {}; | |
/** | |
* Loader Status Enum | |
* @enum {Number} KISSY.Loader.Status | |
*/ | |
Loader.Status = { | |
/** init */ | |
'INIT': 0, | |
/** loading */ | |
'LOADING': 1, | |
/** loaded */ | |
'LOADED': 2, | |
/** error */ | |
'ERROR': 3, | |
/** attached */ | |
'ATTACHED': 4 | |
}; | |
})(KISSY);/** | |
* @ignore | |
* Utils for kissy loader | |
* @author [email protected] | |
*/ | |
(function (S) { | |
var Loader = S.Loader, | |
Path = S.Path, | |
host = S.Env.host, | |
startsWith = S.startsWith, | |
data = Loader.Status, | |
ATTACHED = data.ATTACHED, | |
LOADED = data.LOADED, | |
ERROR = data.ERROR, | |
// LOADING = data.LOADING, | |
/** | |
* @class KISSY.Loader.Utils | |
* Utils for KISSY Loader | |
* @singleton | |
* @private | |
*/ | |
Utils = Loader.Utils = {}, | |
doc = host.document; | |
// http://wiki.commonjs.org/wiki/Packages/Mappings/A | |
// 如果模块名以 / 结尾,自动加 index | |
function indexMap(s) { | |
if (typeof s == 'string') { | |
return indexMapStr(s); | |
} else { | |
var ret = [], | |
i = 0, | |
l = s.length; | |
for (; i < l; i++) { | |
ret[i] = indexMapStr(s[i]); | |
} | |
return ret; | |
} | |
} | |
function indexMapStr(s) { | |
// 'x/' 'x/y/z/' | |
if (s.charAt(s.length - 1) == '/') { | |
s += 'index'; | |
} | |
return s; | |
} | |
function pluginAlias(runtime,name) { | |
var index = name.indexOf('!'); | |
if (index != -1) { | |
var pluginName = name.substring(0,index); | |
name = name.substring(index + 1); | |
S.use(pluginName, { | |
sync: true, | |
success: function (S, Plugin) { | |
if (Plugin.alias) { | |
//noinspection JSReferencingMutableVariableFromClosure | |
name = Plugin.alias(runtime,name, pluginName); | |
} | |
} | |
}); | |
} | |
return name; | |
} | |
S.mix(Utils, { | |
/** | |
* get document head | |
* @return {HTMLElement} | |
*/ | |
docHead: function () { | |
return doc.getElementsByTagName('head')[0] || doc.documentElement; | |
}, | |
/** | |
* Get absolute path of dep module.similar to {@link KISSY.Path#resolve} | |
* @param moduleName current module 's name | |
* @param depName dep module 's name | |
* @return {string|Array} | |
*/ | |
normalDepModuleName: function (moduleName, depName) { | |
var i = 0, l; | |
if (!depName) { | |
return depName; | |
} | |
if (typeof depName == 'string') { | |
if (startsWith(depName, '../') || startsWith(depName, './')) { | |
// x/y/z -> x/y/ | |
return Path.resolve(Path.dirname(moduleName), depName); | |
} | |
return Path.normalize(depName); | |
} | |
for (l = depName.length; i < l; i++) { | |
depName[i] = Utils.normalDepModuleName(moduleName, depName[i]); | |
} | |
return depName; | |
}, | |
/** | |
* create modules info | |
* @param runtime | |
* @param modNames | |
*/ | |
createModulesInfo: function (runtime, modNames) { | |
S.each(modNames, function (m) { | |
Utils.createModuleInfo(runtime, m); | |
}); | |
}, | |
/** | |
* create single module info | |
* @param runtime | |
* @param modName | |
* @param [cfg] | |
* @return {KISSY.Loader.Module} | |
*/ | |
createModuleInfo: function (runtime, modName, cfg) { | |
modName = indexMapStr(modName); | |
var mods = runtime.Env.mods, | |
mod = mods[modName]; | |
if (mod) { | |
return mod; | |
} | |
// 防止 cfg 里有 tag,构建 fullpath 需要 | |
mods[modName] = mod = new Loader.Module(S.mix({ | |
name: modName, | |
runtime: runtime | |
}, cfg)); | |
return mod; | |
}, | |
/** | |
* Whether modNames is attached. | |
* @param runtime | |
* @param modNames | |
* @return {Boolean} | |
*/ | |
'isAttached': function (runtime, modNames) { | |
return isStatus(runtime, modNames, ATTACHED); | |
}, | |
/** | |
* Get module values | |
* @param runtime | |
* @param modNames | |
* @return {Array} | |
*/ | |
getModules: function (runtime, modNames) { | |
var mods = [runtime], mod, | |
unalias, | |
allOk, | |
m, | |
runtimeMods = runtime.Env.mods; | |
S.each(modNames, function (modName) { | |
mod = runtimeMods[modName]; | |
if (!mod || mod.getType() != 'css') { | |
unalias = Utils.unalias(runtime, modName); | |
allOk = S.reduce(unalias, function (a, n) { | |
m = runtimeMods[n]; | |
return a && m && m.status == ATTACHED; | |
}, true); | |
if (allOk) { | |
mods.push(runtimeMods[unalias[0]].value); | |
} else { | |
mods.push(null); | |
} | |
} | |
}); | |
return mods; | |
}, | |
attachModsRecursively: function (modNames, runtime, stack, errorList) { | |
stack = stack || []; | |
var i, | |
s = 1, | |
l = modNames.length, | |
stackDepth = stack.length; | |
for (i = 0; i < l; i++) { | |
s = Utils.attachModRecursively(modNames[i], runtime, stack, errorList) && s; | |
stack.length = stackDepth; | |
} | |
return s; | |
}, | |
attachModRecursively: function (modName, runtime, stack, errorList) { | |
var mods = runtime.Env.mods, | |
status, | |
m = mods[modName]; | |
if (!m) { | |
return 0; | |
} | |
status = m.status; | |
if (status == ATTACHED) { | |
return 1; | |
} | |
if (status == ERROR) { | |
errorList.push(m); | |
} | |
if (status != LOADED) { | |
return 0; | |
} | |
if ('@DEBUG@') { | |
if (S.inArray(modName, stack)) { | |
stack.push(modName); | |
S.error('find cyclic dependency between mods: ' + stack); | |
return 0; | |
} | |
stack.push(modName); | |
} | |
if (Utils.attachModsRecursively(m.getNormalizedRequires(), | |
runtime, stack, errorList)) { | |
Utils.attachMod(runtime, m); | |
return 1; | |
} | |
return 0; | |
}, | |
/** | |
* Attach specified mod. | |
* @param runtime | |
* @param mod | |
*/ | |
attachMod: function (runtime, mod) { | |
if (mod.status != LOADED) { | |
return; | |
} | |
var fn = mod.fn; | |
if (typeof fn === 'function') { | |
// 需要解开 index,相对路径 | |
// 但是需要保留 alias,防止值不对应 | |
mod.value = fn.apply(mod, Utils.getModules(runtime, mod.getRequiresWithAlias())); | |
} else { | |
mod.value = fn; | |
} | |
mod.status = ATTACHED; | |
}, | |
/** | |
* Get mod names as array. | |
* @param modNames | |
* @return {String[]} | |
*/ | |
getModNamesAsArray: function (modNames) { | |
if (typeof modNames == 'string') { | |
modNames = modNames.replace(/\s+/g, '').split(','); | |
} | |
return modNames; | |
}, | |
/** | |
* Three effects: | |
* 1. add index : / => /index | |
* 2. unalias : core => dom,event,ua | |
* 3. relative to absolute : ./x => y/x | |
* @param {KISSY} runtime Global KISSY instance | |
* @param {String|String[]} modNames Array of module names | |
* or module names string separated by comma | |
* @param {String} [refModName] | |
* @return {String[]} | |
*/ | |
normalizeModNames: function (runtime, modNames, refModName) { | |
return Utils.unalias(runtime, | |
Utils.normalizeModNamesWithAlias(runtime, modNames, refModName)); | |
}, | |
/** | |
* unalias module name. | |
* @param runtime | |
* @param names | |
* @return {Array} | |
*/ | |
unalias: function (runtime, names) { | |
var ret = [].concat(names), | |
i, | |
m, | |
alias, | |
ok = 0, | |
j, | |
mods = runtime['Env'].mods; | |
while (!ok) { | |
ok = 1; | |
for (i = ret.length - 1; i >= 0; i--) { | |
if ((m = mods[ret[i]]) && (alias = m.alias)) { | |
ok = 0; | |
for (j = alias.length - 1; j >= 0; j--) { | |
if (!alias[j]) { | |
alias.splice(j, 1); | |
} | |
} | |
ret.splice.apply(ret, [i, 1].concat(indexMap(alias))); | |
} | |
} | |
} | |
return ret; | |
}, | |
/** | |
* normalize module names | |
* @param runtime | |
* @param modNames | |
* @param [refModName] | |
* @return {Array} | |
*/ | |
normalizeModNamesWithAlias: function (runtime, modNames, refModName) { | |
var ret = [], i, l; | |
if (modNames) { | |
// 1. index map | |
for (i = 0, l = modNames.length; i < l; i++) { | |
// conditional loader | |
// requires:[window.localStorage?"local-storage":""] | |
if (modNames[i]) { | |
ret.push(pluginAlias(runtime,indexMap(modNames[i]))); | |
} | |
} | |
} | |
// 2. relative to absolute (optional) | |
if (refModName) { | |
ret = Utils.normalDepModuleName(refModName, ret); | |
} | |
return ret; | |
}, | |
/** | |
* register module with factory | |
* @param runtime | |
* @param name | |
* @param fn | |
* @param [config] | |
*/ | |
registerModule: function (runtime, name, fn, config) { | |
name = indexMapStr(name); | |
var mods = runtime.Env.mods, | |
mod = mods[name]; | |
if (mod && mod.fn) { | |
S.log(name + ' is defined more than once'); | |
return; | |
} | |
// 没有 use,静态载入的 add 可能执行 | |
Utils.createModuleInfo(runtime, name); | |
mod = mods[name]; | |
// 注意:通过 S.add(name[, fn[, config]]) 注册的代码,无论是页面中的代码, | |
// 还是 js 文件里的代码,add 执行时,都意味着该模块已经 LOADED | |
S.mix(mod, { | |
name: name, | |
status: LOADED, | |
fn: fn | |
}); | |
S.mix(mod, config); | |
// S.log(name + ' is loaded', 'info'); | |
}, | |
/** | |
* Get mapped path. | |
* @param runtime | |
* @param path | |
* @param [rules] | |
* @return {String} | |
*/ | |
getMappedPath: function (runtime, path, rules) { | |
var mappedRules = rules || | |
runtime.Config.mappedRules || | |
[], | |
i, | |
m, | |
rule; | |
for (i = 0; i < mappedRules.length; i++) { | |
rule = mappedRules[i]; | |
if (m = path.match(rule[0])) { | |
return path.replace(rule[0], rule[1]); | |
} | |
} | |
return path; | |
} | |
}); | |
function isStatus(runtime, modNames, status) { | |
var mods = runtime.Env.mods, | |
mod, | |
i; | |
modNames = S.makeArray(modNames); | |
for (i = 0; i < modNames.length; i++) { | |
mod = mods[modNames[i]]; | |
if (!mod || mod.status !== status) { | |
return 0; | |
} | |
} | |
return 1; | |
} | |
})(KISSY);/** | |
* @ignore | |
* setup data structure for kissy loader | |
* @author [email protected] | |
*/ | |
(function (S) { | |
var Loader = S.Loader, | |
Path = S.Path, | |
IGNORE_PACKAGE_NAME_IN_URI = 'ignorePackageNameInUri', | |
Utils = Loader.Utils; | |
function forwardSystemPackage(self, property) { | |
return property in self ? | |
self[property] : | |
self.runtime.Config[property]; | |
} | |
/** | |
* @class KISSY.Loader.Package | |
* @private | |
* This class should not be instantiated manually. | |
*/ | |
function Package(cfg) { | |
S.mix(this, cfg); | |
} | |
S.augment(Package, { | |
reset: function (cfg) { | |
S.mix(this, cfg); | |
}, | |
/** | |
* Tag for package. | |
* tag can not contain ".", eg: Math.random() ! | |
* @return {String} | |
*/ | |
getTag: function () { | |
return forwardSystemPackage(this, 'tag'); | |
}, | |
/** | |
* Get package name. | |
* @return {String} | |
*/ | |
getName: function () { | |
return this.name; | |
}, | |
/** | |
* Get package base. | |
* @return {String} | |
*/ | |
'getBase': function () { | |
return forwardSystemPackage(this, 'base'); | |
}, | |
getPrefixUriForCombo: function () { | |
var self = this, | |
packageName = self.getName(); | |
return self.getBase() + ( | |
packageName && !self.isIgnorePackageNameInUri() ? | |
(packageName + '/') : | |
'' | |
); | |
}, | |
getPackageUri: function () { | |
var self = this; | |
if (self.packageUri) { | |
return self.packageUri; | |
} | |
return self.packageUri = new S.Uri(this.getPrefixUriForCombo()); | |
}, | |
/** | |
* Get package baseUri | |
* @return {KISSY.Uri} | |
*/ | |
getBaseUri: function () { | |
return forwardSystemPackage(this, 'baseUri'); | |
}, | |
/** | |
* Whether is debug for this package. | |
* @return {Boolean} | |
*/ | |
isDebug: function () { | |
return forwardSystemPackage(this, 'debug'); | |
}, | |
/** | |
* whether request mod file without package name | |
* @return {Boolean} | |
*/ | |
isIgnorePackageNameInUri: function () { | |
return forwardSystemPackage(this, IGNORE_PACKAGE_NAME_IN_URI); | |
}, | |
/** | |
* Get charset for package. | |
* @return {String} | |
*/ | |
getCharset: function () { | |
return forwardSystemPackage(this, 'charset'); | |
}, | |
/** | |
* Whether modules are combined for this package. | |
* @return {Boolean} | |
*/ | |
isCombine: function () { | |
return forwardSystemPackage(this, 'combine'); | |
}, | |
/** | |
* Get package group (for combo). | |
* @returns {String} | |
*/ | |
getGroup: function () { | |
return forwardSystemPackage(this, 'group'); | |
} | |
}); | |
Loader.Package = Package; | |
/** | |
* @class KISSY.Loader.Module | |
* @private | |
* This class should not be instantiated manually. | |
*/ | |
function Module(cfg) { | |
this.status = Loader.Status.INIT; | |
S.mix(this, cfg); | |
this.callbacks = []; | |
} | |
S.augment(Module, { | |
addCallback: function (callback) { | |
this.callbacks.push(callback); | |
}, | |
notifyAll: function () { | |
var callback; | |
var len = this.callbacks.length, | |
i = 0; | |
for (; i < len; i++) { | |
callback = this.callbacks[i]; | |
try { | |
callback(this); | |
} catch (e) { | |
setTimeout(function () { | |
throw e; | |
}, 0); | |
} | |
} | |
this.callbacks = []; | |
}, | |
/** | |
* Set the value of current module | |
* @param v value to be set | |
*/ | |
setValue: function (v) { | |
this.value = v; | |
}, | |
/** | |
* Get the type if current Module | |
* @return {String} css or js | |
*/ | |
getType: function () { | |
var self = this, | |
v = self.type; | |
if (!v) { | |
if (Path.extname(self.name).toLowerCase() == '.css') { | |
v = 'css'; | |
} else { | |
v = 'js'; | |
} | |
self.type = v; | |
} | |
return v; | |
}, | |
getFullPathUri: function () { | |
var self = this, | |
t, | |
fullpathUri, | |
packageBaseUri, | |
packageInfo, | |
packageName, | |
path; | |
if (!self.fullpathUri) { | |
if (self.fullpath) { | |
fullpathUri = new S.Uri(self.fullpath); | |
} else { | |
packageInfo = self.getPackage(); | |
packageBaseUri = packageInfo.getBaseUri(); | |
path = self.getPath(); | |
// #262 | |
if (packageInfo.isIgnorePackageNameInUri() && | |
// native mod does not allow ignore package name | |
(packageName = packageInfo.getName())) { | |
path = Path.relative(packageName, path); | |
} | |
fullpathUri = packageBaseUri.resolve(path); | |
if (t = self.getTag()) { | |
t += '.' + self.getType(); | |
fullpathUri.query.set('t', t); | |
} | |
} | |
self.fullpathUri = fullpathUri; | |
} | |
return self.fullpathUri; | |
}, | |
/** | |
* Get the fullpath of current module if load dynamically | |
* @return {String} | |
*/ | |
getFullPath: function () { | |
var self = this, | |
fullpathUri; | |
if (!self.fullpath) { | |
fullpathUri = self.getFullPathUri(); | |
self.fullpath = Utils.getMappedPath(self.runtime, fullpathUri.toString()); | |
} | |
return self.fullpath; | |
}, | |
/** | |
* Get the path (without package base) | |
* @return {String} | |
*/ | |
getPath: function () { | |
var self = this; | |
return self.path || | |
(self.path = defaultComponentJsName(self)) | |
}, | |
/** | |
* Get the value of current module | |
* @return {*} | |
*/ | |
getValue: function () { | |
return this.value; | |
}, | |
/** | |
* Get the name of current module | |
* @return {String} | |
*/ | |
getName: function () { | |
return this.name; | |
}, | |
/** | |
* Get the package which current module belongs to. | |
* @return {KISSY.Loader.Package} | |
*/ | |
getPackage: function () { | |
var self = this; | |
return self.packageInfo || | |
(self.packageInfo = getPackage(self.runtime, self.name)); | |
}, | |
/** | |
* Get the tag of current module. | |
* tag can not contain ".", eg: Math.random() ! | |
* @return {String} | |
*/ | |
getTag: function () { | |
var self = this; | |
return self.tag || self.getPackage().getTag(); | |
}, | |
/** | |
* Get the charset of current module | |
* @return {String} | |
*/ | |
getCharset: function () { | |
var self = this; | |
return self.charset || self.getPackage().getCharset(); | |
}, | |
/** | |
* Get module objects required by this one | |
* @return {KISSY.Loader.Module[]} | |
*/ | |
'getRequiredMods': function () { | |
var self = this, | |
runtime = self.runtime; | |
return S.map(self.getNormalizedRequires(), function (r) { | |
return Utils.createModuleInfo(runtime, r); | |
}); | |
}, | |
getRequiresWithAlias: function () { | |
var self = this, | |
requiresWithAlias = self.requiresWithAlias, | |
requires = self.requires; | |
if (!requires || requires.length == 0) { | |
return requires || []; | |
} else if (!requiresWithAlias) { | |
self.requiresWithAlias = requiresWithAlias = | |
Utils.normalizeModNamesWithAlias(self.runtime, requires, self.name); | |
} | |
return requiresWithAlias; | |
}, | |
getNormalizedRequires: function () { | |
var self = this, | |
normalizedRequires, | |
normalizedRequiresStatus = self.normalizedRequiresStatus, | |
status = self.status, | |
requires = self.requires; | |
if (!requires || requires.length == 0) { | |
return requires || []; | |
} else if ((normalizedRequires = self.normalizedRequires) && | |
// 事先声明的依赖不能当做 loaded 状态下真正的依赖 | |
(normalizedRequiresStatus == status)) { | |
return normalizedRequires; | |
} else { | |
self.normalizedRequiresStatus = status; | |
return self.normalizedRequires = | |
Utils.normalizeModNames(self.runtime, requires, self.name); | |
} | |
} | |
}); | |
Loader.Module = Module; | |
function defaultComponentJsName(m) { | |
var name = m.name, | |
extname = '.' + m.getType(), | |
min = '-min'; | |
name = Path.join(Path.dirname(name), Path.basename(name, extname)); | |
if (m.getPackage().isDebug()) { | |
min = ''; | |
} | |
return name + min + extname; | |
} | |
var systemPackage = new Loader.Package({ | |
name: '', | |
runtime: S | |
}); | |
function getPackage(self, modName) { | |
var packages = self.config('packages'), | |
pName = '', | |
p; | |
for (p in packages) { | |
// longest match | |
if (S.startsWith(modName, p) && | |
p.length > pName.length) { | |
pName = p; | |
} | |
} | |
return packages[pName] || systemPackage; | |
} | |
})(KISSY);/** | |
* @ignore | |
* script/css load across browser | |
* @author [email protected] | |
*/ | |
(function (S) { | |
var CSS_POLL_INTERVAL = 30, | |
UA= S.UA, | |
Utils = S.Loader.Utils, | |
// central poll for link node | |
timer = 0, | |
monitors = { | |
// node.id:{callback:callback,node:node} | |
}; | |
/** | |
* @ignore | |
* References: | |
* - http://unixpapa.com/js/dyna.html | |
* - http://www.blaze.io/technical/ies-premature-execution-problem/ | |
* | |
* `onload` event is supported in WebKit since 535.23 | |
* - https://bugs.webkit.org/show_activity.cgi?id=38995 | |
* `onload/onerror` event is supported since Firefox 9.0 | |
* - https://bugzilla.mozilla.org/show_bug.cgi?id=185236 | |
* - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events | |
* | |
* monitor css onload across browsers.issue about 404 failure. | |
* | |
* - firefox not ok(4 is wrong): | |
* - http://yearofmoo.com/2011/03/cross-browser-stylesheet-preloading/ | |
* - all is ok | |
* - http://lifesinger.org/lab/2011/load-js-css/css-preload.html | |
* - others | |
* - http://www.zachleat.com/web/load-css-dynamically/ | |
*/ | |
function startCssTimer() { | |
if (!timer) { | |
// S.log('start css polling'); | |
cssPoll(); | |
} | |
} | |
// single thread is ok | |
function cssPoll() { | |
for (var url in monitors) { | |
var callbackObj = monitors[url], | |
node = callbackObj.node, | |
exName, | |
loaded = 0; | |
if (UA.webkit) { | |
// http://www.w3.org/TR/Dom-Level-2-Style/stylesheets.html | |
if (node['sheet']) { | |
S.log('webkit loaded : ' + url); | |
loaded = 1; | |
} | |
} else if (node['sheet']) { | |
try { | |
var cssRules = node['sheet'].cssRules; | |
if (cssRules) { | |
S.log('same domain firefox loaded : ' + url); | |
loaded = 1; | |
} | |
} catch (ex) { | |
exName = ex.name; | |
S.log('firefox getStyle : ' + exName + ' ' + ex.code + ' ' + url); | |
// http://www.w3.org/TR/dom/#dom-domexception-code | |
if (// exName == 'SecurityError' || | |
// for old firefox | |
exName == 'NS_ERROR_DOM_SECURITY_ERR') { | |
S.log(exName + ' firefox loaded : ' + url); | |
loaded = 1; | |
} | |
} | |
} | |
if (loaded) { | |
if (callbackObj.callback) { | |
callbackObj.callback.call(node); | |
} | |
delete monitors[url]; | |
} | |
} | |
if (S.isEmptyObject(monitors)) { | |
timer = 0; | |
// S.log('end css polling'); | |
} else { | |
timer = setTimeout(cssPoll, CSS_POLL_INTERVAL); | |
} | |
} | |
S.mix(Utils, { | |
pollCss: // refer : http://lifesinger.org/lab/2011/load-js-css/css-preload.html | |
// 暂时不考虑如何判断失败,如 404 等 | |
function (node, callback) { | |
var href = node.href, | |
arr; | |
arr = monitors[href] = {}; | |
arr.node = node; | |
arr.callback = callback; | |
startCssTimer(); | |
} | |
}); | |
})(KISSY);/** | |
* @ignore | |
* getScript support for css and js callback after load | |
* @author [email protected] | |
*/ | |
(function (S) { | |
var MILLISECONDS_OF_SECOND = 1000, | |
doc = S.Env.host.document, | |
Utils = S.Loader.Utils, | |
Path = S.Path, | |
jsCssCallbacks = {}, | |
UA = S.UA, | |
// onload for webkit 535.23 Firefox 9.0 | |
// https://bugs.webkit.org/show_activity.cgi?id=38995 | |
// https://bugzilla.mozilla.org/show_bug.cgi?id=185236 | |
// https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events | |
// phantomjs 1.7 == webkit 534.34 | |
isOldWebKit = UA.webkit < 536; | |
S.mix(S, { | |
/** | |
* Load a javascript/css file from the server using a GET HTTP request, | |
* then execute it. | |
* | |
* for example: | |
* @example | |
* getScript(url, success, charset); | |
* // or | |
* getScript(url, { | |
* charset: string | |
* success: fn, | |
* error: fn, | |
* timeout: number | |
* }); | |
* | |
* Note 404/500 status in ie<9 will trigger success callback. | |
* If you want a jsonp operation, please use {@link KISSY.IO} instead. | |
* | |
* @param {String} url resource's url | |
* @param {Function|Object} [success] success callback or config | |
* @param {Function} [success.success] success callback | |
* @param {Function} [success.error] error callback | |
* @param {Number} [success.timeout] timeout (s) | |
* @param {String} [success.charset] charset of current resource | |
* @param {String} [charset] charset of current resource | |
* @return {HTML} script/style node | |
* @member KISSY | |
*/ | |
getScript: function (url, success, charset) { | |
// can not use KISSY.Uri, url can not be encoded for some url | |
// eg: /??dom.js,event.js , ? , should not be encoded | |
var config = success, | |
css = 0, | |
error, | |
timeout, | |
attrs, | |
callbacks, | |
timer; | |
if (S.startsWith(Path.extname(url).toLowerCase(), '.css')) { | |
css = 1; | |
} | |
if (S.isPlainObject(config)) { | |
success = config['success']; | |
error = config['error']; | |
timeout = config['timeout']; | |
charset = config['charset']; | |
attrs = config['attrs']; | |
} | |
callbacks = jsCssCallbacks[url] = jsCssCallbacks[url] || []; | |
callbacks.push([success, error]); | |
if (callbacks.length > 1) { | |
// S.log(' queue js : ' + callbacks.length + ' : for :' + url + ' by ' + (config.source || '')); | |
return callbacks.node; | |
} else { | |
// S.log('init getScript : by ' + config.source); | |
} | |
var head = Utils.docHead(), | |
node = doc.createElement(css ? 'link' : 'script'), | |
clearTimer = function () { | |
if (timer) { | |
timer.cancel(); | |
timer = undefined; | |
} | |
}; | |
if (attrs) { | |
S.each(attrs, function (v, n) { | |
node.setAttribute(n, v); | |
}); | |
} | |
if (charset) { | |
node.charset = charset; | |
} | |
if (css) { | |
node.href = url; | |
node.rel = 'stylesheet'; | |
} else { | |
node.src = url; | |
node.async = true; | |
} | |
callbacks.node = node; | |
var end = function (error) { | |
var index = error, | |
fn; | |
clearTimer(); | |
S.each(jsCssCallbacks[url], function (callback) { | |
if (fn = callback[index]) { | |
fn.call(node); | |
} | |
}); | |
delete jsCssCallbacks[url]; | |
}, | |
useNative = 'onload' in node; | |
if (css && isOldWebKit) { | |
useNative = false; | |
} | |
function onload() { | |
var readyState = node.readyState; | |
if (!readyState || | |
readyState == "loaded" || | |
readyState == "complete") { | |
node.onreadystatechange = node.onload = null; | |
end(0) | |
} | |
} | |
//标准浏览器 css and all script | |
if (useNative) { | |
node.onload = onload; | |
node.onerror = function () { | |
node.onerror = null; | |
end(1); | |
}; | |
} | |
// old chrome/firefox for css | |
else if (css) { | |
Utils.pollCss(node, function () { | |
end(0); | |
}); | |
} else { | |
node.onreadystatechange = onload; | |
} | |
if (timeout) { | |
timer = S.later(function () { | |
end(1); | |
}, timeout * MILLISECONDS_OF_SECOND); | |
} | |
if (css) { | |
// css order matters | |
// so can not use css in head | |
head.appendChild(node); | |
} else { | |
// can use js in head | |
head.insertBefore(node, head.firstChild); | |
} | |
return node; | |
} | |
}); | |
})(KISSY); | |
/* | |
[email protected] refactor@2012-03-29 | |
- 考虑连续重复请求单个 script 的情况,内部排队 | |
[email protected] 2012-03-13 | |
- getScript | |
- 404 in ie<9 trigger success , others trigger error | |
- syntax error in all trigger success | |
*//** | |
* @ignore | |
* Declare config info for KISSY. | |
* @author [email protected] | |
*/ | |
(function (S, undefined) { | |
var Loader = S.Loader, | |
Utils = Loader.Utils, | |
host = S.Env.host, | |
location = host.location, | |
simulatedLocation, | |
locationHref, | |
configFns = S.Config.fns; | |
if (!S.UA.nodejs && location && (locationHref = location.href)) { | |
simulatedLocation = new S.Uri(locationHref) | |
} | |
/* | |
modify current module path | |
[ | |
[/(.+-)min(.js(\?t=\d+)?)$/, '$1$2'], | |
[/(.+-)min(.js(\?t=\d+)?)$/, function(_,m1,m2){ | |
return m1+m2; | |
}] | |
] | |
*/ | |
configFns.map = function (rules) { | |
var Config = this.Config; | |
if (rules === false) { | |
return Config.mappedRules = []; | |
} | |
return Config.mappedRules = (Config.mappedRules || []).concat(rules || []); | |
}; | |
configFns.mapCombo = function (rules) { | |
var Config = this.Config; | |
if (rules === false) { | |
return Config.mappedComboRules = []; | |
} | |
return Config.mappedComboRules = (Config.mappedComboRules || []).concat(rules || []); | |
}; | |
/* | |
包声明 | |
biz -> . | |
表示遇到 biz/x | |
在当前网页路径找 biz/x.js | |
@private | |
*/ | |
configFns.packages = function (cfgs) { | |
var name, | |
Config = this.Config, | |
ps = Config.packages = Config.packages || {}; | |
if (cfgs) { | |
S.each(cfgs, function (cfg, key) { | |
// 兼容数组方式 | |
name = cfg.name || key; | |
// 兼容 path | |
var baseUri = normalizeBase(cfg.base || cfg.path); | |
cfg.name = name; | |
cfg.base = baseUri.toString(); | |
cfg.baseUri = baseUri; | |
cfg.runtime = S; | |
delete cfg.path; | |
if (ps[name]) { | |
ps[name].reset(cfg); | |
} else { | |
ps[name] = new Loader.Package(cfg); | |
} | |
}); | |
return undefined; | |
} else if (cfgs === false) { | |
Config.packages = { | |
}; | |
return undefined; | |
} else { | |
return ps; | |
} | |
}; | |
/* | |
只用来指定模块依赖信息. | |
<code> | |
KISSY.config({ | |
base: '', | |
// dom-min.js | |
debug: '', | |
combine: true, | |
tag: '', | |
packages: { | |
'biz1': { | |
// path change to base | |
base: 'haha', | |
// x.js | |
debug: '', | |
tag: '', | |
combine: false, | |
} | |
}, | |
modules: { | |
'biz1/main': { | |
requires: ['biz1/part1', 'biz1/part2'] | |
} | |
} | |
}); | |
*/ | |
configFns.modules = function (modules) { | |
var self = this, | |
Env = self.Env; | |
if (modules) { | |
S.each(modules, function (modCfg, modName) { | |
Utils.createModuleInfo(self, modName, modCfg); | |
S.mix(Env.mods[modName], modCfg); | |
}); | |
} | |
}; | |
/* | |
KISSY 's base path. | |
*/ | |
configFns.base = function (base) { | |
var self = this, | |
Config = self.Config, | |
baseUri; | |
if (!base) { | |
return Config.base; | |
} | |
baseUri = normalizeBase(base); | |
Config.base = baseUri.toString(); | |
Config.baseUri = baseUri; | |
return undefined; | |
}; | |
function normalizeBase(base) { | |
var baseUri; | |
base = base.replace(/\\/g, '/'); | |
if (base.charAt(base.length - 1) != '/') { | |
base += '/'; | |
} | |
if (simulatedLocation) { | |
baseUri = simulatedLocation.resolve(base); | |
} else { | |
// add scheme for S.Uri | |
// currently only for nodejs | |
if (!S.startsWith(base, 'file:')) { | |
base = 'file:' + base; | |
} | |
baseUri = new S.Uri(base); | |
} | |
return baseUri; | |
} | |
})(KISSY);/** | |
* simple loader for KISSY. one module on request. | |
* @ignore | |
* @author [email protected] | |
*/ | |
(function (S, undefined) { | |
var Loader, Status, Utils, UA, | |
// standard browser 如果 add 没有模块名,模块定义先暂存这里 | |
currentMod = undefined, | |
// ie 开始载入脚本的时间 | |
startLoadTime = 0, | |
// ie6,7,8开始载入脚本对应的模块名 | |
startLoadModName, | |
LOADING, LOADED, ERROR, ATTACHED; | |
Loader = S.Loader; | |
Status = Loader.Status; | |
Utils = Loader.Utils; | |
UA = S.UA; | |
LOADING = Status.LOADING; | |
LOADED = Status.LOADED; | |
ERROR = Status.ERROR; | |
ATTACHED = Status.ATTACHED; | |
/** | |
* @class KISSY.Loader.SimpleLoader | |
* one module one request | |
* @param runtime KISSY | |
* @param waitingModules load checker | |
* @private | |
*/ | |
function SimpleLoader(runtime, waitingModules) { | |
S.mix(this, { | |
runtime: runtime, | |
requireLoadedMods: {}, | |
waitingModules: waitingModules | |
}); | |
} | |
S.augment(SimpleLoader, { | |
use: function (normalizedModNames) { | |
var i, | |
l = normalizedModNames.length; | |
for (i = 0; i < l; i++) { | |
this.loadModule(normalizedModNames[i]); | |
} | |
}, | |
// only load mod requires once | |
// prevent looping dependency sub tree more than once for one use() | |
loadModRequires: function (mod) { | |
// 根据每次 use 缓存子树 | |
var self = this, | |
requireLoadedMods = self.requireLoadedMods, | |
modName = mod.name, | |
requires; | |
if (!requireLoadedMods[modName]) { | |
requireLoadedMods[modName] = 1; | |
requires = mod.getNormalizedRequires(); | |
self.use(requires); | |
} | |
}, | |
loadModule: function (modName) { | |
var self = this, | |
waitingModules = self.waitingModules, | |
runtime = self.runtime, | |
status, | |
isWait, | |
mod = Utils.createModuleInfo(runtime, modName); | |
status = mod.status; | |
if (status == ATTACHED || status == ERROR) { | |
return; | |
} | |
// 已经静态存在在页面上 | |
// 或者该模块不是根据自身模块名动态加载来的(io.js包含 io/base,io/form) | |
if (status === LOADED) { | |
self.loadModRequires(mod); | |
} else { | |
isWait = waitingModules.contains(modName); | |
// prevent duplicate listen for this use | |
// use('a,a') or | |
// user('a,c') a require b, c require b | |
// listen b only once | |
// already waiting, will notify loadReady in the future | |
if (isWait) { | |
return; | |
} | |
mod.addCallback(function () { | |
// 只在 LOADED 后加载依赖项一次 | |
// 防止 config('modules') requires 和模块中 requires 不一致 | |
self.loadModRequires(mod); | |
waitingModules.remove(modName); | |
// notify current loader instance | |
waitingModules.notifyAll(); | |
}); | |
waitingModules.add(modName); | |
// in case parallel use (more than one use) | |
if (status < LOADING) { | |
// load and attach this module | |
self.fetchModule(mod); | |
} | |
} | |
}, | |
// Load a single module. | |
fetchModule: function (mod) { | |
var self = this, | |
runtime = self.runtime, | |
timeout = runtime.Config.timeout, | |
modName = mod.getName(), | |
charset = mod.getCharset(), | |
url = mod.getFullPath(), | |
ie = UA.ie, | |
isCss = mod.getType() == 'css'; | |
mod.status = LOADING; | |
if (ie && !isCss) { | |
startLoadModName = modName; | |
startLoadTime = Number(+new Date()); | |
} | |
S.getScript(url, { | |
attrs: ie ? { | |
'data-mod-name': modName | |
} : undefined, | |
timeout: timeout, | |
// syntaxError in all browser will trigger this | |
// same as #111 : https://github.com/kissyteam/kissy/issues/111 | |
success: function () { | |
if (isCss) { | |
// css does not set LOADED because no add for css! must be set manually | |
Utils.registerModule(runtime, modName, S.noop); | |
} else { | |
// does not need this step for css | |
// standard browser(except ie9) fire load after KISSY.add immediately | |
if (currentMod) { | |
// S.log('standard browser get mod name after load : ' + modName); | |
Utils.registerModule(runtime, | |
modName, currentMod.fn, | |
currentMod.config); | |
currentMod = undefined; | |
} | |
} | |
// nodejs is synchronous, | |
// use('x,y') | |
// need x,y filled into waitingMods first | |
// x,y waiting -> x -> load -> y -> load -> check | |
S.later(checkHandler); | |
}, | |
error: checkHandler, | |
// source:mod.name + '-init', | |
charset: charset | |
}); | |
function checkHandler() { | |
if (mod.fn) { | |
var msg = 'load remote module: "' + modName + | |
'" from: "' + url + '"'; | |
S.log(msg, 'info'); | |
} else { | |
// ie will call success even when getScript error(404) | |
_modError(); | |
} | |
// notify all loader instance | |
mod.notifyAll(); | |
} | |
function _modError() { | |
var msg = modName + | |
' is not loaded! can not find module in path : ' + | |
url; | |
S.log(msg, 'error'); | |
mod.status = ERROR; | |
} | |
} | |
}); | |
// ie 特有,找到当前正在交互的脚本,根据脚本名确定模块名 | |
// 如果找不到,返回发送前那个脚本 | |
function findModuleNameByInteractive() { | |
var scripts = S.Env.host.document.getElementsByTagName('script'), | |
re, | |
i, | |
name, | |
script; | |
for (i = scripts.length - 1; i >= 0; i--) { | |
script = scripts[i]; | |
if (script.readyState == 'interactive') { | |
re = script; | |
break; | |
} | |
} | |
if (re) { | |
name = re.getAttribute('data-mod-name'); | |
} else { | |
// sometimes when read module file from cache, | |
// interactive status is not triggered | |
// module code is executed right after inserting into dom | |
// i has to preserve module name before insert module script into dom, | |
// then get it back here | |
// S.log('can not find interactive script,time diff : ' + (+new Date() - self.__startLoadTime), 'error'); | |
// S.log('old_ie get mod name from cache : ' + self.__startLoadModName); | |
name = startLoadModName; | |
} | |
return name; | |
} | |
SimpleLoader.add = function (name, fn, config, runtime) { | |
if (typeof name === 'function') { | |
config = fn; | |
fn = name; | |
if (UA.ie) { | |
// http://groups.google.com/group/commonjs/browse_thread/thread/5a3358ece35e688e/43145ceccfb1dc02#43145ceccfb1dc02 | |
name = findModuleNameByInteractive(); | |
// S.log('ie get modName by interactive: ' + name); | |
Utils.registerModule(runtime, name, fn, config); | |
startLoadModName = null; | |
startLoadTime = 0; | |
} else { | |
// 其他浏览器 onload 时,关联模块名与模块定义 | |
currentMod = { | |
fn: fn, | |
config: config | |
}; | |
} | |
} | |
}; | |
Loader.SimpleLoader = SimpleLoader; | |
})(KISSY); | |
/* | |
2013-06-04 [email protected] | |
- refactor merge combo loader and simple loader | |
- support error callback | |
2012-10-08 [email protected] refactor | |
- use 调用先统一 load 再统一 attach | |
2012-09-20 [email protected] refactor | |
- 参考 async 重构,去除递归回调 | |
*//** | |
* combo loader for KISSY. using combo to load module files. | |
* @ignore | |
* @author [email protected] | |
*/ | |
(function (S) { | |
function loadScripts(rss, callback, charset, timeout) { | |
var count = rss && rss.length, | |
errorList = [], | |
successList = []; | |
function complete() { | |
if (!(--count)) { | |
callback(successList, errorList); | |
} | |
} | |
S.each(rss, function (rs) { | |
S.getScript(rs.fullpath, { | |
timeout: timeout, | |
success: function () { | |
successList.push(rs); | |
complete(); | |
}, | |
error: function () { | |
errorList.push(rs); | |
complete(); | |
}, | |
charset: charset | |
}); | |
}); | |
} | |
var Loader = S.Loader, | |
Status = Loader.Status, | |
Utils = Loader.Utils, | |
LOADING = Status.LOADING, | |
LOADED = Status.LOADED, | |
ERROR = Status.ERROR, | |
groupTag = S.now(), | |
ATTACHED = Status.ATTACHED; | |
ComboLoader.groupTag = groupTag; | |
/** | |
* @class KISSY.Loader.ComboLoader | |
* using combo to load module files | |
* @param runtime KISSY | |
* @param waitingModules | |
* @private | |
*/ | |
function ComboLoader(runtime, waitingModules) { | |
S.mix(this, { | |
runtime: runtime, | |
waitingModules: waitingModules | |
}); | |
} | |
function debugRemoteModules(rss) { | |
S.each(rss, function (rs) { | |
var ms = []; | |
S.each(rs.mods, function (m) { | |
if (m.status == LOADED) { | |
ms.push(m.name); | |
} | |
}); | |
if (ms.length) { | |
S.log('load remote modules: "' + ms.join(', ') + '" from: "' + rs.fullpath + '"'); | |
} | |
}); | |
} | |
function getCommonPrefix(str1, str2) { | |
str1 = str1.split(/\//); | |
str2 = str2.split(/\//); | |
var l = Math.min(str1.length, str2.length); | |
for (var i = 0; i < l; i++) { | |
if (str1[i] !== str2[i]) { | |
break; | |
} | |
} | |
return str1.slice(0, i).join('/') + '/'; | |
} | |
/** | |
* Returns hash code of a string | |
* djb2 algorithm | |
* @param {String} str string to be hashed | |
* @private | |
* @return {String} the hash code | |
*/ | |
function getHash(str) { | |
var hash = 5381, | |
i; | |
for (i = str.length; --i > -1;) { | |
hash = ((hash << 5) + hash) + str.charCodeAt(i); | |
/* hash * 33 + char */ | |
} | |
return hash + ''; | |
} | |
// ----------------------- private end | |
S.augment(ComboLoader, { | |
/** | |
* use, _forceSync for kissy.js, initialize dom,event sync | |
*/ | |
use: function (normalizedModNames) { | |
var self = this, | |
allModNames, | |
comboUrls, | |
timeout = S.Config.timeout, | |
runtime = self.runtime; | |
allModNames = S.keys(self.calculate(normalizedModNames)); | |
Utils.createModulesInfo(runtime, allModNames); | |
comboUrls = self.getComboUrls(allModNames); | |
// load css first to avoid page blink | |
S.each(comboUrls.css, function (cssOne) { | |
loadScripts(cssOne, function (success, error) { | |
if ('@DEBUG@') { | |
debugRemoteModules(success); | |
} | |
S.each(success, function (one) { | |
S.each(one.mods, function (mod) { | |
Utils.registerModule(runtime, mod.getName(), S.noop); | |
// notify all loader instance | |
mod.notifyAll(); | |
}); | |
}); | |
S.each(error, function (one) { | |
S.each(one.mods, function (mod) { | |
var msg = mod.name + | |
' is not loaded! can not find module in path : ' + | |
one.fullpath; | |
S.log(msg, 'error'); | |
mod.status = ERROR; | |
// notify all loader instance | |
mod.notifyAll(); | |
}); | |
}); | |
}, cssOne.charset, timeout); | |
}); | |
// jss css download in parallel | |
S.each(comboUrls['js'], function (jsOne) { | |
loadScripts(jsOne, function (success) { | |
if ('@DEBUG@') { | |
debugRemoteModules(success); | |
} | |
S.each(jsOne, function (one) { | |
S.each(one.mods, function (mod) { | |
// fix #111 | |
// https://github.com/kissyteam/kissy/issues/111 | |
if (!mod.fn) { | |
var msg = mod.name + | |
' is not loaded! can not find module in path : ' + | |
one.fullpath; | |
S.log(msg, 'error'); | |
mod.status = ERROR; | |
} | |
// notify all loader instance | |
mod.notifyAll(); | |
}); | |
}); | |
}, jsOne.charset, timeout); | |
}); | |
}, | |
/** | |
* calculate dependency | |
* @param modNames | |
* @param [cache] | |
* @param ret | |
* @return {Array} | |
*/ | |
calculate: function (modNames, cache, ret) { | |
var i, | |
m, | |
mod, | |
modStatus, | |
self = this, | |
waitingModules = self.waitingModules, | |
runtime = self.runtime; | |
ret = ret || {}; | |
// 提高性能,不用每个模块都再次全部依赖计算 | |
// 做个缓存,每个模块对应的待动态加载模块 | |
cache = cache || {}; | |
for (i = 0; i < modNames.length; i++) { | |
m = modNames[i]; | |
if (cache[m]) { | |
continue; | |
} | |
cache[m] = 1; | |
mod = Utils.createModuleInfo(runtime, m); | |
modStatus = mod.status; | |
if (modStatus === ERROR) { | |
continue; | |
} | |
if (modStatus != LOADED && modStatus != ATTACHED) { | |
if (!waitingModules.contains(m)) { | |
if (modStatus != LOADING) { | |
mod.status = LOADING; | |
ret[m] = 1; | |
} | |
mod.addCallback(function (mod) { | |
waitingModules.remove(mod.getName()); | |
// notify current loader instance | |
waitingModules.notifyAll(); | |
}); | |
waitingModules.add(m); | |
} | |
} | |
self.calculate(mod.getNormalizedRequires(), cache, ret); | |
} | |
return ret; | |
}, | |
getComboMods: function (modNames, comboPrefixes) { | |
var comboMods = {}, | |
packageUri, | |
runtime = this.runtime, | |
i = 0, | |
l = modNames.length, | |
modName, mod, packageInfo, type, typedCombos, mods, | |
tag, charset, packagePath, | |
packageName, group, fullpath; | |
for (; i < l; ++i) { | |
modName = modNames[i]; | |
mod = Utils.createModuleInfo(runtime, modName); | |
type = mod.getType(); | |
fullpath = mod.getFullPath(); | |
packageInfo = mod.getPackage(); | |
packageName = packageInfo.getName(); | |
charset = packageInfo.getCharset(); | |
tag = packageInfo.getTag(); | |
group = packageInfo.getGroup(); | |
packagePath = packageInfo.getPrefixUriForCombo(); | |
packageUri = packageInfo.getPackageUri(); | |
var comboName = packageName; | |
// whether group packages can be combined (except default package and non-combo modules) | |
if ((mod.canBeCombined = packageInfo.isCombine() && | |
S.startsWith(fullpath, packagePath)) && group) { | |
// combined package name | |
comboName = group + '_' + charset + '_' + groupTag; | |
var groupPrefixUri; | |
if (groupPrefixUri = comboPrefixes[comboName]) { | |
if (groupPrefixUri.isSameOriginAs(packageUri)) { | |
groupPrefixUri.setPath(getCommonPrefix(groupPrefixUri.getPath(), | |
packageUri.getPath())); | |
} else { | |
comboName = packageName; | |
comboPrefixes[packageName] = packageUri; | |
} | |
} else { | |
comboPrefixes[comboName] = packageUri.clone(); | |
} | |
} else { | |
comboPrefixes[packageName] = packageUri; | |
} | |
typedCombos = comboMods[type] = comboMods[type] || {}; | |
if (!(mods = typedCombos[comboName])) { | |
mods = typedCombos[comboName] = []; | |
mods.charset = charset; | |
mods.tags = [tag]; // [package tag] | |
} else { | |
if (mods.tags.length == 1 && mods.tags[0] == tag) { | |
} else { | |
mods.tags.push(tag); | |
} | |
} | |
mods.push(mod); | |
} | |
return comboMods; | |
}, | |
/** | |
* Get combo urls | |
* @param modNames | |
* @return {Object} | |
*/ | |
getComboUrls: function (modNames) { | |
var runtime = this.runtime, | |
Config = runtime.Config, | |
comboPrefix = Config.comboPrefix, | |
comboSep = Config.comboSep, | |
maxFileNum = Config.comboMaxFileNum, | |
maxUrlLength = Config.comboMaxUrlLength; | |
var comboPrefixes = {}; | |
// {type, {comboName, [modInfo]}}} | |
var comboMods = this.getComboMods(modNames, comboPrefixes); | |
// {type, {comboName, [url]}}} | |
var comboRes = {}; | |
// generate combo urls | |
for (var type in comboMods) { | |
comboRes[type] = {}; | |
for (var comboName in comboMods[type]) { | |
var currentComboUrls = []; | |
var currentComboMods = []; | |
var mods = comboMods[type][comboName]; | |
var tags = mods.tags; | |
var tag = tags.length > 1 ? getHash(tags.join('')) : tags[0]; | |
var suffix = (tag ? '?t=' + encodeURIComponent(tag) + '.' + type : ''), | |
suffixLength = suffix.length, | |
basePrefix = comboPrefixes[comboName].toString(), | |
baseLen = basePrefix.length, | |
prefix = basePrefix + comboPrefix, | |
res = comboRes[type][comboName] = []; | |
var l = prefix.length; | |
res.charset = mods.charset; | |
res.mods = []; | |
function pushComboUrl() { | |
// map the whole combo path | |
//noinspection JSReferencingMutableVariableFromClosure | |
res.push({ | |
fullpath: Utils.getMappedPath(runtime, prefix + | |
currentComboUrls.join(comboSep) + suffix, | |
Config.mappedComboRules), | |
mods: currentComboMods | |
}); | |
} | |
for (var i = 0; i < mods.length; i++) { | |
var currentMod = mods[i]; | |
res.mods.push(currentMod); | |
// map individual module | |
var fullpath = currentMod.getFullPath(); | |
if (!currentMod.canBeCombined) { | |
res.push({ | |
fullpath: fullpath, | |
mods: [currentMod] | |
}); | |
continue; | |
} | |
// ignore query parameter | |
var path = fullpath.slice(baseLen).replace(/\?.*$/, ''); | |
currentComboUrls.push(path); | |
currentComboMods.push(currentMod); | |
if (currentComboUrls.length > maxFileNum || | |
(l + currentComboUrls.join(comboSep).length + suffixLength > maxUrlLength)) { | |
currentComboUrls.pop(); | |
currentComboMods.pop(); | |
pushComboUrl(); | |
currentComboUrls = []; | |
currentComboMods = []; | |
i--; | |
} | |
} | |
if (currentComboUrls.length) { | |
pushComboUrl(); | |
} | |
} | |
} | |
return comboRes; | |
} | |
}); | |
Loader.ComboLoader = ComboLoader; | |
})(KISSY); | |
/* | |
2013-07-25 阿古, yiminghe | |
- support group combo for packages | |
2013-06-04 [email protected] | |
- refactor merge combo loader and simple loader | |
- support error callback | |
2012-02-20 yiminghe note: | |
- three status | |
0: initialized | |
LOADED: load into page | |
ATTACHED: fn executed | |
*//** | |
* @ignore | |
* mix loader into S and infer KISSY baseUrl if not set | |
* @author [email protected] | |
*/ | |
(function (S, undefined) { | |
var Loader = KISSY.Loader, | |
Env = S.Env, | |
Utils = Loader.Utils, | |
SimpleLoader = Loader.SimpleLoader, | |
ComboLoader = Loader.ComboLoader; | |
function WaitingModules(fn) { | |
S.mix(this, { | |
fn: fn, | |
waitMods: {} | |
}); | |
} | |
WaitingModules.prototype = { | |
constructor: WaitingModules, | |
notifyAll: function () { | |
var self = this, | |
fn = self.fn; | |
if (fn && S.isEmptyObject(self.waitMods)) { | |
self.fn = null; | |
fn(); | |
} | |
}, | |
add: function (modName) { | |
this.waitMods[modName] = 1; | |
}, | |
remove: function (modName) { | |
delete this.waitMods[modName]; | |
}, | |
contains: function (modName) { | |
return this.waitMods[modName]; | |
} | |
}; | |
Loader.WaitingModules = WaitingModules; | |
S.mix(S, { | |
/** | |
* Registers a module with the KISSY global. | |
* @param {String} name module name. | |
* it must be set if combine is true in {@link KISSY#config} | |
* @param {Function} fn module definition function that is used to return | |
* this module value | |
* @param {KISSY} fn.S KISSY global instance | |
* @param {Object} [cfg] module optional config data | |
* @param {String[]} cfg.requires this module's required module name list | |
* @member KISSY | |
* | |
* for example: | |
* @example | |
* // dom module's definition | |
* KISSY.add('dom', function(S, xx){ | |
* return {css: function(el, name, val){}}; | |
* },{ | |
* requires:['xx'] | |
* }); | |
*/ | |
add: function (name, fn, cfg) { | |
if (typeof name == 'string') { | |
Utils.registerModule(S, name, fn, cfg); | |
} else { | |
SimpleLoader.add(name, fn, cfg, S); | |
} | |
}, | |
/** | |
* Attached one or more modules to global KISSY instance. | |
* @param {String|String[]} modNames moduleNames. 1-n modules to bind(use comma to separate) | |
* @param {Function} success callback function executed | |
* when KISSY has the required functionality. | |
* @param {KISSY} success.S KISSY instance | |
* @param success.x... used module values | |
* @member KISSY | |
* | |
* for example: | |
* @example | |
* // loads and attached overlay,dd and its dependencies | |
* KISSY.use('overlay,dd', function(S, Overlay){}); | |
*/ | |
use: function (modNames, success) { | |
var Config = S.Config, | |
normalizedModNames, | |
loader, | |
error, | |
sync, | |
waitingModules = new WaitingModules(loadReady); | |
if (S.isPlainObject(success)) { | |
sync = success.sync; | |
error = success.error; | |
success = success.success; | |
} | |
modNames = Utils.getModNamesAsArray(modNames); | |
modNames = Utils.normalizeModNamesWithAlias(S, modNames); | |
normalizedModNames = Utils.unalias(S, modNames); | |
function loadReady() { | |
var errorList = [], | |
ret; | |
ret = Utils.attachModsRecursively(normalizedModNames, S, undefined, errorList); | |
if (ret) { | |
if (success) { | |
if (sync) { | |
success.apply(S, Utils.getModules(S, modNames)); | |
} else { | |
// standalone error trace | |
setTimeout(function () { | |
success.apply(S, Utils.getModules(S, modNames)); | |
}, 0); | |
} | |
} | |
} else if (errorList.length) { | |
if (error) { | |
if (sync) { | |
error.apply(S, errorList); | |
} else { | |
setTimeout(function () { | |
error.apply(S, errorList); | |
}, 0); | |
} | |
} | |
} else { | |
waitingModules.fn = loadReady; | |
loader.use(normalizedModNames); | |
} | |
} | |
if (Config.combine && !S.UA.nodejs) { | |
loader = new ComboLoader(S, waitingModules); | |
} else { | |
loader = new SimpleLoader(S, waitingModules); | |
} | |
// in case modules is loaded statically | |
// synchronous check | |
// but always async for loader | |
if (sync) { | |
waitingModules.notifyAll(); | |
} else { | |
setTimeout(function () { | |
waitingModules.notifyAll(); | |
}, 0); | |
} | |
return S; | |
}, | |
require: function (name) { | |
return Utils.getModules(S, | |
Utils.normalizeModNamesWithAlias(S, [name]))[1]; | |
} | |
}); | |
function returnJson(s) { | |
return (new Function('return ' + s))(); | |
} | |
var baseReg = /^(.*)(seed|kissy)(?:-min)?\.js[^/]*/i, | |
baseTestReg = /(seed|kissy)(?:-min)?\.js/i; | |
function getBaseInfoFromOneScript(script) { | |
// can not use KISSY.Uri | |
// /??x.js,dom.js for tbcdn | |
var src = script.src || ''; | |
if (!src.match(baseTestReg)) { | |
return 0; | |
} | |
var baseInfo = script.getAttribute('data-config'); | |
if (baseInfo) { | |
baseInfo = returnJson(baseInfo); | |
} else { | |
baseInfo = {}; | |
} | |
var comboPrefix = baseInfo.comboPrefix = baseInfo.comboPrefix || '??'; | |
var comboSep = baseInfo.comboSep = baseInfo.comboSep || ','; | |
var parts , | |
base, | |
index = src.indexOf(comboPrefix); | |
// no combo | |
if (index == -1) { | |
base = src.replace(baseReg, '$1'); | |
} else { | |
base = src.substring(0, index); | |
// a.tbcdn.cn??y.js, ie does not insert / after host | |
// a.tbcdn.cn/combo? comboPrefix=/combo? | |
if (base.charAt(base.length - 1) != '/') { | |
base += '/'; | |
} | |
parts = src.substring(index + comboPrefix.length).split(comboSep); | |
S.each(parts, function (part) { | |
if (part.match(baseTestReg)) { | |
base += part.replace(baseReg, '$1'); | |
return false; | |
} | |
return undefined; | |
}); | |
} | |
return S.mix({ | |
base: base | |
}, baseInfo); | |
} | |
/** | |
* get base from seed.js | |
* @return {Object} base for kissy | |
* @ignore | |
* | |
* for example: | |
* @example | |
* http://a.tbcdn.cn/??s/kissy/x.y.z/seed-min.js,p/global/global.js | |
* note about custom combo rules, such as yui3: | |
* combo-prefix='combo?' combo-sep='&' | |
*/ | |
function getBaseInfo() { | |
// get base from current script file path | |
// notice: timestamp | |
var scripts = Env.host.document.getElementsByTagName('script'), | |
i, | |
info; | |
for (i = scripts.length - 1; i >= 0; i--) { | |
if (info = getBaseInfoFromOneScript(scripts[i])) { | |
return info; | |
} | |
} | |
S.error('must load kissy by file name: seed.js or seed-min.js'); | |
return null; | |
} | |
if (S.UA.nodejs) { | |
// nodejs: no tag | |
S.config({ | |
charset: 'utf-8', | |
base: __dirname.replace(/\\/g, '/').replace(/\/$/, '') + '/' | |
}); | |
} else { | |
// will transform base to absolute path | |
S.config(S.mix({ | |
// 2k(2048) url length | |
comboMaxUrlLength: 2000, | |
// file limit number for a single combo url | |
comboMaxFileNum: 40, | |
charset: 'utf-8', | |
lang: 'zh-cn', | |
tag: '20130813190516' | |
}, getBaseInfo())); | |
} | |
// Initializes loader. | |
Env.mods = {}; // all added mods | |
})(KISSY); | |
/* | |
2013-06-04 [email protected] | |
- refactor merge combo loader and simple loader | |
- support error callback | |
*//** | |
* i18n plugin for kissy loader | |
* @author [email protected] | |
*/ | |
KISSY.add('i18n', { | |
alias: function (S, name) { | |
return name + '/i18n/' + S.Config.lang; | |
} | |
});/** | |
* @ignore | |
* web.js | |
* @author [email protected], [email protected] | |
* this code can only run at browser environment | |
*/ | |
(function (S, undefined) { | |
var win = S.Env.host, | |
UA = S.UA, | |
doc = win['document'], | |
docElem = doc && doc.documentElement, | |
location = win.location, | |
EMPTY = '', | |
readyDefer = new S.Defer(), | |
readyPromise = readyDefer.promise, | |
// The number of poll times. | |
POLL_RETIRES = 500, | |
// The poll interval in milliseconds. | |
POLL_INTERVAL = 40, | |
// #id or id | |
RE_ID_STR = /^#?([\w-]+)$/, | |
RE_NOT_WHITESPACE = /\S/, | |
standardEventModel = !!(doc && doc.addEventListener), | |
DOM_READY_EVENT = 'DOMContentLoaded', | |
READY_STATE_CHANGE_EVENT = 'readystatechange', | |
LOAD_EVENT = 'load', | |
COMPLETE = 'complete', | |
addEventListener = standardEventModel ? function (el, type, fn) { | |
el.addEventListener(type, fn, false); | |
} : function (el, type, fn) { | |
el.attachEvent('on' + type, fn); | |
}, | |
removeEventListener = standardEventModel ? function (el, type, fn) { | |
el.removeEventListener(type, fn, false); | |
} : function (el, type, fn) { | |
el.detachEvent('on' + type, fn); | |
}; | |
S.mix(S, { | |
/** | |
* A crude way of determining if an object is a window | |
* @member KISSY | |
*/ | |
isWindow: function (obj) { | |
return obj != null && obj == obj.window; | |
}, | |
/** | |
* get xml representation of data | |
* @param {String} data | |
* @member KISSY | |
*/ | |
parseXML: function (data) { | |
// already a xml | |
if (data.documentElement) { | |
return data; | |
} | |
var xml; | |
try { | |
// Standard | |
if (win['DOMParser']) { | |
xml = new DOMParser().parseFromString(data, 'text/xml'); | |
} else { // IE | |
xml = new ActiveXObject('Microsoft.XMLDOM'); | |
xml.async = false; | |
xml.loadXML(data); | |
} | |
} catch (e) { | |
S.log('parseXML error : '); | |
S.log(e); | |
xml = undefined; | |
} | |
if (!xml || !xml.documentElement || xml.getElementsByTagName('parsererror').length) { | |
S.error('Invalid XML: ' + data); | |
} | |
return xml; | |
}, | |
/** | |
* Evaluates a script in a global context. | |
* @member KISSY | |
*/ | |
globalEval: function (data) { | |
if (data && RE_NOT_WHITESPACE.test(data)) { | |
// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context | |
// http://msdn.microsoft.com/en-us/library/ie/ms536420(v=vs.85).aspx always return null | |
( win.execScript || function (data) { | |
win[ 'eval' ].call(win, data); | |
} )(data); | |
} | |
}, | |
/** | |
* Specify a function to execute when the Dom is fully loaded. | |
* @param fn {Function} A function to execute after the Dom is ready | |
* | |
* for example: | |
* @example | |
* KISSY.ready(function(S){}); | |
* | |
* @chainable | |
* @member KISSY | |
*/ | |
ready: function (fn) { | |
readyPromise.done(fn); | |
return this; | |
}, | |
/** | |
* Executes the supplied callback when the item with the supplied id is found. | |
* @param id {String} The id of the element, or an array of ids to look for. | |
* @param fn {Function} What to execute when the element is found. | |
* @member KISSY | |
*/ | |
available: function (id, fn) { | |
id = (id + EMPTY).match(RE_ID_STR)[1]; | |
var retryCount = 1, | |
node, | |
timer = S.later(function () { | |
if ((node = doc.getElementById(id)) && (fn(node) || 1) || | |
++retryCount > POLL_RETIRES) { | |
timer.cancel(); | |
} | |
}, POLL_INTERVAL, true); | |
} | |
}); | |
function fireReady() { | |
// nodejs | |
if (doc && !UA.nodejs) { | |
removeEventListener(win, LOAD_EVENT, fireReady); | |
} | |
readyDefer.resolve(S); | |
} | |
/** | |
* Binds ready events. | |
* @ignore | |
*/ | |
function bindReady() { | |
// Catch cases where ready() is called after the | |
// browser event has already occurred. | |
if (!doc || doc.readyState === COMPLETE) { | |
fireReady(); | |
return; | |
} | |
// A fallback to window.onload, that will always work | |
addEventListener(win, LOAD_EVENT, fireReady); | |
// w3c mode | |
if (standardEventModel) { | |
var domReady = function () { | |
removeEventListener(doc, DOM_READY_EVENT, domReady); | |
fireReady(); | |
}; | |
addEventListener(doc, DOM_READY_EVENT, domReady); | |
} | |
// IE event model is used | |
else { | |
var stateChange = function () { | |
if (doc.readyState === COMPLETE) { | |
removeEventListener(doc, READY_STATE_CHANGE_EVENT, stateChange); | |
fireReady(); | |
} | |
}; | |
// ensure firing before onload (but completed after all inner iframes is loaded) | |
// maybe late but safe also for iframes | |
addEventListener(doc, READY_STATE_CHANGE_EVENT, stateChange); | |
// If IE and not a frame | |
// continually check to see if the document is ready | |
var notframe, | |
doScroll = docElem && docElem.doScroll; | |
try { | |
notframe = (win['frameElement'] === null); | |
} catch (e) { | |
notframe = false; | |
} | |
// can not use in iframe,parent window is dom ready so doScroll is ready too | |
if (doScroll && notframe) { | |
var readyScroll = function () { | |
try { | |
// Ref: http://javascript.nwbox.com/IEContentLoaded/ | |
doScroll('left'); | |
fireReady(); | |
} catch (ex) { | |
//S.log('detect document ready : ' + ex); | |
setTimeout(readyScroll, POLL_INTERVAL); | |
} | |
}; | |
readyScroll(); | |
} | |
} | |
} | |
// If url contains '?ks-debug', debug mode will turn on automatically. | |
if (location && (location.search || EMPTY).indexOf('ks-debug') !== -1) { | |
S.Config.debug = true; | |
} | |
// bind on start | |
// in case when you bind but the DOMContentLoaded has triggered | |
// then you has to wait onload | |
// worst case no callback at all | |
bindReady(); | |
if (UA.ie) { | |
try { | |
doc.execCommand('BackgroundImageCache', false, true); | |
} catch (e) { | |
} | |
} | |
})(KISSY, undefined); | |
/** | |
* @ignore | |
* Default KISSY Gallery and core alias. | |
* @author [email protected] | |
*/ | |
(function (S) { | |
S.config({ | |
modules: { | |
core: { | |
alias: ['dom', 'event', 'io', 'anim', 'base', 'node', 'json', 'ua', 'cookie'] | |
} | |
} | |
}); | |
if (typeof location != 'undefined') { | |
var https = S.startsWith(location.href, 'https'); | |
var prefix = https ? 'https://s.tbcdn.cn/s/kissy/' : 'http://a.tbcdn.cn/s/kissy/'; | |
S.config({ | |
packages: { | |
gallery: { | |
base: prefix | |
}, | |
mobile: { | |
base: prefix | |
} | |
} | |
}); | |
} | |
})(KISSY); | |
(function(config,Features,UA){ | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'anim': {requires: ['dom','anim/base','anim/timer',KISSY.Features.isTransitionSupported() ? "anim/transition" : ""]} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'anim/base': {requires: ['dom','event/custom']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'anim/timer': {requires: ['dom','event','anim/base']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'anim/transition': {requires: ['dom','event','anim/base']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'base': {requires: ['event/custom']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'button': {requires: ['node','component/control']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'color': {requires: ['base']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'combobox': {requires: ['node','component/control','menu','base','io']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'component/container': {requires: ['component/control','component/manager']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'component/control': {requires: ['node','base','promise','component/manager','xtemplate']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'component/extension/align': {requires: ['node']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'component/extension/delegate-children': {requires: ['node','component/manager']} | |
}); | |
if (KISSY.UA.ie !== 6) { | |
KISSY.add("component/extension/shim-render", function () { | |
}); | |
}/*Generated By KISSY Module Compiler*/ | |
config({ | |
'component/plugin/drag': {requires: ['base','dd']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'component/plugin/resize': {requires: ['resizable']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'date/format': {requires: ['date/gregorian','i18n!date']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'date/gregorian': {requires: ['i18n!date']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'date/picker': {requires: ['node','date/gregorian','i18n!date/picker','component/control','date/format']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'dd': {requires: ['node','base']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'dd/plugin/constrain': {requires: ['base','node']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'dd/plugin/proxy': {requires: ['node','base','dd']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'dd/plugin/scroll': {requires: ['dd','base','node']} | |
}); | |
config({ | |
"dom/basic": { | |
"alias": [ | |
'dom/base', | |
Features.isIELessThan(9) ? 'dom/ie' : '', | |
Features.isClassListSupported() ? '' : 'dom/class-list' | |
] | |
}, | |
"dom": { | |
"alias": [ | |
'dom/basic', | |
!Features.isQuerySelectorSupported() ? 'dom/selector' : '' | |
] | |
} | |
});/*Generated By KISSY Module Compiler*/ | |
config({ | |
'dom/class-list': {requires: ['dom/base']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'dom/ie': {requires: ['dom/base']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'dom/selector': {requires: ['dom/basic']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'editor': {requires: ['node','html-parser','component/control']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'event': {requires: ['event/dom','event/custom']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'event/custom': {requires: ['event/base']} | |
}); | |
config({ | |
"event/dom": { | |
"alias": [ | |
"event/dom/base", | |
Features.isTouchEventSupported() || Features.isMsPointerSupported() ? | |
'event/dom/touch' : '', | |
Features.isDeviceMotionSupported() ? | |
'event/dom/shake' : '', | |
Features.isHashChangeSupported() ? | |
'' : 'event/dom/hashchange', | |
Features.isIELessThan(9) ? | |
'event/dom/ie' : '', | |
UA.ie ? '' : 'event/dom/focusin' | |
] | |
} | |
});/*Generated By KISSY Module Compiler*/ | |
config({ | |
'event/dom/base': {requires: ['event/base','dom']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'event/dom/focusin': {requires: ['event/dom/base']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'event/dom/hashchange': {requires: ['event/dom/base','dom']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'event/dom/ie': {requires: ['event/dom/base','dom']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'event/dom/shake': {requires: ['event/dom/base']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'event/dom/touch': {requires: ['event/dom/base','dom']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'filter-menu': {requires: ['menu','node','component/extension/content-render']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'io': {requires: ['dom','event']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'kison': {requires: ['base']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'menu': {requires: ['node','component/container','component/extension/delegate-children','component/control','component/extension/content-render','component/extension/align','component/extension/shim-render']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'menubutton': {requires: ['node','button','component/extension/content-render','menu']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'mvc': {requires: ['base','node','io','json']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'node': {requires: ['dom','event/dom','anim']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'overlay': {requires: ['component/container','component/extension/align','node','component/extension/content-render','component/extension/shim-render']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'resizable': {requires: ['node','base','dd']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'resizable/plugin/proxy': {requires: ['base','node']} | |
}); | |
config({ | |
"scroll-view": { | |
"alias": [Features.isTouchEventSupported() ? 'scroll-view/drag' : 'scroll-view/base'] | |
} | |
});/*Generated By KISSY Module Compiler*/ | |
config({ | |
'scroll-view/base': {requires: ['node','component/container','component/extension/content-render']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'scroll-view/drag': {requires: ['scroll-view/base','dd','node']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'scroll-view/plugin/pull-to-refresh': {requires: ['base']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'scroll-view/plugin/scrollbar': {requires: ['base','node','dd','component/control']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'separator': {requires: ['component/control']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'split-button': {requires: ['component/container','button','menubutton']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'stylesheet': {requires: ['dom']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'swf': {requires: ['dom','json','base']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'tabs': {requires: ['component/container','toolbar','button']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'toolbar': {requires: ['component/container','component/extension/delegate-children','node']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'tree': {requires: ['node','component/container','component/extension/content-render','component/extension/delegate-children']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'xtemplate': {requires: ['xtemplate/runtime','xtemplate/compiler']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'xtemplate/compiler': {requires: ['xtemplate/runtime']} | |
}); | |
/*Generated By KISSY Module Compiler*/ | |
config({ | |
'xtemplate/nodejs': {requires: ['xtemplate']} | |
}); | |
})(function(c){ | |
KISSY.config('modules', c); | |
},KISSY.Features,KISSY.UA); | |
/** | |
* @ignore | |
* 1. export KISSY 's functionality to module system | |
* 2. export light-weighted json parse | |
*/ | |
(function (S) { | |
// empty mod for conditional loading | |
S.add('empty', S.noop); | |
S.add('promise', function () { | |
return S.Promise; | |
}); | |
S.add('ua', function () { | |
return S.UA; | |
}); | |
S.add('uri', function () { | |
return S.Uri; | |
}); | |
S.add('path', function () { | |
return S.Path | |
}); | |
var UA = S.UA, | |
Env = S.Env, | |
win = Env.host, | |
doc = win.document || {}, | |
documentMode = doc.documentMode, | |
nativeJson = ((UA.nodejs && typeof global === 'object') ? global : win).JSON; | |
// ie 8.0.7600.16315@win7 json bug! | |
if (documentMode && documentMode < 9) { | |
nativeJson = null; | |
} | |
if (nativeJson) { | |
S.add('json', function () { | |
return S.JSON = nativeJson; | |
}); | |
// light weight json parse | |
S.parseJson = function (data) { | |
return nativeJson.parse(data); | |
}; | |
} else { | |
// Json RegExp | |
var INVALID_CHARS_REG = /^[\],:{}\s]*$/, | |
INVALID_BRACES_REG = /(?:^|:|,)(?:\s*\[)+/g, | |
INVALID_ESCAPES_REG = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, | |
INVALID_TOKENS_REG = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g; | |
S.parseJson = function (data) { | |
if (data === null) { | |
return data; | |
} | |
if (typeof data === "string") { | |
// for ie | |
data = S.trim(data); | |
if (data) { | |
// from json2 | |
if (INVALID_CHARS_REG.test(data.replace(INVALID_ESCAPES_REG, "@") | |
.replace(INVALID_TOKENS_REG, "]") | |
.replace(INVALID_BRACES_REG, ""))) { | |
return ( new Function("return " + data) )(); | |
} | |
} | |
} | |
return S.error("Invalid Json: " + data); | |
}; | |
} | |
// exports for nodejs | |
if (S.UA.nodejs) { | |
S.KISSY = S; | |
module.exports = S; | |
} | |
})(KISSY); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment