mass Framework前後通吃,後端是基於node.js組建起來。後端與前端不一樣,是依賴於node.js內建模組提供的一些API,因此前端那些依賴於瀏覽器提供的DOM API的模組無法複用。現在掐指一算,基本上核心模組上只有lang, flow能用得上,more則多一點,如random, pinyin, base64, uuid...
node.js內建了模組載入機制,這與我在前端使用的API非常不一致,我就再把它封裝一層。核心模組與前端的核心模組在其他方法也盡量保持一致,當然像domReady在後端就不需要,去掉。如類型判定,由於是V8引擎,支援ECMA262v5,因此可以寫得精簡。最大的不同是,DEBUG用到mass.log,它的第二個參數是表示格式化高亮。
代碼是暫時丟在這裡,以後還要做各種擴充的!
(function(){ //後端部分 2011.12.4 by 司徒正美 function mass(){} var version = 0.1, class2type = { "[object global]" : "Global" , "null" : "Null" , "NaN" : "NaN" , "undefined" : "Undefined" }, toString = class2type.toString; /** * 糅雜,為一個對象添加更多成員 * @param {Object} target 目標對象 * @param {Object} source 屬性包 * @return {Object} 目標對象 */ function mix(target, source){ var args = [].slice.call(arguments), key, ride = typeof args[args.length - 1] == "boolean" ? args.pop() : true; target = target || {}; for(var i = 1; source = args[i++];){ for (key in source) { if (ride || !(key in target)) { target[key] = source[key]; } } } return target; } var rformat = /([\d\D]+?)/ig; var formats = { bold : [1, 22], italic : [3, 23], underline : [4, 24], inverse : [7, 27], strike : [9, 29] }; var colors = {}; ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'].forEach(function(word,i){ colors[word] = i }); colors.gray = 99; function format (arr, str) { return '\x1b[' + arr[0] + 'm' + str + '\x1b[' + arr[1] + 'm'; }; mix(mass,{//為此版本的命名空間對象新增成員 rword : /[^, ]+/g, v : version, "@debug" : true, root: process.cwd(), /** * 數組化 * @param {ArrayLike} nodes 要處理的類數組對象 * @param {Number} start 可選。要抽取的片斷的起始下標。如果是負數,從後面取起 * @param {Number} end 可選。規定從何處結束選取 * @return {Array} */ slice: function (nodes, start, end) { for(var i = 0,n = nodes.length, result = []; i < n; i++){ result[i] = nodes[i]; } if (arguments.length > 1) { return result.slice(start , (end || result.length)); } else { return result; } }, /** * 用於取得資料的類型或判定資料的類型 * @param {Any} obj 要檢測的東西 * @param {String} str 要比較的類型 * @return {String|Boolean} */ type : function (obj, str){ var result = class2type[ (obj == null || obj !== obj )? obj : toString.call(obj) ] || "#"; if( result.charAt(0) === "#"){ if(Buffer.isBuffer(obj)){ result = 'Buffer'; //返回構造器名字 }else{ result = toString.call(obj).slice(8,-1); } } if(str){ return str === result; } return result; }, /** * 用於調試 * @param {String} s 要列印的內容 * @param {Boolean} color 進行各種顏色的高亮,使用<code style="format:blod;color:red;background:green"> * format的值可以為formats中五個之一或它們的組合(以空格隔開),背景色與字型色只能為colors之一 */ log:function (s, color){ if(color){ s = s.replace(rformat,function(a,style,ret){ style.toLowerCase().split(";").forEach(function(arr){ arr = arr.split(":"); var type = arr[0].trim(),val = arr[1].trim(); switch(type){ case "format": val.replace(/\w+/g,function(word){ if(formats[word]){ ret = format(formats[word],ret) } }); break; case "background": case "color": var array = type == "color" ? [30,39] : [40,49] if( colors[val]){ array[0] += colors[val] ret = format(array,ret) } } }); return ret; }); } console.log(s); }, /** * 產生索引值統一的對象,用於高速化判定 * @param {Array|String} array 如果是字串,請用","或空格分開 * @param {Number} val 可選,預設為1 * @return {Object} */ oneObject : function(array, val){ if(typeof array == "string"){ array = array.match(mass.rword) || []; } var result = {},value = val !== void 0 ? val :1; for(var i=0,n=array.length;i < n;i++){ result[array[i]] = value; } return result; }, mix:mix }); mass.noop = mass.error = function(){}; "Boolean,Number,String,Function,Array,Date,RegExp,Arguments".replace(mass.rword,function(name){ class2type[ "[object " + name + "]" ] = name; }); var rmodule = /([^(\s]+)\(?([^)]*)\)?/, names = [],//需要處理的模組名列表 rets = {},//用於收集模組的傳回值 cbi = 1e4 ;//用於產生回呼函數的名字 var map = mass["@modules"] = {}; //執行並移除所有依賴都具備的模組或回調 function resolveCallbacks(){ loop: for (var i = names.length,repeat, name; name = names[--i]; ) { var obj = map[name], deps = obj.deps; for(var key in deps){ if(deps.hasOwnProperty(key) && map[key].state != 2 ){ continue loop; } } //如果deps是Null 物件或者其依賴的模組的狀態都是2 if( obj.state != 2){ names.splice(i,1);//必須先移除再執行 var fn = obj.callback; rets[fn._name] = fn.apply(null,incarnate(obj.args));//只收集模組的傳回值 obj.state = 2; repeat = true; } } repeat && resolveCallbacks(); } function incarnate(args){//傳入一組模組名,返回對應模組的傳回值 for(var i = 0,ret = [], name; name = args[i++];){ ret.push(rets[name]); } return ret; } function deferred(){//一個簡單的非同步列隊 var list = [],self = function(fn){ fn && fn.call && list.push(fn); return self; } self.method = "pop"; self.fire = function(fn){ while(fn = list[self.method]()){ fn(); } return list.length ? self : self.complete(); } self.complete = mass.noop; return self; } var nativeModules = mass.oneObject("assert,child_process,cluster,crypto,dgram,dns,"+ "events,fs,http,https,net,os,path,querystring,readline,repl,tls,tty,url,util,vm,zlib") function useNativeRequire(name,url){ var nick = name.slice(1); if(nativeModules[nick]){ map[name].state = 2; rets[name] = require(nick); resolveCallbacks(); }else{ url = url || mass.root+"/" + nick + ".js"; try{ require(url); resolveCallbacks() }catch(e){ mass.stack(Function('mass.log("\033[31m'+e+'\033[39m")')); mass.stack.fire();//列印錯誤堆棧 } } } mass.mix(mass,{ stack : deferred(), define:function(name,deps,callback){//模組名,依賴列表,模組本身 if(typeof deps == "function"){//處理只有兩個參數的情況 callback = deps; deps = ""; } callback._name = "@"+name; //模組名 this.require(deps,callback); }, require:function(deps,callback,errback){//依賴列表,正向回調,負向回調 var _deps = {}, args = [], dn = 0, cn = 0; (deps +"").replace(mass.rword,function(url,name,match){ dn++; match = url.match(rmodule); name = "@"+ match[1];//取得模組名 if(!map[name]){ //防止重複產生節點與請求 map[name] = { };//state: undefined, 未載入; 1 已載入; 2 : 已執行 useNativeRequire(name,match[2]);//載入模組 }else if(map[name].state === 2){ cn++; } if(!_deps[name] ){ args.push(name); _deps[name] = "司徒正美";//去重 } }); var cbname = callback._name; if(dn === cn ){//在依賴都已執行過或沒有依賴的情況下 if(cbname && !(cbname in rets)){ map[cbname].state = 2 //如果是使用合并方式,模組會跑進此分支(只會執行一次) return rets[cbname] = callback.apply(null,incarnate(args)); }else if(!cbname){//普通的回調可執行無數次 return callback.apply(null,incarnate(args)) } } cbname = cbname || "@cb"+ (cbi++).toString(32); if(errback){ mass.stack(errback);//壓入錯誤堆棧 } map[cbname] = {//建立或更新模組的狀態 callback:callback, deps:_deps, args: args, state: 1 };//在正常情況下模組只能通過resolveCallbacks執行 names.unshift(cbname); resolveCallbacks(); } }); exports.mass = global.mass = mass;})();