UNPKG

tppl

Version:

js模板引擎,代码由https://github.com/jojoin/tppl 提供,由jover对其进行npm打包

1,508 lines (1,375 loc) 456 kB
/* Copyright 2012, KISSY UI Library v1.20 MIT Licensed build time: Feb 8 17:28 */ /* * a seed where KISSY grows up from , KISS Yeah ! * @author lifesinger@gmail.com,yiminghe@gmail.com */ (function (S, undefined) { /** * @namespace KISSY */ var host = this, meta = { /** * Copies all the properties of s to r. * @param deep {boolean} whether recursive mix if encounter object * @return {Object} the augmented object */ mix:function (r, s, ov, wl, deep) { if (!s || !r) { return r; } if (ov === undefined) { ov = true; } var i, p, len; if (wl && (len = wl.length)) { for (i = 0; i < len; i++) { p = wl[i]; if (p in s) { _mix(p, r, s, ov, deep); } } } else { for (p in s) { _mix(p, r, s, ov, deep); } } return r; } }, _mix = function (p, r, s, ov, deep) { if (ov || !(p in r)) { var target = r[p], src = s[p]; // prevent never-end loop if (target === src) { return; } // 来源是数组和对象,并且要求深度 mix if (deep && src && (S.isArray(src) || S.isPlainObject(src))) { // 目标值为对象或数组,直接 mix // 否则 新建一个和源值类型一样的空数组/对象,递归 mix var clone = target && (S.isArray(target) || S.isPlainObject(target)) ? target : (S.isArray(src) ? [] : {}); r[p] = S.mix(clone, src, ov, undefined, true); } else if (src !== undefined) { r[p] = s[p]; } } }, // If KISSY is already defined, the existing KISSY object will not // be overwritten so that defined namespaces are preserved. seed = (host && host[S]) || {}, guid = 0, EMPTY = ''; // The host of runtime environment. specify by user's seed or <this>, // compatibled for '<this> is null' in unknown engine. host = seed.__HOST || (seed.__HOST = host || {}); // shortcut and meta for seed. // override previous kissy S = host[S] = meta.mix(seed, meta); S.mix(S, { configs:{}, // S.app() with these members. __APP_MEMBERS:['namespace'], __APP_INIT_METHODS:['__init'], /** * The version of the library. * @type {String} */ version:'1.20', buildTime:'20120208172832', /** * 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. * @return {Object} the new merged object */ merge:function () { var o = {}, i, l = arguments.length; for (i = 0; i < l; i++) { S.mix(o, arguments[i]); } return o; }, /** * Applies prototype properties from the supplier to the receiver. * @return {Object} the augmented object */ augment:function (/*r, s1, s2, ..., ov, wl*/) { var args = S.makeArray(arguments), len = args.length - 2, r = args[0], ov = args[len], wl = args[len + 1], i = 1; if (!S.isArray(wl)) { ov = wl; wl = undefined; len++; } if (!S.isBoolean(ov)) { ov = undefined; len++; } for (; i < len; i++) { S.mix(r.prototype, args[i].prototype || args[i], 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 px {Object} prototype properties to add/override * @param {Object} [sx] static properties to add/override * @return r {Object} */ extend:function (r, s, px, sx) { if (!s || !r) { return r; } var create = Object.create ? function (proto, c) { return Object.create(proto, { constructor:{ value:c } }); } : function (proto, c) { function F() { } F.prototype = proto; var o = new F(); o.constructor = c; return o; }, sp = s.prototype, rp; // add prototype chain rp = create(sp, r); r.prototype = S.mix(rp, r.prototype); r.superclass = create(sp, s); // add prototype overrides if (px) { S.mix(rp, px); } // add object overrides if (sx) { S.mix(r, sx); } return r; }, /**************************************************************************************** * The KISSY System Framework * ****************************************************************************************/ /** * Initializes KISSY */ __init:function () { this.Config = this.Config || {}; this.Env = this.Env || {}; // NOTICE: '@DEBUG@' will replace with '' when compressing. // So, if loading source file, debug is on by default. // If loading min version, debug is turned off automatically. this.Config.debug = '@DEBUG@'; }, /** * 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. * <code> * 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 * </code> * @return {Object} A reference to the last namespace object created */ 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; }, /** * create app based on KISSY. * @param name {String} the app name * @param sx {Object} static properties to add/override * <code> * S.app('TB'); * TB.namespace('app'); // returns TB.app * </code> * @return {Object} A reference to the app global object */ app:function (name, sx) { var isStr = S.isString(name), O = isStr ? host[name] || {} : name, i = 0, len = S.__APP_INIT_METHODS.length; S.mix(O, this, true, S.__APP_MEMBERS); for (; i < len; i++) { S[S.__APP_INIT_METHODS[i]].call(O); } S.mix(O, S.isFunction(sx) ? sx() : sx); isStr && (host[name] = O); return O; }, config:function (c) { var configs, cfg, r; for (var p in c) { if (c.hasOwnProperty(p)) { if ((configs = this['configs']) && (cfg = configs[p])) { r = cfg(c[p]); } } } 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) { throw msg; } }, /* * Generate a global unique id. * @param {String} [pre] guid prefix * @return {String} the guid */ guid:function (pre) { return (pre || EMPTY) + guid++; } }); S.__init(); return S; })('KISSY', undefined); /** * @module lang * @author lifesinger@gmail.com,yiminghe@gmail.com * @description this code can run in any ecmascript compliant environment */ (function (S, undefined) { var host = S.__HOST, TRUE = true, FALSE = false, OP = Object.prototype, toString = OP.toString, hasOwnProperty = OP.hasOwnProperty, AP = Array.prototype, indexOf = AP.indexOf, lastIndexOf = AP.lastIndexOf, filter = AP.filter, every = AP.every, some = AP.some, //reduce = AP.reduce, trim = String.prototype.trim, map = AP.map, EMPTY = '', HEX_BASE = 16, CLONE_MARKER = '__~ks_cloned', COMPARE_MARKER = '__~ks_compared', STAMP_MARKER = '__~ks_stamped', RE_TRIM = /^[\s\xa0]+|[\s\xa0]+$/g, encode = encodeURIComponent, decode = decodeURIComponent, SEP = '&', EQ = '=', // [[Class]] -> type pairs class2type = {}, // http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet htmlEntities = { '&amp;':'&', '&gt;':'>', '&lt;':'<', '&#x60;':'`', '&#x2F;':'/', '&quot;':'"', '&#x27;':"'" }, reverseEntities = {}, escapeReg, unEscapeReg, // - # $ ^ * ( ) + [ ] { } | \ , . ? escapeRegExp = /[\-#$\^*()+\[\]{}|\\,.?\s]/g; (function () { for (var k in htmlEntities) { if (htmlEntities.hasOwnProperty(k)) { reverseEntities[htmlEntities[k]] = k; } } })(); 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"); } function isValidParamValue(val) { var t = typeof val; // If the type of val is null, undefined, number, string, boolean, return true. return nullOrUndefined(val) || (t !== 'object' && t !== 'function'); } S.mix(S, { /** * stamp a object by guid * @return guid associated with this object */ stamp:function (o, readOnly, marker) { if (!o) { return o } 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; }, noop:function () { }, /** * Determine the internal JavaScript [[Class]] of an object. */ type:function (o) { return nullOrUndefined(o) ? String(o) : class2type[toString.call(o)] || 'object'; }, isNullOrUndefined:nullOrUndefined, isNull:function (o) { return o === null; }, isUndefined:function (o) { return o === undefined; }, /** * Checks to see if an object is empty. */ 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()" or "new FunctionClass()"). * Ref: http://lifesinger.org/blog/2010/12/thinking-of-isplainobject/ */ isPlainObject:function (o) { /** * note by yiminghe * isPlainObject(node=document.getElementById("xx")) -> false * toString.call(node) : ie678 == '[object Object]',other =='[object HTMLElement]' * 'isPrototypeOf' in node : ie678 === false ,other === true */ return o && toString.call(o) === '[object Object]' && 'isPrototypeOf' in o; }, /** * 两个目标是否内容相同 * * @param a 比较目标1 * @param b 比较目标2 * @param [mismatchKeys] internal use * @param [mismatchValues] internal use */ 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 nullOrUndefined(a) && nullOrUndefined(b); } if (a instanceof Date && b instanceof Date) { return a.getTime() == b.getTime(); } if (S.isString(a) && S.isString(b)) { return (a == b); } if (S.isNumber(a) && S.isNumber(b)) { 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 * @param {Function} filter filter function * @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; }, /** * Removes the whitespace from the beginning and end of a string. */ trim:trim ? function (str) { return nullOrUndefined(str) ? EMPTY : trim.call(str); } : function (str) { return nullOrUndefined(str) ? EMPTY : str.toString().replace(RE_TRIM, EMPTY); }, /** * Substitutes keywords in a string using an object/array. * Removes undefined keywords and ignores escaped keywords. */ substitute:function (str, o, regexp) { if (!S.isString(str) || !S.isPlainObject(o)) { return str; } return str.replace(regexp || /\\?\{([^{}]+)\}/g, function (match, name) { if (match.charAt(0) === '\\') { return match.slice(1); } return (o[name] === undefined) ? EMPTY : o[name]; }); }, /** * 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] */ each:function (object, fn, context) { if (object) { var key, val, i = 0, length = object && object.length, isObj = length === undefined || S.type(object) === 'function'; context = context || host; if (isObj) { for (key in object) { // 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. */ 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. */ 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 uniques 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 */ 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. */ 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. * @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. */ 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; }, // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map 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 = S.isString(arr) ? 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; }, /** * @refer https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/reduce */ 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; }, 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; }, 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; }, /** * it is not same with native bind * @refer https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind */ bind:function (fn, obj) { var slice = [].slice, args = slice.call(arguments, 2), fNOP = function () { }, bound = function () { return fn.apply(this instanceof fNOP ? this : obj, args.concat(slice.call(arguments))); }; fNOP.prototype = fn.prototype; bound.prototype = new fNOP(); return bound; }, /** * Gets current date in milliseconds. * @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/ */ now:Date.now || function () { return +new Date(); }, /** * frequently used in taobao cookie about nick */ fromUnicode:function (str) { return str.replace(/\\u([a-f\d]{4})/ig, function (m, u) { return String.fromCharCode(parseInt(u, HEX_BASE)); }); }, /** * escape string to html * @refer http://yiminghe.javaeye.com/blog/788929 * http://wonko.com/post/html-escaping * @param str {string} text2html show */ escapeHTML:function (str) { return str.replace(getEscapeReg(), function (m) { return reverseEntities[m]; }); }, escapeRegExp:function (str) { return str.replace(escapeRegExp, '\\$&'); }, /** * unescape html to string * @param str {string} html2text */ unEscapeHTML:function (str) { return str.replace(getUnEscapeReg(), function (m, n) { return htmlEntities[m] || String.fromCharCode(+n); }); }, /** * Converts object to a true array. * @param o {object|Array} array like object or array * @return {Array} */ makeArray:function (o) { if (nullOrUndefined(o)) { return []; } if (S.isArray(o)) { return o; } // The strings and functions also have 'length' if (typeof o.length !== 'number' || S.isString(o) || S.isFunction(o)) { return [o]; } var ret = []; for (var i = 0, l = o.length; i < l; i++) { ret[i] = o[i]; } return ret; }, /** * Creates a serialized string of an array or object. * @return {String} * <code> * {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' * </code> */ param:function (o, sep, eq, arr) { if (!S.isPlainObject(o)) { return EMPTY; } sep = sep || SEP; eq = eq || EQ; if (S.isUndefined(arr)) { arr = TRUE; } var buf = [], key, val; for (key in o) { if (o.hasOwnProperty(key)) { val = o[key]; key = encode(key); // val is valid non-array value if (isValidParamValue(val)) { buf.push(key, eq, encode(val + EMPTY), sep); } // val is not empty array else if (S.isArray(val) && val.length) { for (var i = 0, len = val.length; i < len; ++i) { if (isValidParamValue(val[i])) { buf.push(key, (arr ? encode("[]") : EMPTY), eq, encode(val[i] + EMPTY), 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. * <code> * '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: ''} * </code> */ unparam:function (str, sep, eq) { if (typeof str !== 'string' || (str = S.trim(str)).length === 0) { return {}; } sep = sep || SEP; eq = eq || EQ; var ret = {}, pairs = str.split(sep), pair, key, val, i = 0, len = pairs.length; for (; i < len; ++i) { pair = pairs[i].split(eq); key = decode(pair[0]); try { val = decode(pair[1] || EMPTY); } catch (e) { S.log(e + "decodeURIComponent error : " + pair[1], "error"); val = pair[1] || EMPTY; } if (S.endsWith(key, "[]")) { key = key.substring(0, key.length - 2); } if (hasOwnProperty.call(ret, key)) { if (S.isArray(ret[key])) { ret[key].push(val); } else { ret[key] = [ret[key], val]; } } else { ret[key] = val; } } return ret; }, /** * 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 {Number} the number of milliseconds to wait until the fn is executed. * @param periodic {Boolean} if true, executes continuously at supplied interval * until canceled. * @param context {Object} 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 [myarray]. * @return {Object} a timer object. Call the cancel() method on this object to stop * the timer. */ later:function (fn, when, periodic, context, data) { when = when || 0; var m = fn, d = S.makeArray(data), f, r; if (S.isString(fn)) { 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); } } }; }, startsWith:function (str, prefix) { return str.lastIndexOf(prefix, 0) === 0; }, endsWith:function (str, suffix) { var ind = str.length - suffix.length; return ind >= 0 && str.indexOf(suffix, ind) == ind; }, /** * Based on YUI3 * Throttles a call to a method based on the time between calls. * @param {function} fn The function call to throttle. * @param {object} context ontext 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. */ 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 {object} [context] * @param {Number} ms */ 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, FALSE, context || this); } f.stop = function () { if (bufferTimer) { bufferTimer.cancel(); bufferTimer = 0; } }; return f; } }); // for idea ..... auto-hint S.mix(S, { isBoolean:isValidParamValue, isNumber:isValidParamValue, isString:isValidParamValue, isFunction:isValidParamValue, isArray:isValidParamValue, isDate:isValidParamValue, isRegExp:isValidParamValue, isObject:isValidParamValue }); 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; } }); function nullOrUndefined(o) { return S.isNull(o) || S.isUndefined(o); } 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 (input.hasOwnProperty(k)) { 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 (b.hasOwnProperty(property)) { if (!hasKey(a, property) && hasKey(b, property)) { mismatchKeys.push("expected has key '" + property + "', but missing from actual."); } } } for (property in a) { if (a.hasOwnProperty(property)) { if (!hasKey(b, property) && hasKey(a, property)) { mismatchKeys.push("expected missing key '" + property + "', but present in actual."); } } } for (property in b) { if (b.hasOwnProperty(property)) { 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, undefined); /** * setup data structure for kissy loader * @author yiminghe@gmail.com */ (function(S){ if("require" in this) { return; } S.__loader={}; S.__loaderUtils={}; S.__loaderData={}; })(KISSY);/** * map mechanism * @author yiminghe@gmail.com */ (function (S, loader) { if ("require" in this) { return; } /** * modify current module path * @param rules * @example * [ * [/(.+-)min(.js(\?t=\d+)?)$/,"$1$2"], * [/(.+-)min(.js(\?t=\d+)?)$/,function(_,m1,m2){ * return m1+m2; * }] * ] */ S.configs.map = function (rules) { S.Config.mappedRules = (S.Config.mappedRules || []).concat(rules); }; S.mix(loader, { __getMappedPath:function (path) { var __mappedRules = S.Config.mappedRules || []; for (var i = 0; i < __mappedRules.length; i++) { var m, rule = __mappedRules[i]; if (m = path.match(rule[0])) { return path.replace(rule[0], rule[1]); } } return path; } }); })(KISSY, KISSY.__loader);/** * combine mechanism * @author yiminghe@gmail.com */ (function (S, loader) { if ("require" in this) { return; } var combines; /** * compress 'from module' to 'to module' * { * core:['dom','ua','event','node','json','ajax','anim','base','cookie'] * } */ combines = S.configs.combines = function (from, to) { var cs; if (S.isObject(from)) { S.each(from, function (v, k) { S.each(v, function (v2) { combines(v2, k); }); }); return; } cs = S.Config.combines = S.Config.combines || {}; if (to) { cs[from] = to; } else { return cs[from] || from; } }; S.mix(loader, { __getCombinedMod:function (modName) { var cs; cs = S.Config.combines = S.Config.combines || {}; return cs[modName] || modName; } }); })(KISSY, KISSY.__loader);/** * status constants * @author yiminghe@gmail.com */ (function(S, data) { if ("require" in this) { return; } // 脚本(loadQueue)/模块(mod) 公用状态 S.mix(data, { "INIT":0, "LOADING" : 1, "LOADED" : 2, "ERROR" : 3, // 模块特有 "ATTACHED" : 4 }); })(KISSY, KISSY.__loaderData);/** * utils for kissy loader * @author yiminghe@gmail.com */ (function(S, loader, utils) { if ("require" in this) { return; } var ua = navigator.userAgent,doc = document; S.mix(utils, { docHead:function() { return doc.getElementsByTagName('head')[0] || doc.documentElement; }, isWebKit:!!ua.match(/AppleWebKit/), IE : !!ua.match(/MSIE/), isCss:function(url) { return /\.css(?:\?|$)/i.test(url); }, isLinkNode:function(n) { return n.nodeName.toLowerCase() == 'link'; }, /** * resolve relative part of path * x/../y/z -> y/z * x/./y/z -> x/y/z * @param path uri path * @return {string} resolved path * @description similar to path.normalize in nodejs */ normalizePath:function(path) { var paths = path.split("/"), re = [], p; for (var i = 0; i < paths.length; i++) { p = paths[i]; if (p == ".") { } else if (p == "..") { re.pop(); } else { re.push(p); } } return re.join("/"); }, /** * 根据当前模块以及依赖模块的相对路径,得到依赖模块的绝对路径 * @param moduleName 当前模块 * @param depName 依赖模块 * @return {string|Array} 依赖模块的绝对路径 * @description similar to path.resolve in nodejs */ normalDepModuleName:function normalDepModuleName(moduleName, depName) { if (!depName) { return depName; } if (S.isArray(depName)) { for (var i = 0; i < depName.length; i++) { depName[i] = normalDepModuleName(moduleName, depName[i]); } return depName; } if (startsWith(depName, "../") || startsWith(depName, "./")) { var anchor = "",index; // x/y/z -> x/y/ if ((index = moduleName.lastIndexOf("/")) != -1) { anchor = moduleName.substring(0, index + 1); } return normalizePath(anchor + depName); } else if (depName.indexOf("./") != -1 || depName.indexOf("../") != -1) { return normalizePath(depName); } else { return depName; } }, //去除后缀名,要考虑时间戳? removePostfix:function (path) { return path.replace(/(-min)?\.js[^/]*$/i, ""); }, /** * 路径正则化,不能是相对地址 * 相对地址则转换成相对页面的绝对地址 * 用途: * package path 相对地址则相对于当前页面获取绝对地址 */ normalBasePath:function (path) { path = S.trim(path); // path 为空时,不能变成 "/" if (path && path.charAt(path.length - 1) != '/') { path += "/"; } /** * 一定要正则化,防止出现 ../ 等相对路径 * 考虑本地路径 */ if (!path.match(/^(http(s)?)|(file):/i) && !startsWith(path, "/")) { path = loader.__pagePath + path; } return normalizePath(path); }, /** * 相对路径文件名转换为绝对路径 * @param path */ absoluteFilePath:function(path) { path = utils.normalBasePath(path); return path.substring(0, path.length - 1); }, //http://wiki.commonjs.org/wiki/Packages/Mappings/A //如果模块名以 / 结尾,自动加 index indexMapping:function (names) { for (var i = 0; i < names.length; i++) { if (names[i].match(/\/$/)) { names[i] += "index"; } } return names; } }); var startsWith = S.startsWith,normalizePath = utils.normalizePath; })(KISSY, KISSY.__loader, KISSY.__loaderUtils);/** * script/css load across browser * @author yiminghe@gmail.com */ (function(S, utils) { if ("require" in this) { return; } var CSS_POLL_INTERVAL = 30, /** * central poll for link node */ timer = 0, monitors = { /** * node.id:[callback] */ }; function startCssTimer() { if (!timer) { S.log("start css polling"); cssPoll(); } } // single thread is ok function cssPoll() { for (var url in monitors) { var callbacks = monitors[url], node = callbacks.node, loaded = 0; if (utils.isWebKit) { if (node['sheet']) { S.log("webkit loaded : " +