ydr-util
Version:
587 lines (499 loc) • 14 kB
JavaScript
/*!
* 数据遍历
* @author ydr.me
* 2014-09-14 17:26
*/
;
var qs = require('querystring');
var path = require('path');
var typeis = require('./typeis.js');
var crypto = require('./crypto.js');
//var request = require('./request.js');
var udf;
var canListTypeArr = 'array object nodelist htmlcollection'.split(' ');
var REG_STRING_FIX = /[.*+?^=!:${}()|[\]/\\]/g;
var REG_PATH = path.sep === '/' ? /\\/ : /\//g;
var REG_URL = /\\/g;
var REG_NOT_UTF16_SINGLE = /[^\x00-\xff]{2}/g;
var REG_BEGIN_0 = /^0+/;
/**
* 格式化数字,如果是非数字则返回默认值
* @param {*} obj 待格式化对象
* @param {*} [dft] 非数字时的默认值
* @returns {*}
*/
exports.parseInt = function (obj, dft) {
obj = parseInt(obj, 10);
return isNaN(obj) ? dft : obj;
};
/**
* 格式化数字,如果是非数字则返回默认值
* @param {*} obj 待格式化对象
* @param {*} [dft] 非数字时的默认值
* @returns {*}
*/
exports.parseFloat = function (obj, dft) {
obj = parseFloat(obj);
return isNaN(obj) ? dft : obj;
};
/**
* 遍历元素
* @param {Array/Object} list 数组、可枚举对象
* @param {Function} callback 回调,返回false时停止遍历
* @param {*} [context] 上下文
*
* @example
* // 与 jQuery.each 一样
* // 返回 false 时将退出当前遍历
* data.each(list, function(key, val){});
*/
exports.each = function (list, callback, context) {
var i;
var j;
// 数组 或 类似数组
if (list && list.length !== udf) {
for (i = 0, j = exports.parseInt(list.length, 0); i < j; i++) {
context = context || global;
if (callback.call(context, i, list[i]) === false) {
break;
}
}
}
// 纯对象
else if (list !== null && list !== udf) {
for (i in list) {
if (list.hasOwnProperty(i)) {
context = context || global;
if (callback.call(context, i, list[i]) === false) {
break;
}
}
}
}
};
/**
* 扩展静态对象
* @param {Boolean|Object} [isExtendDeep] 是否深度扩展,可省略,默认false
* @param {Object} [source] 源对象
* @param {...Object} [target] 目标对象,可以是多个
* @returns {*}
*
* @example
* // 使用方法与 jQuery.extend 一样
* var o1 = {a: 1};
* var o2 = {b: 2};
* var o3 = data.extend(true, o1, o2);
* // => {a: 1, b: 2}
* o1 === o3
* // => true
*
* // 如果不想污染原始对象,可以传递一个空对象作为容器
* var o1 = {a: 1};
* var o2 = {b: 2};
* var o3 = data.extend(true, {}, o1, o2);
* // => {a: 1, b: 2}
* o1 === o3
* // => fale
*/
exports.extend = function (isExtendDeep, source, target) {
var args = arguments;
var firstArgIsBoolean = typeof(args[0]) === 'boolean';
var current = firstArgIsBoolean ? 1 : 0;
var length = args.length;
var i;
var obj;
var sourceType;
var objType;
isExtendDeep = firstArgIsBoolean && args[0] === true;
source = args[current++];
for (; current < length; current++) {
obj = args[current];
for (i in obj) {
if (obj.hasOwnProperty(i) && obj[i] !== undefined) {
sourceType = typeis(source[i]);
objType = typeis(obj[i]);
if (objType === 'object' && isExtendDeep) {
source[i] = sourceType !== objType ? {} : source[i];
exports.extend.call(this, isExtendDeep, source[i], obj[i]);
} else if (objType === 'array' && isExtendDeep) {
source[i] = sourceType !== objType ? [] : source[i];
exports.extend.call(this, isExtendDeep, source[i], obj[i]);
} else {
source[i] = obj[i];
}
}
}
}
return source;
};
/**
* 转换对象为一个纯数组,只要对象有length属性即可
* @param {Object} [obj] 对象
* @param {Boolean} [isConvertWhole] 是否转换整个对象为数组中的第0个元素,当该对象无length属性时,默认false
* @returns {Array}
*
* @example
* var o = {0:"foo", 1:"bar", length: 2}
* data.toArray(o);
* // => ["foo", "bar"]
*
* var a1 = [1, 2, 3];
* // 转换后的数组是之前的副本
* var a2 = data.toArray(a1);
* // => [1, 2, 3]
* a2 === a1;
* // => false
*/
exports.toArray = function (obj, isConvertWhole) {
var ret = [];
var i = 0;
var j;
var objType = typeis(obj);
if (canListTypeArr.indexOf(objType) > -1 && typeis(obj.length) === 'number' && obj.length >= 0) {
for (j = obj.length; i < j; i++) {
ret.push(obj[i]);
}
} else if (obj && isConvertWhole) {
ret.push(obj);
}
return ret;
};
///**
// * 判断一个对象是否有属于自身的方法、属性,而不是原型链方法、属性以及其他继承来的方法、属性
// * @param obj {Object} 判断对象
// * @param prop {String} 方法、属性名称
// * @returns {Boolean}
// *
// * @example
// * var o = {a: 1};
// * data.hasOwnProperty(o, 'a');
// * // => true
// */
//exports.hasOwnProperty = function (obj, prop) {
// return Object.prototype.hasOwnProperty.call(obj, prop);
//};
/**
* 对象1级比较,找出相同和不同的键
* @param obj1 {Object|Array}
* @param obj2 {Object|Array}
* @returns {Object}
*
* @example
* data.compare({a:1,b:2,c:3}, {a:1,d:4});
* // =>
* // {
* // same: ["a"],
* // only: [
* // ["b", "c"],
* // ["d"]
* // ],
* // different: ["b", "c", "d"]
* // }
*/
exports.compare = function (obj1, obj2) {
var obj1Type = typeis(obj1);
var obj2Type = typeis(obj2);
var obj1Only = [];
var obj2Only = [];
var same = [];
// 类型不同
if (obj1Type !== obj2Type) {
return null;
}
// 对象
if (obj1Type === 'object' || obj1Type === 'array') {
exports.each(obj1, function (key, val) {
if (obj2[key] !== val) {
obj1Only.push(key);
} else {
same.push(key);
}
});
exports.each(obj2, function (key, val) {
if (obj1[key] !== val) {
obj2Only.push(key);
}
});
return {
same: same,
only: [
obj1Only,
obj2Only
],
different: obj1Only.concat(obj2Only)
};
} else {
return null;
}
};
/**
* 修正正则字符串
* @param regExpString
* @returns {String}
*
* @example
* data.fixRegExp('/');
* // => '\/'
*/
exports.fixRegExp = function (regExpString) {
return regExpString.replace(REG_STRING_FIX, '\\$&');
};
/**
* 计算字节长度
* @param string {String} 原始字符串
* @param [doubleLength=2] {Number} 双字节长度,默认为2
* @returns {number}
*
* @example
* data.bytes('我123');
* // => 5
*/
exports.bytes = function (string, doubleLength) {
string += '';
doubleLength = exports.parseInt(doubleLength, 2);
var i = 0,
j = string.length,
k = 0,
c;
for (; i < j; i++) {
c = string.charCodeAt(i);
k += (c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f) ? 1 : doubleLength;
}
return k;
};
/**
* 计算字符串长度
* 双字节的字符使用 length 属性计算不准确
* @ref http://es6.ruanyifeng.com/#docs/string
* @param string {String} 原始字符串
*
* @example
* var s = "𠮷";
* s.length = 2;
* dato.length(s);
* // => 3
*/
exports.length = function (string) {
string += '';
return string.replace(REG_NOT_UTF16_SINGLE, '*').length;
};
/**
* 按长度补0填充数字
* @param {Number|String} number 数字
* @param {Number} length 长度
* @return {String} 修复后的数字
*
* @example
* fixedNumber(2, 4);
* // => "0002"
*/
exports.fillNumber = function (number, length) {
var len = length;
var start = '';
while (len--) {
start += '0';
}
return (start + number).slice(-length);
};
/**
* 修正 path 路径为系统分隔符
* @param p
* @returns {String}
*/
exports.fixPath = function (p) {
return p.replace(REG_PATH, path.sep);
};
/**
* 转换路径为 URL 格式
* @param p
* @returns {string}
*/
exports.toURLPath = function (p) {
return String(p).replace(REG_URL, '/');
};
/**
* 删除注释
* @param str 原始字符串
* @returns {String}
*/
exports.removeComments = function (str) {
var currentChar;
var nextChar;
var insideString = false;
var insideComment = false;
var ret = '';
for (var i = 0; i < str.length; i++) {
currentChar = str[i];
nextChar = str[i + 1];
if (!insideComment && str[i - 1] !== '\\' && currentChar === '"') {
insideString = !insideString;
}
if (insideString) {
ret += currentChar;
continue;
}
if (!insideComment && currentChar + nextChar === '//') {
insideComment = 'single';
i++;
} else if (insideComment === 'single' && currentChar + nextChar === '\r\n') {
insideComment = false;
i++;
ret += currentChar;
ret += nextChar;
continue;
} else if (insideComment === 'single' && currentChar === '\n') {
insideComment = false;
} else if (!insideComment && currentChar + nextChar === '/*') {
insideComment = 'multi';
i++;
continue;
} else if (insideComment === 'multi' && currentChar + nextChar === '*/') {
insideComment = false;
i++;
continue;
}
if (insideComment) {
continue;
}
ret += currentChar;
}
return ret;
};
/**
* base64 to ascii
* @param ascii
*/
exports.atob = function (ascii) {
try {
return new Buffer(encodeURIComponent(ascii), 'utf8').toString('base64');
} catch (err) {
return '';
}
};
/**
* base64 to ascii
* @param base64
*/
exports.btoa = function (base64) {
try {
return decodeURIComponent(new Buffer(base64, 'base64').toString());
} catch (err) {
return '';
}
};
/**
* 人类数字,千位分割
* @param number {String|Number} 数字(字符串)
* @param [separator=","] {String} 分隔符
* @param [length=3] {Number} 分隔长度
* @returns {string} 分割后的字符串
*/
exports.humanize = function (number, separator, length) {
separator = separator || ',';
length = length || 3;
var reg = new RegExp('(\\d)(?=(\\d{' + length + '})+$)', 'g');
var arr = String(number).split('.');
var p1 = arr[0].replace(reg, '$1' + separator);
return p1 + (arr[1] ? '.' + arr[1] : '');
};
/**
* 比较两个长整型数值
* @param long1 {String} 长整型数值字符串1
* @param long2 {String} 长整型数值字符串2
* @param [operator=">"] {String} 比较操作符,默认比较 long1 > long2
* @returns {*}
*/
exports.than = function (long1, long2, operator) {
operator = operator || '>';
long1 = String(long1).replace(REG_BEGIN_0, '');
long2 = String(long2).replace(REG_BEGIN_0, '');
// 1. 比较长度
if (long1.length > long2.length) {
return operator === '>';
} else if (long1.length < long2.length) {
return operator === '<';
}
var long1List = exports.humanNumber(long1, ',', 15).split(',');
var long2List = exports.humanNumber(long2, ',', 15).split(',');
//[
// '123456',
// '789012345678901',
// '234567890123456',
// '789012345678901',
// '234567890123457'
// ]
// 2. 比较数组长度
if (long1List.length > long2List.length) {
return operator === '>';
} else if (long1List.length < long2List.length) {
return operator === '<';
}
// 3. 遍历比较
var ret = false;
exports.each(long1List, function (index, number1) {
var number2 = long2List[index];
if (number1 > number2) {
ret = operator === '>';
return false;
} else if (number1 < number2) {
ret = operator === '<';
return false;
}
});
return ret;
};
/**
* 获取 gravatar
* @param email {String} 邮箱
* @param [options] {Object} 配置
* @param [options.host="http://gravatar.duoshuo.com/avatar/"] {String} 服务器
* @param [options.size=100] {Number} 尺寸
* @param [options.default="retro"] {Number} 默认头像
* @param [options.forcedefault=false] {*} 是否忽略默认头像
* @param [options.rating] {*} 评级
* @returns {string}
*/
exports.gravatar = function (email, options) {
options = options || {};
email = email.toLowerCase();
if (!options.host) {
options.host = 'http://gravatar.duoshuo.com/avatar/';
}
options.host += crypto.md5(email) + '?';
if (!options.size) {
options.size = 100;
}
if (!options.default) {
//options.default = 'http://s.ydr.me/p/i/avatar.png';
options.default = 'retro';
}
if (options.forcedefault) {
options.forcedefault = 'y';
} else {
options.forcedefault = false;
}
var query = {
s: options.size
};
if(options.default){
query.d = options.default;
}
if(options.forcedefault){
query.f = options.forcedefault;
}
if(options.rating){
query.r = options.rating;
}
return options.host + qs.stringify(query);
};
//var ascii = '云淡然2014';
//var base64;
//console.log(base64 = exports.atob(ascii));
//console.log(exports.btoa(base64));
//console.log(exports.humanize('31231231231210.003112'));
//var long1 = '12345678901234567890';
//var long2 = '5';
//
//var ret = exports.than(long1, long2);
//console.log(ret);
//console.log(exports.gravatar('1@1.com'));