【JavaScript】字符串处理函数集合
var $string = {}, toString, template, parseURL, buildURL, mapQuery, test, contains, trim, clean, camelCase, hyphenate, capitalize, escapeRegExp, toInt, toFloat, toSingleLine, toHtml, toTitle, toQueryPair, toQueryString, hexToRgb, rgbToHex, stripScripts, substitute, replaceAll, byteLength, cutRight, cutByBytes, isNumber, isEmail, encodeHtmlSimple, decodeHtmlSimple, decodeHtmlSimple2, encodeHtmlAttributeSimple, encodeHtmlAttribute, encodeHtml, encodeScript, encodeHrefScript, encodeRegExp, encodeUrl, encodeUriComponent, vaildURL, getCharWidth, cutByWidth; /** * 将任意变量转换为字符串的方法 * @param o * @returns {string} */ toString = function(o){ return (o + ""); }; /** * 多行或单行字符串模板处理 * * @method template * @memberOf string * * @return {String} 返回与数据对象合成后的字符串 * * @example * <script type="text/html" id="user_tmpl"> * <% for ( var i = 0; i < users.length; i++ ) { %> * <li><a href="<%=users[i].url%>"><%=users[i].name%></a></li> * <% } %> * </script> * * Jx().$package(function(J){ * // 用 obj 对象的数据合并到字符串模板中 * template("Hello, {name}!", { * name:"Beauty" * }); * }; */ template = (function(){ var openTag = '<%', closeTag = '%>', retTag = '$return', vars = 'var ', varsInTpl, codeArr = ''.trim ? [retTag + ' = "";', retTag + ' +=', ';', retTag + ';', 'print=function(){' + retTag + '+=[].join.call(arguments,"")},'] : [retTag + ' = [];', retTag + '.push(', ')', retTag + '.join("");', 'print=function(){' + retTag + '.push.apply(arguments)},'], keys = ('break,case,catch,continue,debugger,default,delete,do,else,false,finally,for,function,if' + ',in,instanceof,new,null,return,switch,this,throw,true,try,typeof,var,void,while,with' // Reserved words + ',abstract,boolean,byte,char,class,const,double,enum,export,extends,final,float,goto' + ',implements,import,int,interface,long,native,package,private,protected,public,short' + ',static,super,synchronized,throws,transient,volatile' // ECMA 5 - use strict + ',arguments,let,yield').split(','), keyMap = {}; for (var i = 0, len = keys.length; i < len; i++) { keyMap[keys[i]] = 1; } function _getCompileFn(source) { vars = 'var '; varsInTpl = {}; varsInTpl[retTag] = 1; var openArr = source.split(openTag), tmpCode = ''; for (var i = 0, len = openArr.length; i < len; i++) { var c = openArr[i], cArr = c.split(closeTag); if (cArr.length == 1) { tmpCode += _html(cArr[0]); } else { tmpCode += _js(cArr[0]); tmpCode += cArr[1] ? _html(cArr[1]) : ''; } } var code = vars + codeArr[0] + tmpCode + 'return ' + codeArr[3]; return new Function('$data', code); } function _html(s) { s = s .replace(/('|"|\\)/g, '\\$1') .replace(/\r/g, '\\r') .replace(/\n/g, '\\n'); s = codeArr[1] + '"' + s + '"' + codeArr[2]; return s + '\n'; } function _js(s) { if (/^=/.test(s)) { s = codeArr[1] + s.substring(1).replace(/[\s;]*$/, '') + codeArr[2]; } dealWithVars(s); return s + '\n'; } function dealWithVars(s) { s = s.replace(/\/\*.*?\*\/|'[^']*'|"[^"]*"|\.[\$\w]+/g, ''); var sArr = s.split(/[^\$\w\d]+/); for (var i = 0, len = sArr.length; i < len; i++) { var c = sArr[i]; if (!c || keyMap[c] || /^\d/.test(c)) { continue; } if (!varsInTpl[c]) { if (c === 'print') { vars += codeArr[4]; } else { vars += (c + '=$data.hasOwnProperty("' + c + '")?$data.' + c + ':window.' + c + ','); } varsInTpl[c] = 1; } } } var cache = {}; /** * @param {String} str 模板字符串 * @param {Object} data 要套入的数据对象 */ return function (str, data) { // Figure out if we're getting a template, or if we need to // load the template - and be sure to cache the result. var fn = !/\W/.test(str) ? cache[str] || (cache[str] = _getCompileFn(document.getElementById(str).innerHTML)) : _getCompileFn(str); // Provide some basic currying to the user return data ? fn(data) : fn; }; })(); /** * 分解 URL 为一个对象,成员为:scheme, user, pass, host, port, path, query, fragment * * @method parseURL * @memberOf string * * @param {String} str URL 地址 * @return {Object} 如果解析失败则返回 null */ parseURL = function(str) { var ret = null; if (null !== (ret = parseURL.RE.exec(str))) { var specObj = {}; for (var i = 0, j = parseURL.SPEC.length; i < j ; i ++) { var curSpec = parseURL.SPEC[i]; specObj[curSpec] = ret[i + 1]; } ret = specObj; specObj = null; } return ret; }; /** * 将一个对象(成员为:scheme, user, pass, host, port, path, query, fragment)重新组成为一个字符串 * * @method buildURL * @memberOf string * * @param {Object} obj URl 对象 * @return {String} 如果是可接受的 url 则返回 true */ buildURL = function(obj) { var ret = ''; // prefix & surffix var prefix = {}, surffix = {}; for (var i = 0, j = parseURL.SPEC.length; i < j ; i ++) { var curSpec = parseURL.SPEC[i]; if (!obj[curSpec]) { continue; } switch (curSpec) { case 'scheme': surffix[curSpec] = '://'; break; case 'pass': prefix[curSpec] = ':'; case 'user': prefix['host'] = '@'; break; //case 'host': case 'port': prefix[curSpec] = ':'; break; //case 'path': case 'query': prefix[curSpec] = '?'; break; case 'fragment': prefix[curSpec] = '#'; break; } // rebuild if (curSpec in prefix) { ret += prefix[curSpec]; } if (curSpec in obj) { ret += obj[curSpec]; } if (curSpec in surffix) { ret += surffix[curSpec]; } } prefix = null; surffix = null; obj = null; return ret; }; /** * @ignore */ parseURL.SPEC = ['scheme', 'user', 'pass', 'host', 'port', 'path', 'query', 'fragment']; parseURL.RE = /^([^:]+):\/\/(?:([^:@]+):?([^@]*)@)?(?:([^/?#:]+):?(\d*))([^?#]*)(?:\?([^#]+))?(?:#(.+))?$/; /** * 将 uri 的查询字符串参数映射成对象 * * @method mapQuery * @memberOf string * * @param {String} uri 要映射的 uri * @return {Object} 按照 uri 映射成的对象 * * @example * Jx().$package(function(J){ * var url = "http://web.qq.com/?qq=4765078&style=blue"; * // queryObj 则得到一个{qq:"4765078", style:"blue"}的对象。 * var queryObj = mapQuery(url); * }; */ mapQuery = function(uri){ //window.location.search uri = uri && uri.split('#')[0] || window.location.search; //remove hash var i, key, value, index = uri.indexOf("?"), pieces = uri.substring(index + 1).split("&"), params = {}; if (index === -1) {//如果连?号都没有,直接返回,不再进行处理. az 2011/5/11 return params; } for (i = 0; i < pieces.length; i++) { try { index = pieces[i].indexOf("="); key = pieces[i].substring(0, index); value = pieces[i].substring(index + 1); if (!(params[key] = unescape(value))) { throw new Error("uri has wrong query string when run mapQuery."); } } catch (e) { //J.out("错误:[" + e.name + "] "+e.message+", " + e.fileName+", 行号:"+e.lineNumber+"; stack:"+typeof e.stack, 2); } } return params; }; /** * * test的方法 * * @memberOf string * * @param {String|RegExp} regex 正则表达式,或者正则表达式的字符串 * @param {String} params 正则的参数 * @return {Boolean} 返回结果 */ test = function(string, regex, params){ return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(string); }; /** * 判断是否含有指定的字符串 * * @memberOf string * @name contains * @function * @param {String} string 是否含有的字符串 * @param {String} separator 分隔符,可选 * @return {Boolean} 如果含有,返回 true,否则返回 false */ contains = function(string1, string2, separator){ return (separator) ? (separator + string1 + separator).indexOf(separator + string2 + separator) > -1 : string1.indexOf(string2) > -1; }; /** * 清除字符串开头和结尾的空格 * * @memberOf string * * @return {String} 返回清除后的字符串 */ trim = function(string){ return String(string).replace(/^\s+|\s+$/g, ''); }; /** * 清除字符串开头和结尾的空格,并把字符串之间的多个空格转换为一个空格 * * @memberOf string * * @return {String} 返回清除后的字符串 */ clean = function(string){ return trim(string.replace(/\s+/g, ' ')); }; /** * 将“-”连接的字符串转换成驼峰式写法 * * @memberOf string * * @return {String} 返回转换后的字符串 */ camelCase = function(string){ return string.replace(/-\D/g, function(match){ return match.charAt(1).toUpperCase(); }); }; /** * 将驼峰式写法字符串转换成“-”连接的 * * @memberOf string * * @return {String} 返回转换后的字符串 */ hyphenate = function(string){ return string.replace(/[A-Z]/g, function(match){ return ('-' + match.charAt(0).toLowerCase()); }); }; /** * 将字符串转换成全大写字母 * * @memberOf string * * @return {String} 返回转换后的字符串 */ capitalize = function(string){ return string.replace(/\b[a-z]/g, function(match){ return match.toUpperCase(); }); }; /** * 转换 RegExp 正则表达式 * * @memberOf string * * @return {String} 返回转换后的字符串 */ escapeRegExp = function(string){ return string.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); }; /** * 将字符串转换成整数 * * @memberOf string * * @return {Number} 返回转换后的整数 */ toInt = function(string, base){ return parseInt(string, base || 10); }; /** * 将字符串转换成浮点数 * * @memberOf string * @param {String} string 要转换的字符串 * @return {Number} 返回转换后的浮点数 */ toFloat = function(string){ return parseFloat(string); }; /** * 将带换行符的字符串转换成无换行符的字符串 * * @memberOf string * @param {String} str 要转换的字符串 * @return {String} 返回转换后的字符串 */ toSingleLine = function(str){ return String(str).replace(/\r/gi,"") .replace(/\n/gi,""); }; /** * 将字符串转换成html源码 * * @memberOf string * @param {String} str 要转换的字符串 * @return {String} 返回转换后的html代码字符串 */ toHtml = function(str){ return String(str).replace(/&/gi,"&") .replace(/\\/gi,"\") .replace(/\'/gi,"'") .replace(/\"/gi,""") .replace (/</gi,"<") .replace(/>/gi,">") .replace(/ /gi," ") .replace(/\r\n/g,"<br />") .replace(/\n\r/g,"<br />") .replace(/\n/g,"<br />") .replace(/\r/g,"<br />"); }; /** * 将字符串转换成用于title的字符串 * * @memberOf string * @param {String} str 要转换的字符串 * @return {Number} 返回转换后的in title字符串 */ toTitle = function(str){ return String(str).replace(/\\/gi,"\\") .replace(/\'/gi,"\'") .replace(/\"/gi,"\'"); }; /** * 将颜色 Hex 写法转换成 RGB 写法 * * @memberOf string * @return {String} 返回转换后的字符串 * @author rewrite by dmyang */ hexToRgb = function(string){ var hex = string.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); var _convert = function(array) { var length = array.length; if (length !== 3) return null; for(var i=0, value; i<length; i++) { value = array[i]; if(value.length === 1) value += value; array[i] = parseInt(value, 16); } return 'rgb(' + array + ')'; }; return (hex) ? _convert(hex.slice(1)) : null; }; /** * 将颜色 RGB 写法转换成 Hex 写法 * * @memberOf string * @return {String} 返回转换后的字符串 * @author rewrite by dmyang */ rgbToHex = function(string){ var r = string.match(/\d{1,3}/g); return (r) ? '#' + ((1 << 24) + ((r[0] << 0) << 16) + ((r[1] << 0) << 8) + (r[2] << 0)).toString(16).slice(1) : null; }; /** * 脱去script标签 * * @memberOf string * @return {String} 返回转换后的字符串 */ stripScripts = function(string, option){ var scripts = ''; var text = string.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){ scripts += arguments[1] + '\n'; return ''; }); if (option === true){ $exec(scripts); }else if($type(option) == 'function'){ option(scripts, text); } return text; }; /** * 。。。。 * * @memberOf string * @param {Object} obj 要转换成查询字符串的对象 * @return {String} 返回转换后的查询字符串 */ toQueryPair = function(key, value) { return encodeURIComponent(String(key)) + "=" + encodeURIComponent(String(value)); }; /** * 。。。。 * * @memberOf string * @param {Object} obj 要转换成查询字符串的对象 * @return {String} 返回转换后的查询字符串 */ toQueryString = function(obj){ var result=[]; for(var key in obj){ result.push(toQueryPair(key, obj[key])); } return result.join("&"); }; /** * 。。。。 * * @memberOf string * @return {String} 返回转换后的字符串 */ substitute = function(string, object, regexp){ return string.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){ if (match.charAt(0) == '\\') return match.slice(1); return (object[name] != undefined) ? object[name] : ''; }); }; /** * 全局替换指定的字符串 * * @memberOf string * @return {String} 返回替换后的字符串 */ replaceAll = function(string, reallyDo, replaceWith, ignoreCase) { if (!RegExp.prototype.isPrototypeOf(reallyDo)) { return string.replace(new RegExp(reallyDo, (ignoreCase ? "gi": "g")), replaceWith); } else { return string.replace(reallyDo, replaceWith); } }; /** * 计算字符串的字节长度 * * @memberOf string * @param {String} string * @param {Number} n 指定一个中文的字节数, 默认为2 * @return {Number} 返回自己长度 */ byteLength = function(string,n){ n= n||2; return string.replace(/[^\x00-\xff]/g,({2:"aa",3:"aaa"})[n]).length; }; /** * 按字符按给定长度裁剪给定字符串 * @memberOf string * @param {String} string * @param {Number} n * @return {String} */ cutRight = function(string, n){ return string.substring(0, (string.length - n)); }; /** * 按字节按给定长度裁剪给定字符串 * @memberOf string * @param {String} string * @param {Number} n * @return {String} */ cutByBytes = function(string,n) { var s= string; while(byteLength(s)>n) { s= cutRight(s,1); } return s; }; /** * 判断给定字符串是否是数字 * @memberOf string * @name isNumber * @function * * @param {String} string * @param {Number} n * @return {String} */ isNumber = function(string){ if (string.search(/^\d+$/) !== -1){ return true; } else{ return false; } }; /** * 判断一个字符串是否是邮箱格式 * @memberOf string * @param {String} emailStr * @return {Boolean} */ isEmail = function(emailStr){ if (emailStr.search(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/) !== -1){ return true; } else{ return false; } }; /* JS安全API v1.1 Created By Web Application Security Group of TSC UpDate: 2007-12-08 */ /** * html正文编码, 对需要出现在HTML正文里(除了HTML属性外)的不信任输入进行编码 * @memberOf string * @param {String} sStr * @return {String} */ encodeHtmlSimple = function(sStr){ sStr = sStr.replace(/&/g,"&"); sStr = sStr.replace(/>/g,">"); sStr = sStr.replace(/</g,"<"); sStr = sStr.replace(/"/g,"""); sStr = sStr.replace(/'/g,"'"); return sStr; }; /** * html正文解码, 对HtmlEncode函数的结果进行解码 * @memberOf string * @param {String} sStr * @return {String} */ decodeHtmlSimple = function(sStr){ sStr = sStr.replace(/&/g,"&"); sStr = sStr.replace(/>/g,">"); sStr = sStr.replace(/</g,"<"); sStr = sStr.replace(/"/g,'"'); sStr = sStr.replace(/'/g,"'"); return sStr; }; decodeHtmlSimple2 = function(sStr){ sStr = sStr.replace(/&/g,"&"); sStr = sStr.replace(/>/g,">"); sStr = sStr.replace(/</g,"<"); sStr = sStr.replace(/\\\\"/g,'"'); sStr = sStr.replace(/\\\\'/g,"'"); return sStr; }; /** * html属性编码:对需要出现在HTML属性里的不信任输入进行编码 注意: (1)该函数不适用于属性为一个URL地址的编码.这些标记包括:a/img/frame/iframe/script/xml/embed/object... 属性包括:href/src/lowsrc/dynsrc/background/... (2)该函数不适用于属性名为 style="[Un-trusted input]" 的编码 * @memberOf string * @param {String} sStr * @return {String} */ encodeHtmlAttributeSimple = function(sStr){ sStr = sStr.replace(/&/g,"&"); sStr = sStr.replace(/>/g,">"); sStr = sStr.replace(/</g,"<"); sStr = sStr.replace(/"/g,"""); sStr = sStr.replace(/'/g,"'"); sStr = sStr.replace(/=/g,"="); sStr = sStr.replace(/`/g,"`"); return sStr; }; /** * 用做过滤直接放到HTML里的 * @memberOf string * @param {String} sStr * @return {String} */ encodeHtml = function(sStr) { return sStr.replace(/[&'"<>\/\\\-\x00-\x09\x0b-\x0c\x1f\x80-\xff]/g, function(r){ return "&#"+r.charCodeAt(0)+";"; }).replace(/ /g, " ").replace(/\r\n/g, "<br />").replace(/\n/g, "<br />").replace(/\r/g, "<br />"); }; /** * 用做过滤HTML标签里面的东东 比如这个例子里的<input value="XXXX"> XXXX就是要过滤的 * @memberOf string * @param {String} sStr * @return {String} */ encodeHtmlAttribute = function(sStr) { return sStr.replace(/[&'"<>\/\\\-\x00-\x1f\x80-\xff]/g, function(r){ return "&#"+r.charCodeAt(0)+";"; }); }; /** * 用做过滤直接放到HTML里js中的 * @memberOf string * @param {String} sStr * @return {String} */ encodeScript = function(sStr) { sStr+="";//确保为String return sStr.replace(/[\\"']/g, function(r){ return "\\"+r; }).replace(/%/g, "\\x25").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\x01/g, "\\x01"); }; /** * 用做过滤直接放到<a href="javascript:XXXX">中的 * @memberOf string * @param {String} sStr * @return {String} */ encodeHrefScript = function(sStr) { return encodeHtml(encodeUrl(escScript(sStr))); }; /** * 用做过滤直接放到正则表达式中的 * @memberOf string * @param {String} sStr * @return {String} */ encodeRegExp = function(sStr) { return sStr.replace(/[\\\^\$\*\+\?\{\}\.\(\)\[\]]/g, function(a,b){ return "\\"+a; }); }; /** * 用做过滤直接URL参数里的 比如 http://show8.qq.com/abc_cgi?a=XXX XXX就是要过滤的 * @memberOf string * @param {String} sStr * @return {String} */ encodeUrl = function(sStr) { return escape(sStr).replace(/\+/g, "%2B"); }; /** 对需要出现在一个URI的一部分的不信任输入进行编码 例如: <a href="http://search.msn.com/results.aspx?q1=[Un-trusted-input]& q2=[Un-trusted-input]">Click Here!</a> 以下字符将会被编码: 除[a-zA-Z0-9.-_]以外的字符都会被替换成URL编码 * * @memberOf string * @param {String} sStr * @return {String} */ encodeUriComponent = function(sStr){ sStr = encodeURIComponent(sStr); sStr = sStr.replace(/~/g,"%7E"); sStr = sStr.replace(/!/g,"%21"); sStr = sStr.replace(/\*/g,"%2A"); sStr = sStr.replace(/\(/g,"%28"); sStr = sStr.replace(/\)/g,"%29"); sStr = sStr.replace(/'/g,"%27"); sStr = sStr.replace(/\?/g,"%3F"); sStr = sStr.replace(/;/g,"%3B"); return sStr; }; /** * 验证给定字符串是否是url, 如果是url 则返回正常的url * * @memberOf string * @param {String} url * @return {String} */ vaildURL = function(url){ url=encodeURI(url).replace(/(^\s*)|(\s*$)/g, ''), protocolReg=/(^[a-zA-Z0-9]+[^.]):/, domainReg=/^[\S.]+\.[\S.]+$/, domainendReg=/[\w.]+\/(\S*)/, jsReg=/;$/, jpReg=/^[\s*]*javascript[\s*]*:/; if((!protocolReg.test(url)) && (!domainReg.test(url))){ url=""; }else{ if(!protocolReg.test(url)){ url="http://"+url; } if(!domainendReg.test(url)){ url=url+"/"; } //如果是js为协议就清空 if(jpReg.test(url)){ url=""; } } return url; }; /** * 获取字符实际宽度 * @memberOf string * @param {String} str 需要计算的字符串 * @param {Number} fontSize 字体大小,可以不填 * @return {Number} */ getCharWidth = function(str,fontSize) { var d= document.createElement("div"); d.style.visibility= "hidden"; d.style.width= "auto"; if(fontSize) { d.style.fontSize= fontSize + "px"; } d.style.position= "absolute"; d.innerHTML= encodeHtmlSimple(str); document.body.appendChild(d); var width= d.offsetWidth; document.body.removeChild(d); return width; }; /** * 按给定宽度裁剪字符串 * @memberOf string * @param {String} str * @param {Number} fontsize 字体大小 * @param {Number} width 限定的宽度 * @return {Number} */ cutByWidth = function(str,fontsize,width) { for(var i=str.length;i>=0;--i) { str=str.substring(0, i); if(getCharWidth(str, fontsize)<width) { return str; } } return ''; }; $string.cutByWidth = cutByWidth; $string.toString = toString; $string.template = template; $string.parseURL = parseURL; $string.buildURL = buildURL; $string.mapQuery = mapQuery; $string.test = test; $string.contains = contains; $string.trim = trim; $string.clean = clean; $string.camelCase = camelCase; $string.hyphenate = hyphenate; $string.capitalize = capitalize; $string.escapeRegExp = escapeRegExp; $string.toInt = toInt; $string.toFloat = toFloat; $string.toSingleLine = toSingleLine; $string.toHtml = toHtml; $string.toTitle = toTitle; $string.toQueryPair = toQueryPair; $string.toQueryString = toQueryString; $string.hexToRgb = hexToRgb; $string.rgbToHex = rgbToHex; $string.stripScripts = stripScripts; $string.substitute = substitute; $string.replaceAll = replaceAll; $string.byteLength = byteLength; $string.cutRight = cutRight; $string.isNumber = isNumber; $string.isEmail = isEmail; $string.cutByBytes = cutByBytes; //html正文编码:对需要出现在HTML正文里(除了HTML属性外)的不信任输入进行编码 $string.encodeHtmlSimple = encodeHtmlSimple; //html正文解码:对HtmlEncode函数的结果进行解码 $string.decodeHtmlSimple = decodeHtmlSimple; $string.decodeHtmlSimple2 = decodeHtmlSimple2; /* html属性编码:对需要出现在HTML属性里的不信任输入进行编码 注意: (1)该函数不适用于属性为一个URL地址的编码.这些标记包括:a/img/frame/iframe/script/xml/embed/object... 属性包括:href/src/lowsrc/dynsrc/background/... (2)该函数不适用于属性名为 style="[Un-trusted input]" 的编码 */ $string.encodeHtmlAttributeSimple = encodeHtmlAttributeSimple; //用做过滤HTML标签里面的东东 比如这个例子里的<input value="XXXX"> XXXX就是要过滤的 $string.encodeHtmlAttribute = encodeHtmlAttribute; //用做过滤直接放到HTML里的 $string.encodeHtml = encodeHtml; //用做过滤直接放到HTML里js中的 $string.encodeScript = encodeScript; //用做过滤直接放到<a href="javascript:XXXX">中的 $string.encodeHrefScript = encodeHrefScript; //用做过滤直接放到正则表达式中的 $string.encodeRegExp = encodeRegExp; //用做过滤直接URL参数里的 比如 http://show8.qq.com/abc_cgi?a=XXX XXX就是要过滤的 $string.encodeUrl = encodeUrl; /* 对需要出现在一个URI的一部分的不信任输入进行编码 例如: <a href="http://search.msn.com/results.aspx?q1=[Un-trusted-input]& q2=[Un-trusted-input]">Click Here!</a> 以下字符将会被编码: 除[a-zA-Z0-9.-_]以外的字符都会被替换成URL编码 */ $string.encodeUriComponent = encodeUriComponent; $string.vaildURL = vaildURL; $string.getCharWidth = getCharWidth; /** * underscore.string.js */ var underscore_string=(function(){ // Defining helper functions. var nativeTrim = String.prototype.trim; var nativeTrimRight = String.prototype.trimRight; var nativeTrimLeft = String.prototype.trimLeft; var parseNumber = function (source) { return source * 1 || 0; }; var strRepeat = function (str, qty) { if (qty < 1) return ''; var result = ''; while (qty > 0) { if (qty & 1) result += str; qty >>= 1, str += str; } return result; }; var slice = [].slice; var defaultToWhiteSpace = function (characters) { if (characters == null) return '\\s'; else if (characters.source) return characters.source; else return '[' + _s.escapeRegExp(characters) + ']'; }; var escapeChars = { lt: '<', gt: '>', quot: '"', apos: "'", amp: '&' }; var reversedEscapeChars = {}; for (var key in escapeChars) { reversedEscapeChars[escapeChars[key]] = key; } // sprintf() for JavaScript 0.7-beta1 // http://www.diveintojavascript.com/projects/javascript-sprintf // // Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com> // All rights reserved. var sprintf = (function () { function get_type(variable) { return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase(); } var str_repeat = strRepeat; var str_format = function () { if (!str_format.cache.hasOwnProperty(arguments[0])) { str_format.cache[arguments[0]] = str_format.parse(arguments[0]); } return str_format.format.call(null, str_format.cache[arguments[0]], arguments); }; str_format.format = function (parse_tree, argv) { var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length; for (i = 0; i < tree_length; i++) { node_type = get_type(parse_tree[i]); if (node_type === 'string') { output.push(parse_tree[i]); } else if (node_type === 'array') { match = parse_tree[i]; // convenience purposes only if (match[2]) { // keyword argument arg = argv[cursor]; for (k = 0; k < match[2].length; k++) { if (!arg.hasOwnProperty(match[2][k])) { throw new Error(sprintf('[_.sprintf] property "%s" does not exist', match[2][k])); } arg = arg[match[2][k]]; } } else if (match[1]) { // positional argument (explicit) arg = argv[match[1]]; } else { // positional argument (implicit) arg = argv[cursor++]; } if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) { throw new Error(sprintf('[_.sprintf] expecting number but found %s', get_type(arg))); } switch (match[8]) { case 'b': arg = arg.toString(2); break; case 'c': arg = String.fromCharCode(arg); break; case 'd': arg = parseInt(arg, 10); break; case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break; case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break; case 'o': arg = arg.toString(8); break; case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break; case 'u': arg = Math.abs(arg); break; case 'x': arg = arg.toString(16); break; case 'X': arg = arg.toString(16).toUpperCase(); break; } arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+' + arg : arg); pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' '; pad_length = match[6] - String(arg).length; pad = match[6] ? str_repeat(pad_character, pad_length) : ''; output.push(match[5] ? arg + pad : pad + arg); } } return output.join(''); }; str_format.cache = {}; str_format.parse = function (fmt) { var _fmt = fmt, match = [], parse_tree = [], arg_names = 0; while (_fmt) { if ((match = /^[^\x25]+/.exec(_fmt)) !== null) { parse_tree.push(match[0]); } else if ((match = /^\x25{2}/.exec(_fmt)) !== null) { parse_tree.push('%'); } else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) { if (match[2]) { arg_names |= 1; var field_list = [], replacement_field = match[2], field_match = []; if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) { field_list.push(field_match[1]); while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') { if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) { field_list.push(field_match[1]); } else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) { field_list.push(field_match[1]); } else { throw new Error('[_.sprintf] huh?'); } } } else { throw new Error('[_.sprintf] huh?'); } match[2] = field_list; } else { arg_names |= 2; } if (arg_names === 3) { throw new Error('[_.sprintf] mixing positional and named placeholders is not (yet) supported'); } parse_tree.push(match); } else { throw new Error('[_.sprintf] huh?'); } _fmt = _fmt.substring(match[0].length); } return parse_tree; }; return str_format; })(); // Defining underscore.string var _s = { isBlank: function (str) { if (str == null) str = ''; return (/^\s*$/).test(str); }, stripTags: function (str) { if (str == null) return ''; return String(str).replace(/<\/?[^>]+>/g, ''); }, capitalize: function (str) { str = str == null ? '' : String(str); return str.charAt(0).toUpperCase() + str.slice(1); }, chop: function (str, step) { if (str == null) return []; str = String(str); step = ~~step; return step > 0 ? str.match(new RegExp('.{1,' + step + '}', 'g')) : [str]; }, clean: function (str) { return _s.strip(str).replace(/\s+/g, ' '); }, count: function (str, substr) { if (str == null || substr == null) return 0; return String(str).split(substr).length - 1; }, chars: function (str) { if (str == null) return []; return String(str).split(''); }, swapCase: function (str) { if (str == null) return ''; return String(str).replace(/\S/g, function (c) { return c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase(); }); }, escapeHTML: function (str) { if (str == null) return ''; return String(str).replace(/[&<>"']/g, function (m) { return '&' + reversedEscapeChars[m] + ';'; }); }, unescapeHTML: function (str) { if (str == null) return ''; return String(str).replace(/\&([^;]+);/g, function (entity, entityCode) { var match; if (entityCode in escapeChars) { return escapeChars[entityCode]; } else if (match = entityCode.match(/^#x([\da-fA-F]+)$/)) { return String.fromCharCode(parseInt(match[1], 16)); } else if (match = entityCode.match(/^#(\d+)$/)) { return String.fromCharCode(~~match[1]); } else { return entity; } }); }, escapeRegExp: function (str) { if (str == null) return ''; return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); }, splice: function (str, i, howmany, substr) { var arr = _s.chars(str); arr.splice(~~i, ~~howmany, substr); return arr.join(''); }, insert: function (str, i, substr) { return _s.splice(str, i, 0, substr); }, include: function (str, needle) { if (needle === '') return true; if (str == null) return false; return String(str).indexOf(needle) !== -1; }, join: function () { var args = slice.call(arguments), separator = args.shift(); if (separator == null) separator = ''; return args.join(separator); }, lines: function (str) { if (str == null) return []; return String(str).split("\n"); }, reverse: function (str) { return _s.chars(str).reverse().join(''); }, startsWith: function (str, starts) { if (starts === '') return true; if (str == null || starts == null) return false; str = String(str); starts = String(starts); return str.length >= starts.length && str.slice(0, starts.length) === starts; }, endsWith: function (str, ends) { if (ends === '') return true; if (str == null || ends == null) return false; str = String(str); ends = String(ends); return str.length >= ends.length && str.slice(str.length - ends.length) === ends; }, succ: function (str) { if (str == null) return ''; str = String(str); return str.slice(0, -1) + String.fromCharCode(str.charCodeAt(str.length - 1) + 1); }, titleize: function (str) { if (str == null) return ''; return String(str).replace(/(?:^|\s)\S/g, function (c) { return c.toUpperCase(); }); }, camelize: function (str) { return _s.trim(str).replace(/[-_\s]+(.)?/g, function (match, c) { return c.toUpperCase(); }); }, underscored: function (str) { return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase(); }, dasherize: function (str) { return _s.trim(str).replace(/([A-Z])/g, '-$1').replace(/[-_\s]+/g, '-').toLowerCase(); }, classify: function (str) { return _s.titleize(String(str).replace(/_/g, ' ')).replace(/\s/g, ''); }, humanize: function (str) { return _s.capitalize(_s.underscored(str).replace(/_id$/, '').replace(/_/g, ' ')); }, trim: function (str, characters) { if (str == null) return ''; if (!characters && nativeTrim) return nativeTrim.call(str); characters = defaultToWhiteSpace(characters); return String(str).replace(new RegExp('\^' + characters + '+|' + characters + '+$', 'g'), ''); }, ltrim: function (str, characters) { if (str == null) return ''; if (!characters && nativeTrimLeft) return nativeTrimLeft.call(str); characters = defaultToWhiteSpace(characters); return String(str).replace(new RegExp('^' + characters + '+'), ''); }, rtrim: function (str, characters) { if (str == null) return ''; if (!characters && nativeTrimRight) return nativeTrimRight.call(str); characters = defaultToWhiteSpace(characters); return String(str).replace(new RegExp(characters + '+$'), ''); }, truncate: function (str, length, truncateStr) { if (str == null) return ''; str = String(str); truncateStr = truncateStr || '...'; length = ~~length; return str.length > length ? str.slice(0, length) + truncateStr : str; }, /** * _s.prune: a more elegant version of truncate * prune extra chars, never leaving a half-chopped word. * @author github.com/rwz */ prune: function (str, length, pruneStr) { if (str == null) return ''; str = String(str); length = ~~length; pruneStr = pruneStr != null ? String(pruneStr) : '...'; if (str.length <= length) return str; var tmpl = function (c) { return c.toUpperCase() !== c.toLowerCase() ? 'A' : ' '; }, template = str.slice(0, length + 1).replace(/.(?=\W*\w*$)/g, tmpl); // 'Hello, world' -> 'HellAA AAAAA' if (template.slice(template.length - 2).match(/\w\w/)) template = template.replace(/\s*\S+$/, ''); else template = _s.rtrim(template.slice(0, template.length - 1)); return (template + pruneStr).length > str.length ? str : str.slice(0, template.length) + pruneStr; }, words: function (str, delimiter) { if (_s.isBlank(str)) return []; return _s.trim(str, delimiter).split(delimiter || /\s+/); }, pad: function (str, length, padStr, type) { str = str == null ? '' : String(str); length = ~~length; var padlen = 0; if (!padStr) padStr = ' '; else if (padStr.length > 1) padStr = padStr.charAt(0); switch (type) { case 'right': padlen = length - str.length; return str + strRepeat(padStr, padlen); case 'both': padlen = length - str.length; return strRepeat(padStr, Math.ceil(padlen / 2)) + str + strRepeat(padStr, Math.floor(padlen / 2)); default: // 'left' padlen = length - str.length; return strRepeat(padStr, padlen) + str; } }, lpad: function (str, length, padStr) { return _s.pad(str, length, padStr); }, rpad: function (str, length, padStr) { return _s.pad(str, length, padStr, 'right'); }, lrpad: function (str, length, padStr) { return _s.pad(str, length, padStr, 'both'); }, sprintf: sprintf, vsprintf: function (fmt, argv) { argv.unshift(fmt); return sprintf.apply(null, argv); }, toNumber: function (str, decimals) { if (str == null || str == '') return 0; str = String(str); var num = parseNumber(parseNumber(str).toFixed(~~decimals)); return num === 0 && !str.match(/^0+$/) ? Number.NaN : num; }, numberFormat: function (number, dec, dsep, tsep) { if (isNaN(number) || number == null) return ''; number = number.toFixed(~~dec); tsep = tsep || ','; var parts = number.split('.'), fnums = parts[0], decimals = parts[1] ? (dsep || '.') + parts[1] : ''; return fnums.replace(/(\d)(?=(?:\d{3})+$)/g, '$1' + tsep) + decimals; }, strRight: function (str, sep) { if (str == null) return ''; str = String(str); sep = sep != null ? String(sep) : sep; var pos = !sep ? -1 : str.indexOf(sep); return ~pos ? str.slice(pos + sep.length, str.length) : str; }, strRightBack: function (str, sep) { if (str == null) return ''; str = String(str); sep = sep != null ? String(sep) : sep; var pos = !sep ? -1 : str.lastIndexOf(sep); return ~pos ? str.slice(pos + sep.length, str.length) : str; }, strLeft: function (str, sep) { if (str == null) return ''; str = String(str); sep = sep != null ? String(sep) : sep; var pos = !sep ? -1 : str.indexOf(sep); return ~pos ? str.slice(0, pos) : str; }, strLeftBack: function (str, sep) { if (str == null) return ''; str += ''; sep = sep != null ? '' + sep : sep; var pos = str.lastIndexOf(sep); return ~pos ? str.slice(0, pos) : str; }, toSentence: function (array, separator, lastSeparator, serial) { separator = separator || ', ' lastSeparator = lastSeparator || ' and ' var a = array.slice(), lastMember = a.pop(); if (array.length > 2 && serial) lastSeparator = _s.rtrim(separator) + lastSeparator; return a.length ? a.join(separator) + lastSeparator + lastMember : lastMember; }, toSentenceSerial: function () { var args = slice.call(arguments); args[3] = true; return _s.toSentence.apply(_s, args); }, slugify: function (str) { if (str == null) return ''; var from = "ąàáäâãåæćęèéëêìíïîłńòóöôõøùúüûñçżź", to = "aaaaaaaaceeeeeiiiilnoooooouuuunczz", regex = new RegExp(defaultToWhiteSpace(from), 'g'); str = String(str).toLowerCase().replace(regex, function (c) { var index = from.indexOf(c); return to.charAt(index) || '-'; }); return _s.dasherize(str.replace(/[^\w\s-]/g, '')); }, surround: function (str, wrapper) { return [wrapper, str, wrapper].join(''); }, quote: function (str) { return _s.surround(str, '"'); }, exports: function () { var result = {}; for (var prop in this) { if (!this.hasOwnProperty(prop) || prop.match(/^(?:include|contains|reverse)$/)) continue; result[prop] = this[prop]; } return result; }, repeat: function (str, qty, separator) { if (str == null) return ''; qty = ~~qty; // using faster implementation if separator is not needed; if (separator == null) return strRepeat(String(str), qty); // this one is about 300x slower in Google Chrome for (var repeat = []; qty > 0; repeat[--qty] = str) { } return repeat.join(separator); }, levenshtein: function (str1, str2) { if (str1 == null && str2 == null) return 0; if (str1 == null) return String(str2).length; if (str2 == null) return String(str1).length; str1 = String(str1); str2 = String(str2); var current = [], prev, value; for (var i = 0; i <= str2.length; i++) for (var j = 0; j <= str1.length; j++) { if (i && j) if (str1.charAt(j - 1) === str2.charAt(i - 1)) value = prev; else value = Math.min(current[j], current[j - 1], prev) + 1; else value = i + j; prev = current[j]; current[j] = value; } return current.pop(); } }; // Aliases _s.strip = _s.trim; _s.lstrip = _s.ltrim; _s.rstrip = _s.rtrim; _s.center = _s.lrpad; _s.rjust = _s.lpad; _s.ljust = _s.rpad; _s.contains = _s.include; return _s; })(); _.extend($string,underscore_string);