/** * @class Array */ Util.applyIf(Array.prototype, { /** * 检查一个对象是否存在于数组中。 * @param {Object} o 待检查的对象 * @param {Number} from (Optional) 查询起始位置的索引值 * @return {Number} 返回对象在数组中的索引值(如果没有找到则返回-1) */ indexOf : function(o, from){ var len = this.length; from = from || 0; from += (from < 0) ? len : 0; for (; from < len; ++from){ if(this[from] === o){ return from; } } return -1; }, /** * 从数组中删除指定对象,如果没有在数组中找到该对象则不做任何操作。 * @param {Object} o 要找到并将其删除的对象 * @return {Array} 返回数组本身 */ remove : function(o){ var index = this.indexOf(o); if(index != -1){ this.splice(index, 1); } return this; }, /** * 合并两个数组,并保持数组中元素的唯一性 * @param {Array} arr 要找到并将其删除的对象 * @return {Array} 返回合并后的数组 */ unique : function (arr) { if (!Util.isArray(arr)) { arr = []; } for (var i = 0; i < arr.length; i++) { if ($.inArray(arr[i], this) == -1) { this.push(arr[i]); } } return this; }, /** * 根据传入参数获取数组中元素,碰到第一个为true的参数时返回与其位置对应的数组元素 * arguments 可变参,每个都是boolean值 * @return 返回取得的元素,默认返回最后一个元素 */ get : function () { var arg = arguments, l = arg.length < this.length ? arg.length : this.length, res; for (var i = 0; i < l; i++) { if (!!arg[i]) { res = this[i]; break; } } return res || this[this.length - 1]; } }); /** * @class String */ Util.applyIf(String, { /** * 对字符串中 ' 和 \ 字符进行转译 * @param {String} string 待转译的字符串 * @return {String} The 转译后的字符串 * @static */ escape : function(string) { return string.replace(/('|\\)/g, "\\$1"); }, /** * 在字符串的左侧填充指定字符。这对于规范编号和日期字符串特别有用。例程: *

var s = String.leftPad('123', 5, '0');
// s现在包含了这样的字符串: '00123'
     * 
* @param {String} string 原始字符串 * @param {Number} size 输出字符串的总长度 * @param {String} char (optional) 用于填充原始字符串的字符(默认为空格" ") * @return {String} 填充后的字符串 * @static */ leftPad : function (val, size, ch) { var result = String(val); if(!ch) { ch = " "; } while (result.length < size) { result = ch + result; } return result; } }); Util.applyIf(String.prototype, { /** * 使字符串能够在两个值之间切换的公共函数。传入的第一个参数将与当前值比较,如果他们相等,第二个传入参数将是返回值,如果他们不等,第一个参数将返回。方法返回新的字符串但是不会修改原来的值。 *

    // 交替排序方向
    sort = sort.toggle('ASC', 'DESC');
    
    // 相当于这样的表达式:
    sort = (sort == 'ASC' ? 'DESC' : 'ASC');
    
* @param {String} value 与当前字符串进行比较的参数。 * @param {String} other 如果第一参数等于当前值,该参数将返回 * @return {String} 新的字符串 */ toggle : function(value, other){ return this == value ? other : value; }, /** * 去除字符串前后的空白字符。例如: *

    var s = '  foo bar  ';
    alert('-' + s + '-');         //提示 "- foo bar -"
    alert('-' + s.trim() + '-');  //提示 "-foo bar-"
    
* @return {String} 去除空白字符后的字符串 */ trim : function(){ var re = /^\s+|\s+$/g; return function(){ return this.replace(re, ""); }; }(), /** * 通过占位符对字符串进行格式化。占位符的键值可以为数值索引,也可以使关键字字符串{0}, {1}, {key} 等。 * 占位符可以设置默认值:{key:value},当占位符没有被指定替代时将被替换为默认值value。 * 占位符可以带有前缀字符串:{prefix=>key},当占位符被匹配时将被替换为"prefix value"的字符串,该格式主要用于CLI命令拼接。 * 替换占位符的参数列表可以采用可选参数形式,参数匹配关键字为由0开始增长的索引值,也可以是JSON对象形式的键值对。例程: *

var cls = 'my-class', text = 'Some text', src = '<div class="{0}">{1}</div>';
var s = src.format(cls, text);
// s现在包含的字符串为: '<div class="my-class">Some text</div>'

var hello1 = "hello {key:world}", hello2 = "hello {0:world}";
var r = hello1.format(); // r现在包含的字符串为: 'hello world'
r = hello1.format({key:"Mary"}); // r现在包含的字符串为: 'hello Mary'
r = hello2.format("Mary"); // r现在包含的字符串为: 'hello Mary'

// 使用前缀拼接命令
var cmd = "flow-rule {id} session-limit {session:8000} action {action:pass} {in-channel=>in} {out-channel=>out}";
var t = cmd.format({id:1, "in":"inside", "out":"outside"});
// t现在包含的字符串为: 'flow-rule 1 session-limit 8000 action pass in-channel inside out-channel outside'
     * 
* @param {String/Object} value1 替换索引值为0占位符的字符串,或者是包含占位符替换信息列表的对象。 * @param {String} value2 Etc... * @return {String} 格式化后的字符串 */ format : function () { var params = {}, result = this.toString(), index = -1; if (arguments.length) { if (Util.isObject(arguments[0]) || Util.isArray(arguments[0])) { params = arguments[0]; } else { for (var i = 0; i < arguments.length; i++) { params[i] = arguments[i]; } } } while ((index = result.indexOf("{")) != -1) { var holder = result.substring(index, result.indexOf("}") + 1); var key = holder.substring(1, holder.indexOf(":") != -1 ? holder.indexOf(":") : holder.length - 1); var prefix = key; key = (key.indexOf("=>") != -1) ? $.trim(key).split("=>")[1] : key; prefix = (prefix.indexOf("=>") != -1) ? $.trim(prefix).split("=>")[0] : ""; var value = holder.indexOf(":") != -1 ? holder.substring(holder.indexOf(":") + 1, holder.length - 1) : ""; if (key.indexOf("(") == -1) { var c = Util.isDefined(params[key]) && params[key] !== ""; if (c) { value = params[key]; } //alert(holder + " " + (c ? prefix + " " : "") + value) result = result.replace(holder, (c ? prefix + " " : "") + value); } else { words = key.substring(key.indexOf("[") + 1, key.indexOf("]")); words = $.trim(words).split(","); var res = ""; if (key.indexOf("has") != -1) { for (var i = 0; i < words.length; i++) { if (Util.isDefined(params[words[i]]) && params[words[i]] !== "") { res = value; break; } } result = result.replace(holder, res); } } } return result; }, /* * format 的缩减与改进版,只支持替换功能,改进字符限制产生的问题,如 {} */ merge : function () { var params = {}, result = this.toString(), index = -1; if (arguments.length) { if (Util.isObject(arguments[0]) || Util.isArray(arguments[0])) { params = arguments[0]; } else { for (var i = 0; i < arguments.length; i++) { params[i] = arguments[i]; } } } var cfg = result.match(/{.*?}/g), dfParam = {}, content = '', attr = '', length = 0; if (!!cfg) { for (i = 0, length = cfg.length; i < length; i++) { content = cfg[i].slice(1, -1); index = content.indexOf(':'); if (index == -1) { attr = content; dfParam[attr] = ''; } else { attr = content.slice(0, index); dfParam[attr] = content.slice(index + 1); } result = result.replace(new RegExp(cfg[i], "g"), params[attr] || dfParam[attr]); } } return result; }, /** * 计算字符串长度,英文、半角字符算1位,中文、全角字符算2位 * @return {int} 返回字符串长度 */ lengthW : function(){ return this.replace(/[^\x00-\xff]/g,'||').length; } }); /** * @class Function * 所有的函数对象都具有以下的方法(任何JavaScript函数)。 */ Util.apply(Function.prototype, { /** * 创建一个顺序执行的函数序列(原始函数 + 传入函数)。函数将返回原始函数返回的结果。传入函数与原始函数的执行参数相同。例程: *

var sayHi = function(name){
    alert('Hi, ' + name);
}

sayHi('Fred'); // 提示"Hi, Fred"

var sayGoodbye = sayHi.createSequence(function(name){
    alert('Bye, ' + name);
});

sayGoodbye('Fred'); // 弹出两次提示
* @param {Function} fcn 被序列化执行的函数 * @param {Object} scope (optional) 传入的序列函数执行的域对象(this所指的对象)。 * 如果没有指定,默认为原始函数执行的域对象或者浏览器的window对象。 * @return {Function} 新函数 */ createSequence : function(fcn, scope){ var method = this; return (typeof fcn != 'function') ? this : function(){ var retval = method.apply(this || window, arguments); fcn.apply(scope || this || window, arguments); return retval; }; }, /** * 创建一个函数的回调同时传入参数arguments[0], arguments[1], arguments[2], ...可在任何函数上直接调用。例如: myFunction.createCallback(arg1, arg2) * 将创建一个函数执行时传入这两个参数。如果需要在函数执行时指定域对象可使用{@link #createDelegate}代替。这个函数返回的回调将永远在window的域下执行。 *

这个函数在你需要指定参数进行回调时候被使用。如果没有需要传入的参数,你能够直接使用函数的引用(e.g., callback: myFn)。 * 但是如果当你需要传入参数(e.g., callback: myFn(arg1, arg2)),这个函数将会在脚本解析到这里立刻执行。例程: *


var sayHi = function(name){
    alert('Hi, ' + name);
}

// 点击按钮提示"Hi, Fred"
new Button({
    text: 'Say Hi',
    handler: sayHi.createCallback('Fred')
});
* @return {Function} 新函数 */ createCallback : function(/*args...*/){ // make args available, in function below var args = arguments, method = this; return function() { return method.apply(window, args); }; }, /** * 创建一个拦截器函数。传入的函数将在原始函数被调用前调用。如果返回false,原始的函数将不被调用。 * 函数的返回将是原始函数执行的返回值。传入的函数执行时的参数与原始函数相同。例程: *

var sayHi = function(name){
    alert('Hi, ' + name);
}

sayHi('Fred'); // 提示"Hi, Fred"

// 创建一个验证输入的新函数不需直接修改原始函数:
var sayHiToFriend = sayHi.createInterceptor(function(name){
    return name == 'Brian';
});

sayHiToFriend('Fred');  // 没提示
sayHiToFriend('Brian'); // 提示"Hi, Brian"
* @param {Function} fcn 在原始函数执行之前拦截执行的函数。 * @param {Object} scope (optional) 传入的拦截函数执行的域对象(this所指的对象)。 * 如果没有指定,默认为原始函数执行的域对象或者浏览器的window对象。 * @return {Function} 新函数 */ createInterceptor : function(fcn, scope){ var method = this; return !Util.isFunction(fcn) ? this : function() { var me = this, args = arguments; fcn.target = me; fcn.method = method; return (fcn.apply(scope || me || window, args) !== false) ? method.apply(me || window, args) : null; }; }, /** * 创建一个设置函数域的函数代理。 * 对任何函数都能直接调用,例如: this.myFunction.createDelegate(this, [arg1, arg2]) * 将创建一个自动将this指针设置为某个对象的回调函数。例程: *

var sayHi = function(name){
    // 注意这里使用了"this.text"。这个函数需要在具有text属性的域对象中执行。在这个例子中"this"指向了下面传入的对象。
    alert('Hi, ' + name + '. You clicked the "' + this.text + '" button.');
}

var btn = new Button({
    text: 'Say Hi'
});

// 这个回调函数将在btn的域下执行。点击该按钮将打印:
// "Hi, Fred. You clicked the "Say Hi" button."
btn.on('click', sayHi.createDelegate(btn, ['Fred']));
* @param {Object} scope (optional) 函数执行的域对象(this指向的对象)。 * 如果省略,将为浏览器的window对象。 * @param {Array} args (optional) 重写调用参数。(默认为函数被调用时传入的参数) * @param {Boolean/Number} appendArgs (optional) 如果为True参数被添加入调用时传入的参数列表, * 如果为数值则表示指定的参数插入位置。 * @return {Function} 新的函数。 */ createDelegate : function(obj, args, appendArgs){ var method = this; return function() { var callArgs = args || arguments; if (appendArgs === true){ callArgs = Array.prototype.slice.call(arguments, 0); callArgs = callArgs.concat(args); }else if (Util.isNumber(appendArgs)){ callArgs = Array.prototype.slice.call(arguments, 0); var applyArgs = [appendArgs, 0].concat(args); Array.prototype.splice.apply(callArgs, applyArgs); } return method.apply(obj || window, callArgs); }; }, /** * 在设定的毫秒数以后执行方法,在可选的函数域中。例程: *

var sayHi = function(name){
    alert('Hi, ' + name);
}

// 立刻执行:
sayHi('Fred');

// 两秒后执行:
sayHi.defer(2000, this, ['Fred']);

// 这种延时执行匿名函数的语法有时候很有用:
(function(){
    alert('Anonymous');
}).defer(100);
* @param {Number} millis 用于设置调用setTimeout的延时毫秒数(如果小等于0将马上执行) * @param {Object} scope (optional) 函数的执行域(this所指代的对象)。 * 如果省略,将为浏览器的window对象。 * @param {Array} args (optional) 重写调用参数。(默认为函数被调用时传入的参数) * @param {Boolean/Number} appendArgs (optional) 如果为True参数被添加入调用时传入的参数列表, * 如果为数值则表示指定的参数插入位置。 * @return {Number} 用于清除计时器的计时器ID */ defer : function(millis, obj, args, appendArgs){ var fn = this.createDelegate(obj, args, appendArgs); if(millis > 0){ return setTimeout(fn, millis); } fn(); return 0; } });