/** * @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;
}
});