/** * @ignore * escape of lang * @author yiminghe@gmail.com * */ (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 = '=', logger= S.getLogger('s/lang'), 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) { logger.error('decodeURIComponent error : ' + val); logger.error(e); } 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);