/******************************************************************************* * * Nx数据结构相关方法 * * * * 2021.12.10 ******************************************************************************/ require( "base64" ); require( "rseed" ); const md = require( "md5" ); // 默认小数位数 const DefPN = 2; var NxMath = cc.Class( { name: "NxMath", // 创建 ctor: function() { this.uuTracker = 1000; }, // ------------------------------------------------------------ // 方法集:基本的数据类型判断 // ------------------------------------------------------------ // 数字 numGood: function( _n ) { return _n === +_n; }, // 正数 numPositive: function( _n, _inZero /* true */ ) { if( !this.numGood( _n ) ) { return false; } return ( _inZero !== false ) ? ( _n >= 0 ) : ( _n > 0 ); }, // 为X的数 numEqual: function( _n, _t ) { return this.numGood( _n ) && ( _n == _t ); }, // 大于X的数 numGreater: function( _n, _t ) { return this.numGood( _n ) && ( _n > _t ); }, // 小于X的数 numLess: function( _n, _t ) { return this.numGood( _n ) && ( _n < _t ); }, // 范围内的数 numInRange: function( _n, _range /* [x,y] */ ) { if( !this.arrGood( _range ) || _range.length != 2 ) { return false; } return this.numGood( _n ) && ( _n >= _range[ 0 ] && _n <= _range[ 1 ] ); }, // 数字修正 numFix: function( _num, _def = 0 ) { let ret = _num; if( !nx.dt.numGood( ret ) ) { ret = _def; } return ret; }, // 浮点精确度 toFixedDecimal: function( _n ) { if( !this.numGood( _n ) ) { return 0; } // 多保留一位来保证浮点数精度 let fixedNumber = ( _n * 100 ).toFixed( 1 ); return Number( ( parseInt( fixedNumber ) / 100 ).toFixed( 2 ) ); }, // 布尔 isBool: function( _b ) { return _b === !!_b; }, // 字节数格式化 formatBytes: function( bytes, decimals = 2 ) { if( bytes === 0 ) return '0 Bytes'; const k = 1024; const dm = decimals & lt; 0 ? 0 : decimals; const sizes = [ 'Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' ]; const i = Math.floor( Math.log( bytes ) / Math.log( k ) ); return parseFloat( ( bytes / Math.pow( k, i ) ).toFixed( dm ) ) + ' ' + sizes[ i ]; }, // ------------------------------------------------------------ // 方法集:方法的方法 // ------------------------------------------------------------ // 有效方法 fnGood: function( _f ) { return _f != null && typeof _f === "function"; }, // 空方法 fnEmpty: function() { }, // 空方法报错 fnEmptyError: function( _fn ) { if( this.fnGood( _fn ) ) { nx.error( "方法已经存在:", _fn.name ); } }, // 安全回调 fnInvoke: function( _fn ) { if( !this.fnGood( _fn ) ) { return; } if( CC_DEBUG ) { _fn.apply( null, Array.prototype.slice.call( arguments, 1 ) ); } else { try { _fn.apply( null, Array.prototype.slice.call( arguments, 1 ) ); } catch( e ) { nx.error( "[异常]:" + ( e ? e.message + "\n" + e.stack : "未知错误!" ) ); if( e && cc.sys.isNative && window && window.__errorHandler ) { window.__errorHandler( e.message, "", "", e.stack ); } } } }, // ------------------------------------------------------------ // 方法集:字符串的方法 // ------------------------------------------------------------ // 字符串 strGood: function( _s ) { return _s === _s + ""; }, // 非空字符串 strNEmpty: function( _s ) { return this.strGood( _s ) && _s != ""; }, // 空字符串 strEmpty: function( _s ) { return !this.strNEmpty( _s ); }, // 字符串去空格 strTrim: function( _s ) { if( this.strNEmpty( _s ) ) { return _s.replace( /^(\s|\u00A0)+/, "" ).replace( /(\s|\u00A0)+$/, "" ); } return ""; }, // 获取字节长度 strBytes: function( _s ) { if( !this.strNEmpty( _s ) ) { return 0; } var len = 0; for( var i = 0; i < _s.length; i++ ) { var a = _s.charAt( i ); if( a.match( /[^\x00-\xff]/ig ) != null ) { len += 2; } else { len += 1; } } return len; }, // 字符串省略截取 strCut: function( str, len ) { if( !str || !len ) { return ""; } var build = ""; for( var i = 0; i < str.length && len > 0; i++ ) { build += str.substr( i, 1 ); len -= str.charCodeAt( i ) > 127 ? 2 : 1; } if( build.length < str.length ) build += ".."; return build; }, // 字符串省略截取(逆向) strCutR: function( str, len ) { if( !str || !len ) { return ""; } var build = ""; for( var i = str.length - 1; i >= 0 && len > 0; i-- ) { build = str.substr( i, 1 ) + build; len -= str.charCodeAt( i ) > 127 ? 2 : 1; } if( build.length < str.length ) { build = "****" + build; } return build; }, /** * key#value属性串转数组 * @param {*} _sp */ pairSplit: function( _sp ) { let a = _sp.split( '#' ); if( a.length != 2 ) { nx.error( "pairSplit失败,格式无效!", _sp ); return null; } let k = a[ 0 ]; let v = a[ 1 ]; // 如果值是数字,那么转为数字 if( Number( v ) == v ) { v = Number( v ); } return { [ k ]: v }; }, /** * 特定key#value属性串转数组 * 用于统一拆分Array[String]数据 * @param {*} _arr */ arrPairSplit: function( _arr ) { let ret = []; if( this.arrEmpty( _arr ) ) { return ret; } _arr.forEach( _sp => { let d = this.pairSplit( _sp ); if( d ) { ret.push( d ); } } ); return ret; }, /** * 补零 * @param num: 被操作数 * @param n: 固定的总位数 */ prefixZero: function( num, n ) { return ( Array( n ).join( 0 ) + num ).slice( -n ); }, /** * 字符串替换 * replaceAll */ replace: function( _str, _old, _new ) { return _str.replace( new RegExp( _old, "gm" ), _new ); }, // ------------------------------------------------------------ // 方法集:数组的方法 // ------------------------------------------------------------ // 数组 arrGood: function( _a ) { return Object.prototype.toString.call( _a ) === "[object Array]"; }, // 非空数组 arrNEmpty: function( _a ) { return this.arrGood( _a ) && ( _a.length > 0 ); }, // 空数组 arrEmpty: function( _a ) { return !this.arrNEmpty( _a ); }, // 是不是数组成员 arrMember: function( _a, _m, _fn ) { if( !this.arrNEmpty( _a ) ) { return false; } _fn = _fn || function( _v ) { return _v == _m; }; for( let i = 0; i < _a.length; ++i ) { if( _fn( _a[ i ] ) ) { return true; } } return false; }, // 合并数据[带关键字且不能重复] arrConcat: function( _a1, _a2 ) { if( !this.arrGood( _a1 ) || !this.arrGood( _a2 ) ) { nx.error( "合并失败,参数不能为空!" ); return null; } // 合并Table绑定列表 if( CC_DEBUG ) { var _exist = function( _key ) { for( let k in _a1 ) { var m = _a1[ k ]; if( m[ 0 ] == _key ) { return true; } } return false; }; for( let i in _a2 ) { var binder = _a2[ i ]; if( _exist( binder[ 0 ] ) ) { nx.error( "合并失败: %s,有重复关键字项!", binder[ 0 ] ); return null; } } } return _a1.concat( _a2 ); }, // 从数组中删除 arrDelete: function( _a, _fn, _once ) { if( !this.arrNEmpty( _a ) || !this.fnGood( _fn ) ) { return _a; } var i = _a.length; while( i-- ) { if( _fn( _a[ i ] ) ) { _a.splice( i, 1 ); if( _once != false ) { break; } } } return _a; }, // 对象转数组对 arrFromMap: function( _map ) { if( !this.objGood( _map ) ) { nx.warn( "对象为空,转换失败!" ); return []; } var pairs = []; for( let i in _map ) { pairs.push( [ i, _map[ i ] ] ); } return pairs; }, // 数组比较(成员相同) arrCompare: function( _a, _b ) { if( !this.arrGood( _a ) || !this.arrGood( _b ) || _a.length != _b.length ) { return false; } for( let i in _a ) { if( !this.arrMember( _b, _a[ i ] ) ) { return false; } } return true; }, // ------------------------------------------------------------ // 方法集:对象的方法 // ------------------------------------------------------------ // 创建一个UUID(运行时唯一) newUUID: function( _key ) { if( !this.strNEmpty( _key ) ) { _key = "OBJ"; } return _key + ( ++this.uuTracker ); }, // 创建永不重复的Tag newTag: function( _key ) { let tag = this.md5( cc.sys.now().toString() ); if( this.strEmpty( _key ) ) { return tag; } return _key + "." + tag; }, // 空对象 objGood: function( _o ) { return _o != null && typeof _o === "object" && Array.isArray( _o ) === false; }, // 非空对象 objNEmpty: function( _o ) { if( !this.objGood( _o ) ) { return false; } return Object.keys( _o ).length > 0; }, // 空对象 objEmpty: function( _o ) { return !this.objNEmpty( _o ); }, // 对象合并 objConcat: function( _dic1, _dic2 ) { if( !this.objGood( _dic1 ) ) { return this.objClone( _dic2 ); } if( !this.objGood( _dic1 ) ) { return this.objClone( _dic1 ); } var dic = this.objClone( _dic1 ); for( let k in _dic2 ) { dic[ k ] = this.objClone( _dic2[ k ] ); } return dic; }, // 获取对象成员数量 objLen: function( _dic ) { if( !this.objGood( _dic ) ) { return 0; } return Object.keys( _dic ).length; }, // 深复制 objClone: function( _obj ) { // 非对象直接使用返回 if( !_obj || typeof ( _obj ) !== "object" ) { return _obj; } var out = ( _obj.constructor === Array ) ? [] : {}; var _clone = function( o, c ) { for( var i in o ) { if( o[ i ] && typeof o[ i ] === "object" ) { if( o[ i ].constructor === Array ) { c[ i ] = []; } else { c[ i ] = {}; } _clone( o[ i ], c[ i ] ); } else { c[ i ] = o[ i ]; } } return c; }; return _clone( _obj, out ); }, // 复制属性到对象 copyProperties: function( _object, _dic, _log = false ) { if( !this.objGood( _object ) || !this.objGood( _dic ) ) { return false; } if( _log ) { nx.debug( "[DT]属性更新:" ); } for( let k in _dic ) { _object[ k ] = this.objClone( _dic[ k ] ); if( _log ) { nx.debug( "\t\t键值: %s", k ); } } }, // ------------------------------------------------------------ // 方法集:随机数的方法 // ------------------------------------------------------------ // 范围随机数 randomRange( _min, _max ) { var Range = _max - _min; var Rand = Math.random(); var num = _min + Math.round( Rand * Range ); //四舍五入 return num; }, // ------------------------------------------------------------ // 方法集:加密的方法 // ------------------------------------------------------------ // 获取md5 md5: function( key ) { return md.hex_md5( key ); }, // 加密 encode64: function( str, pwd ) { str = Base64.encode( str );//Base64加密 var prand = ""; for( var i = 0; i < pwd.length; i++ ) { prand += pwd.charCodeAt( i ).toString(); } var sPos = Math.floor( prand.length / 5 ); var mult = parseInt( prand.charAt( sPos ) + prand.charAt( sPos * 2 ) + prand.charAt( sPos * 3 ) + prand.charAt( sPos * 4 ) + prand.charAt( sPos * 5 ) ); var incr = Math.ceil( pwd.length / 2 ); var modu = Math.pow( 2, 31 ) - 1; if( mult < 2 ) { alert( "Please choose a more complex or longer password." ); return null; } var salt = Math.round( Math.random() * 1000000000 ) % 100000000; prand += salt; while( prand.length > 10 ) { prand = ( parseInt( prand.substring( 0, 10 ) ) + parseInt( prand.substring( 10, prand.length ) ) ).toString(); } prand = ( mult * prand + incr ) % modu; var enc_chr = ""; var enc_str = ""; for( var i = 0; i < str.length; i++ ) { enc_chr = parseInt( str.charCodeAt( i ) ^ Math.floor( ( prand / modu ) * 255 ) ); if( enc_chr < 16 ) { enc_str += "0" + enc_chr.toString( 16 ); } else enc_str += enc_chr.toString( 16 ); prand = ( mult * prand + incr ) % modu; } salt = salt.toString( 16 ); while( salt.length < 8 ) salt = "0" + salt; enc_str += salt; return enc_str; }, // 解密 decode64: function( str, pwd ) { var prand = ""; for( var i = 0; i < pwd.length; i++ ) { prand += pwd.charCodeAt( i ).toString(); } var sPos = Math.floor( prand.length / 5 ); var mult = parseInt( prand.charAt( sPos ) + prand.charAt( sPos * 2 ) + prand.charAt( sPos * 3 ) + prand.charAt( sPos * 4 ) + prand.charAt( sPos * 5 ) ); var incr = Math.round( pwd.length / 2 ); var modu = Math.pow( 2, 31 ) - 1; var salt = parseInt( str.substring( str.length - 8, str.length ), 16 ); str = str.substring( 0, str.length - 8 ); prand += salt; while( prand.length > 10 ) { prand = ( parseInt( prand.substring( 0, 10 ) ) + parseInt( prand.substring( 10, prand.length ) ) ).toString(); } prand = ( mult * prand + incr ) % modu; var enc_chr = ""; var enc_str = ""; for( var i = 0; i < str.length; i += 2 ) { enc_chr = parseInt( parseInt( str.substring( i, i + 2 ), 16 ) ^ Math.floor( ( prand / modu ) * 255 ) ); enc_str += String.fromCharCode( enc_chr ); prand = ( mult * prand + incr ) % modu; } return Base64.decode( enc_str ); }, // ------------------------------------------------------------ // 方法集:JSON // ------------------------------------------------------------ // 安全执行(异常捕获) trycatch: function( _dosth, _error ) { try { _dosth(); } catch( e ) { nx.error( "[异常]:" + ( e ? e.message + "\n" + e.stack : "未知错误!" ) ); if( _error ) { _error( e ); } if( e && cc.sys.isNative && window && window.__errorHandler ) { window.__errorHandler( e.message, "", "", e.stack ); } } }, // JSON压缩 enjson: function( _object ) { try { return JSON.stringify( _object ); } catch( e ) { nx.error( "[异常]:" + ( e ? e.message + "\n" + e.stack : "未知错误!" ) ); // if( e && cc.sys.isNative && window && window.__errorHandler ) { // window.__errorHandler( e.message, "", "", e.stack ); // } return ""; } }, // JSON解析 dejson: function( _str ) { // 空返回 if( !this.strNEmpty( _str ) ) { return null; } try { return JSON.parse( _str ); } catch( e ) { nx.error( "[异常]:" + ( e ? e.message + "\n" + e.stack : "未知错误!" ) ); // if( e && cc.sys.isNative && window && window.__errorHandler ) { // window.__errorHandler( e.message, "", "", e.stack ); // } return null; } }, // ------------------------------------------------------------ // 方法集:时间的方法 // ------------------------------------------------------------ // 秒级时间戳 timeStamp: function() { return Math.floor( cc.sys.now() / 1000 ); }, // 格式化日期 fmtDate: function( _date, _year = true ) { _date = _date ? _date : new Date(); if( this.numGood( _date ) ) { _date = new Date( _date ); } var _format = function( v ) { return ( v < 10 ) ? ( "0" + v ) : v; }; if( !_year ) { return nx.text.formatS( "%s:%s", _format( _date.getMonth() + 1 ), _format( _date.getDate() ) ); } return nx.text.formatS( "%s-%s-%s", _format( _date.getFullYear() ), _format( _date.getMonth() + 1 ), _format( _date.getDate() ) ); }, // 格式化日期 fmtDateLoc: function( _date, _year = true ) { _date = _date ? _date : new Date(); if( !_year ) { return nx.text.formatS( "%s%s", nx.text.format( "month", _date.getMonth() + 1 ), nx.text.format( "day", _date.getDate() ) ); } return nx.text.formatS( "%s%s%s", nx.text.format( "year", _date.getFullYear() ), nx.text.format( "month", _date.getMonth() + 1 ), nx.text.format( "day", _date.getDate() ) ); }, // 格式化时间 fmtTime: function( _date ) { _date = _date ? _date : new Date(); var _format = function( v ) { return ( v < 10 ) ? ( "0" + v ) : v; }; return nx.text.formatS( "%s:%s:%s", _format( _date.getHours() ), _format( _date.getMinutes() ), _format( _date.getSeconds() ) ); }, // 格式化时间 fmtTimeFromSecs: function( _secs, _full = false ) { if( !this.numPositive( _secs, false ) ) { return _full ? "00:00:00" : "00:00"; } var f = function( v ) { return ( v < 10 ) ? ( "0" + v ) : v; }; var h = Math.floor( _secs / 3600 ); _secs = _secs % 3600; var m = Math.floor( _secs / 60 ); var s = Math.floor( _secs % 60 ); // 完整版本 if( _full ) { return nx.text.formatS( "%s:%s:%s", f( h ), f( m ), f( s ) ); } // 时:分:秒 if( h > 0 ) { return nx.text.formatS( "%d:%s:%s", h, f( m ), f( s ) ); } // 分:秒 if( m > 0 ) { return nx.text.formatS( "%s:%s", f( m ), f( s ) ); } // 秒 return '00:' + f( s ); }, // 计算剩余天数(向上取整) // _ts: 秒级时间戳 getDaysFromNow: function( _ts ) { let secs = Math.max( 0, _ts - ( cc.sys.now() / 1000 ) ); let days = Math.ceil( secs / ( 24 * 3600 ) ); return days; }, // ------------------------------------------------------------ // 方法集:URL的方法 // ------------------------------------------------------------ // 格式化URL formatURL: function( _url, _args ) { if( !this.strNEmpty( _url ) ) { nx.error( "WebTask:URL参数不能为空!" ); return ""; } if( !this.objGood( _args ) ) { return encodeURI( _url ); } var aline = ""; for( var k in _args ) { aline += ( aline == "" ) ? "?" : "&"; var v = _args[ k ]; var o = ( this.objGood( v ) || this.arrGood( v ) ); aline += k + "=" + ( o ? nx.dt.enjson( v ) : v ); } return encodeURI( _url + aline ); }, // ------------------------------------------------------------ // 方法集:数量方法 // ------------------------------------------------------------ /** * 数量千分化 * @param {*} _count : 数量 * @param {*} _pn : 小数位数 * @returns */ fmtCount: function( _count, _pn ) { if( !nx.dt.numPositive( _pn ) || _pn < 0 || _pn > 20 ) { _pn = DefPN; } _count = parseFloat( ( _count + "" ).replace( /[^\d\.-]/g, "" ) ).toFixed( _pn ) + ""; var l = _count.split( "." )[ 0 ].split( "" ).reverse(); var r = _count.split( "." )[ 1 ]; var t = ""; for( let i = 0; i < l.length; i++ ) { t += l[ i ] + ( ( i + 1 ) % 3 == 0 && ( i + 1 ) != l.length ? "," : "" ); } if( r ) { var ret = t.split( "" ).reverse().join( "" ) + "." + r; } else { var ret = t.split( "" ).reverse().join( "" ); } if( ( ret.charAt( 0 ) == "-" || ret.charAt( 0 ) == "+" ) && ret.charAt( 1 ) == "," ) { ret = ret.charAt( 0 ) + ret.slice( 2 ); } return ret; }, /** * 数量简化 * @param {*} _count 数量 * @param {*} _sdd 强制+号 * @param {*} _pn 小数位数 * @param {*} _trimZero 忽略尾部0 * @returns */ shortCount: function( _count, _sdd = false, _pn = 2, _trimZero = true ) { // 转数字 _count = parseInt( _count ); if( !this.numGood( _count ) ) { _count = 0; } var lessZero = false; if( _count < 0 ) { lessZero = true; _count *= -1; } if( !this.numPositive( _pn ) ) { _pn = DefPN; } let value = ""; let unitKey = ""; // 国际通用KM小数点后保留2位来展示 // 小于1K // if( _count < 1000 ) { if( _count < 100000000 ) { value = "" + this.toFixedDecimal( _count ); if( !_trimZero ) { value = this.fmtCount( value, _pn ); } } // // 1M > _count >= 1K // else if( _count >= 1000 && _count < 1000000 ) { // value = this.toFixedDecimal( _count / 1000 ).toFixed( _pn ); // unitKey = "K"; // } // 1B > _count >= 1M else if( _count >= 100000000 && _count < 1000000000 ) { value = this.toFixedDecimal( _count / 1000000 ).toFixed( _pn ); unitKey = "M"; } // >= 1B else { value = this.toFixedDecimal( _count / 1000000000 ).toFixed( _pn ); unitKey = "B"; } // 末尾0忽略 if( _trimZero ) { value = Number( value ) + unitKey; } else { value += unitKey; } if( lessZero ) { value = "-" + value; } else { if( _sdd === true ) { value = "+" + value; } } return value; }, // ------------------------------------------------------------ // 方法集:WEB参数解析 // ------------------------------------------------------------ // 获取URL网址参数 parseURLArgs: function() { var theRequest = {}; // 非浏览器不处理 if( !cc.sys.isBrowser || !location ) { return theRequest; } // 获取url中"?"符后的字串 var url = location.search; if( url.indexOf( "?" ) != -1 ) { var str = url.substr( 1 ); var strs = str.split( "&" ); for( var i = 0; i < strs.length; i++ ) { theRequest[ strs[ i ].split( "=" )[ 0 ] ] = unescape( strs[ i ].split( "=" )[ 1 ] ); } } return theRequest; }, // 针对URL不正规的参数解析 queryURLArg: function( _key ) { // 非浏览器不处理 if( !cc.sys.isBrowser || !window.location ) { return null; } var reg = new RegExp( '(^|&)' + _key + '=([^&]*)(&|$)', 'i' ); var serach = ''; if( window.location.search.length > 0 ) { serach = window.location.search.substr( 1 ); } else { serach = window.location.href.split( '?' )[ 1 ] ? window.location.href.split( '?' )[ 1 ] : ''; } var r = serach.match( reg ); if( r != null ) { return unescape( r[ 2 ] ); } return null; }, // ------------------------------------------------------------ // 方法集:距离的方法 // ------------------------------------------------------------ // 格式化距离 formatDistance: function( _meters ) { if( typeof _meters != "number" ) { return nx.text.format( "distance_none" ); } if( _meters < 1000 ) { return nx.text.format( "distance_m", _meters ); } var km = Math.floor( _meters / 1000 ); return nx.text.format( "distance_km", km ); }, // 文件大小字节转K,M,G formatFileSize: function( _size ) { if( !_size ) { return ""; } var num = 1024.00; //byte if( _size < num ) return _size + "B"; if( _size < Math.pow( num, 2 ) ) return ( _size / num ).toFixed( 2 ) + "K"; //kb if( _size < Math.pow( num, 3 ) ) return ( _size / Math.pow( num, 2 ) ).toFixed( 2 ) + "M"; //M if( _size < Math.pow( num, 4 ) ) return ( _size / Math.pow( num, 3 ) ).toFixed( 2 ) + "G"; //G return ( _size / Math.pow( num, 4 ) ).toFixed( 2 ) + "T"; //T }, // 检测账号是否包含中文 formatAccount: function( str ) { if( typeof str !== 'string' ) { return false; } //去除字符串的左右两边空格 //+表示匹配一次或多次,|表示或者,\s和\u00A0匹配空白字符,/^以……开头,$以……结尾,/g全局匹配,/i忽略大小写 str = ( str || "" ).replace( /^(\s|\u00A0)+|(\s|\u00A0)+$/g, "" ); //匹配中文,match返回包含中文的数组 const chinese = str.match( /[\u4e00-\u9fa5]/g ); //计算字符个数 return !!chinese; }, // 检测姓名是否是中文 formatChineseName: function( str ) { if( str.length <= 1 ) { return false; } const re = /[^\u4e00-\u9fa5]/; return !re.test( str ); }, // 随机字符串生成 randomStr: function( min, max, strRange ) { const rd = function( _l, _r ) { if( _r > _l ) { return Math.round( Math.random() * ( _r - _l ) ) + _l; } else { return _l; } }; var str = ""; strRange = strRange || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; var n = rd( min, max ); var len = strRange.length; for( var i = 0; i < n; i++ ) { var pos = rd( 1, len ) - 1; str += strRange.charAt( pos ); } return str; }, } ); // 模块导出 module.exports = NxMath;