Last active
October 29, 2016 09:23
-
-
Save Gaubee/232b3cda2927af1a40a8ea30e59b63f4 to your computer and use it in GitHub Desktop.
Mind Pact加入事件监听功能
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 事件监听 | |
var MP = require("../mind-pact"); | |
var eve = new MP({}); | |
[ | |
"a", | |
"a.b", | |
"a.b2", | |
"a.b.c", | |
"a.b.c1", | |
"a.b.c2" | |
].forEach(eventName => eve.on(eventName, function() { | |
console.log(eventName, "CHANGED") | |
})); | |
eve.on(["a",function (self,set_paths,set_value,pre_paths,cur_path) { | |
// console.log(arguments) | |
return cur_path | |
}],function () { | |
console.log("smart CHANGED") | |
}) | |
eve.set("a.b", { | |
c: { | |
cc: "cc", | |
dd: "dd" | |
}, | |
c1: "c1" | |
}); | |
// console.log(MP.formatKey("a.b")) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(root, factory) { | |
if (typeof define === 'function' && define.amd) { | |
define(['MP'], factory); | |
} else if (typeof module === "object") { | |
module.exports = factory(root); | |
} else { | |
root.MP = factory(root); | |
} | |
}(this, function(__global) { | |
var QuotedString = /"(?:\.|(\\\")|[^\""\n])*"|'(?:\.|(\\\')|[^\''\n])*'/g, //引号字符串 | |
$NULL = null, | |
$UNDEFINED, | |
$TRUE = !$UNDEFINED, | |
$FALSE = !$TRUE, | |
_placeholder = function(prefix) { | |
return (prefix || "@") + Math.random().toString(36).substr(2) | |
}, | |
$ = { | |
//判断是否为字符串 | |
isS: function(str) { | |
return typeof str === "string" | |
}, | |
//判断是否为数组 | |
isA: function(obj) { | |
return obj instanceof Array; | |
}, | |
//判断是否为函数 | |
isF: function(obj) { | |
return typeof obj === "function" | |
}, | |
//Object.assign | |
assign: Object.assign || function(target) { | |
'use strict'; | |
if (target == null) { | |
throw new TypeError('Cannot convert undefined or null to object'); | |
} | |
target = Object(target); | |
for (var index = 1; index < arguments.length; index++) { | |
var source = arguments[index]; | |
if (source != null) { | |
for (var key in source) { | |
if (Object.prototype.hasOwnProperty.call(source, key)) { | |
target[key] = source[key]; | |
} | |
} | |
} | |
} | |
return target; | |
} | |
}; | |
//JS对象的获取 | |
//\[\]hash取值现在已经在model.get中支持 | |
var _obj_get_reg = /([^\^\~\+\-\*\/\%\=\&\|\?\:\s\(\)\{\}\:\;\'\"\,\<\>\@\#\!]+)/g; | |
var const_key = { | |
"true": $TRUE, | |
"false": $FALSE, | |
"undefined": $UNDEFINED, | |
"null": $NULL, | |
"NaN": NaN, | |
} | |
function MP(basedata) { | |
var self = this; | |
if (!(self instanceof MP)) { | |
return new MP(basedata); | |
} | |
//不对baseData做特殊处理,支持任意类型包括空类型的数据,且数据类型可任意更改 | |
self._database = basedata; | |
// 以固定字符串存储的事件监听,高效 | |
self._LisMap = {}; | |
self._LisMapKeys = []; | |
// 以数组存储的事件监听,低效 | |
self._LisSet = []; | |
}; | |
var Key_Getter_Factory_Cache = MP._Factory_Cache = {}; | |
// 占位符 | |
var PLA = MP.PLA = "PLA"; //_placeholder("PLA"); | |
function PLA_Handle(match, vm, context) { | |
if (match.T === 0) { | |
return vm.get(match.V) | |
} | |
if (match.T === 1) { | |
return match.V | |
} | |
} | |
// 默认的上下文环境 | |
MP.default_content = {}; | |
// 将字符串编译成解析队列 | |
MP.formatKey = function(key, context) { | |
if (!key) { | |
var res = []; | |
res.__context = context; | |
return res; | |
} | |
context = context ? $.assign({}, MP.default_content, context) : $.assign({}, MP.default_content); | |
var _exp_fun_prefix = _placeholder("_F") | |
var _str_ph = _placeholder("_s"); | |
var _release_ph_reg = new RegExp(_str_ph + "[\\d]+" + _str_ph, "g"); | |
var _release_ph_foo = function(str) { | |
return str.replace(_release_ph_reg, function(matchPh) { | |
return _str_maps[matchPh]; | |
}) | |
}; | |
var _str_maps = {}; | |
var index = 0; | |
key = key.replace(QuotedString, function(matchStr) { | |
index += 1; | |
var key = _str_ph + index + _str_ph; | |
_str_maps[key] = matchStr; | |
return key; | |
}); | |
function _format_key(str, layer) { | |
var res = []; | |
var i = 0; | |
var pre_i = 0; | |
var len = str.length; | |
var col = 0; | |
var c; | |
do { | |
c = str.charAt(i); | |
if (col === 0) { | |
if (c === ".") { | |
res.push(_release_ph_foo(str.substring(pre_i, i))); | |
pre_i = i + 1; | |
} else if (c === "[") { | |
col += 1; | |
pre_i !== i && res.push(_release_ph_foo(str.substring(pre_i, i))); | |
pre_i = i + 1; | |
} | |
} else { | |
if (c === "[") { | |
col += 1; | |
} else if (c === "]") { | |
col -= 1; | |
if (col === 0) { | |
var exp_str = str.substring(pre_i, i); | |
var exp_matchs = []; | |
var exp_pattern = exp_str.replace(_obj_get_reg, function(matchStr) { | |
var exp_match_index = exp_matchs.length; | |
// 如果是字符串、数字等变量,或者true、false等常量,直接返回,不直接支持正则 | |
// 全局的函数或者对象比如Math、String等,必须使用外部特定声明 | |
// 正则使用外部声明函数来进行替代,避免表达式模式过于复杂 | |
if (_release_ph_reg.test(matchStr)) { | |
// 获取实际字符串值 | |
matchStr = _release_ph_foo(matchStr); | |
exp_matchs.push({ | |
T: 1, | |
// 删除字符串两边的引号 | |
V: matchStr.substr(1, matchStr.length - 2) | |
}); | |
} else if (isFinite(matchStr)) { | |
exp_matchs.push({ | |
T: 1, | |
V: +matchStr | |
}); | |
} else if (const_key.hasOwnProperty(matchStr)) { | |
exp_matchs.push({ | |
T: 1, | |
V: const_key[matchStr] | |
}); | |
} else if (matchStr.indexOf("__context.") === 0 || matchStr === "__context" || | |
matchStr.indexOf("__vm.") === 0 || matchStr === "__vm" || | |
matchStr.indexOf("__global.") === 0 || matchStr === "__global") { //表达式中的关键字 | |
return matchStr; | |
} else { | |
// 要注意的是,除了被格式化的paths_str,其它默认使用JS表达式的,是要注意不支持obj.1这样的数字写法 | |
var child_res = _format_key(matchStr, layer + 1); | |
if (context.hasOwnProperty(child_res[0])) { | |
return "__context." + matchStr | |
} else { | |
exp_matchs.push({ | |
T: 0, | |
V: child_res | |
}); | |
} | |
} | |
return PLA + "(matchs[" + exp_match_index + "],__vm,__context)"; | |
}); | |
// console.log("exp_pattern:", exp_pattern) | |
// console.log("exp_matchs:", exp_matchs) | |
var exp_fun = ( | |
Key_Getter_Factory_Cache[exp_pattern] || | |
(Key_Getter_Factory_Cache[exp_pattern] = new Function("matchs,__context,__global," + PLA, "return function(__vm){return " + exp_pattern + "}")) | |
)(exp_matchs, context, __global, PLA_Handle); | |
res.push(exp_fun); | |
if (str.charAt(i + 1) === ".") { | |
i += 1; | |
// console.warn(key, "at ", i, "]" + c, "must be", "]."); | |
} | |
// console.log(i, len, res) | |
pre_i = i + 1; | |
} | |
} | |
} | |
i += 1; | |
} while (i < len); | |
// console.log("~:",pre_i,i,len,str.substring(pre_i, i)); | |
(pre_i < i) && res.push(_release_ph_foo(str.substring(pre_i, i))); | |
return res; | |
} | |
var res = _format_key(key, 0); | |
res.__context = context; | |
return res; | |
}; | |
MP.prototype = { | |
set: function(paths, value, context) { | |
var self = this; | |
var database = self._database; | |
var res = database; | |
if (!$.isA(paths)) { | |
paths = MP.formatKey(paths, context); | |
} | |
var path; | |
// 历史路径 | |
var his_paths = ""; | |
var listen_set = this._LisSet; | |
var listen_map = this._LisMap; | |
var listen_map_keys = this._LisMapKeys; | |
var listen_cbs = []; | |
for (var i = 0, len = paths.length; i < len; i += 1) { | |
path = paths[i]; | |
if ($.isF(path)) { | |
path = path(self) | |
} | |
if (i === len - 1) { // 最后一项 | |
var res_last_item = res[path]; | |
if (res_last_item && res_last_item.T === CTK) { // 自定义类型 | |
var pre_path_str = his_paths; | |
res_last_item.S.call(this, path, value, pre_path_str, pre_path_str + "." + path); | |
} else { | |
res[path] = value; | |
} | |
} else { | |
if (!res[path]) { | |
// 整数型的属性默认使用数组来进行创建 | |
var new_path = paths[i + 1]; | |
$.isS(new_path) || (new_path = new_path(self)); | |
res = res[path] = /^[0-9]+$/.test(new_path) ? [] : {} | |
} else { | |
res = res[path] | |
if (res && res.T === CTK) { // 自定义类型 | |
var pre_path_str = his_paths; | |
res = res.G.call(this, path, pre_path_str, pre_path_str + "." + path); | |
} | |
} | |
} | |
// 过滤要监听的事件 | |
if (listen_set.length) { | |
var _listen_set = listen_set.slice(); | |
listen_set = []; | |
for (var _i = 0, _listen_info; _listen_info = _listen_set[_i]; _i += 1) { | |
var _listen_info_path = _listen_info.paths[i]; | |
if ($.isF(_listen_info_path)) { | |
_listen_info_path = _listen_info_path(self, paths, value, his_paths, path) | |
} | |
if (_listen_info_path === path) { | |
if (_listen_info.paths.length === i + 1) { // 已经是最后一项了,完全匹配 | |
listen_cbs.push(_listen_info.cb) | |
} else { // 还没有完全匹配完成,继续循环匹配 | |
listen_set.push(_listen_info) | |
} | |
} | |
} | |
} | |
his_paths += (his_paths ? "." : "") + path; | |
var _listen_map_cbs = listen_map[his_paths]; | |
if (_listen_map_cbs) { | |
listen_cbs.push.apply(listen_cbs, _listen_map_cbs); | |
} | |
} | |
if (typeof value === "object") { //最后,如果这个新值如果是复杂形式,进行判断是否涉及到子集的变动 | |
var sub_his_paths = his_paths + "."; | |
for (var j = 0, _listen_map_keys_len = listen_map_keys.length; j < _listen_map_keys_len; j += 1) { | |
var _listen_map_paths = listen_map_keys[j]; | |
var _subset_value = value; | |
if (_listen_map_paths.indexOf(sub_his_paths) === 0) { | |
var LM_paths = MP.formatKey(_listen_map_paths.substr(sub_his_paths.length)); | |
for (var _l = 0, _l_len = LM_paths.length; _l < _l_len; _l += 1) { | |
var _LM_path = LM_paths[_l]; | |
if (_subset_value.hasOwnProperty(_LM_path)) { | |
_subset_value = _subset_value[_LM_path]; | |
if (_l_len === _l + 1 || typeof _subset_value !== "object") { | |
_listen_map_cbs = listen_map[_listen_map_paths]; | |
listen_cbs.push.apply(listen_cbs, _listen_map_cbs); | |
} | |
} | |
} | |
} | |
} | |
} | |
for (var _c = 0, cb; cb = listen_cbs[_c]; _c += 1) { | |
cb(paths, value) | |
} | |
return paths; | |
}, | |
get: function(paths, context) { | |
var self = this; | |
var database = self._database; | |
var res = database; | |
if (!$.isA(paths)) { | |
paths = MP.formatKey(paths, context); | |
} | |
var path; | |
var his_paths = []; | |
for (var i = 0, len = paths.length; i < len && res; i += 1) { | |
path = paths[i]; | |
if ($.isF(path)) { | |
path = path(self) | |
} | |
res = res[path]; | |
if (res && res.T === CTK) { | |
var pre_path_str = his_paths.join(".") | |
res = res.G.call(this, path, pre_path_str, pre_path_str + "." + path) | |
} | |
his_paths.push(path); | |
} | |
return res; | |
}, | |
setSource: function(paths, value, context) { | |
//注意:这种做法会导致中间某个值如果也是CustomType,那么它会无法工作 | |
CTK = PL_CTK; | |
var res = this.set(paths, value, context) | |
CTK = MP.CTK; | |
return res; | |
}, | |
getSource: function(paths, context) { | |
CTK = PL_CTK; | |
var res = this.get(paths, context) | |
CTK = MP.CTK; | |
return res; | |
}, | |
on: function(paths, callback) { | |
var self = this; | |
if ($.isA(paths)) { | |
self._LisSet.push({ | |
paths: paths, | |
cb: callback | |
}) | |
} else { | |
if (!self._LisMap[paths]) { | |
self._LisMapKeys.push(paths); | |
(self._LisMap[paths] = []).push(callback) | |
} | |
} | |
}, | |
off: function(paths, callback) { | |
var self = this; | |
if ($.isA(paths)) { | |
for (var i = 0, item; item = self._LisSet[i]; i += 1) { | |
if (item.paths === paths && item.cb === callback) { | |
return self._LisSet.splice(i, 1) | |
} | |
} | |
} else { | |
var cb_list = self._LisMap[paths]; | |
if (cb_list) { | |
for (var i = 0, cb; cb = cb_list[i]; i += 1) { | |
if (cb === callback) { | |
return cb_list.splice(i, 1); | |
} | |
} | |
} | |
} | |
}, | |
}; | |
var CTK = MP.CTK = "@Costom-Type-Key@"; | |
var PL_CTK = _placeholder("CTK"); | |
/* | |
* 使用鸭子类型来实现自定义数据 | |
* 在setter、getter等表达式在使用字符串表达的时候,能使得JSON能正确的运作 | |
*/ | |
var c_id = 0; | |
function CustomType(getter, setter) { | |
var type_err = " must\n be : expression(string) \n or : [ expression(string) [, context(any)] ] \n or : function"; | |
if ($.isS(getter) || ($.isA(getter) && $.isS(getter[0]))) { | |
if ($.isS(getter)) { | |
var getter_context = { | |
//关键字占位 | |
__id: c_id++, | |
$cur_key: $NULL, | |
$pre_path_str: $NULL, | |
$full_path_str: $NULL, | |
}; | |
var path_str = getter; | |
} else { | |
var path_str = getter[0]; | |
// 无需关注值的顺序,后面运行的时候会动态赋值 | |
getter_context = $.assign(getter_context, getter[1]); | |
} | |
var _getter_paths = MP.formatKey("[" + path_str + "]", getter_context); | |
getter_context = _getter_paths.__context; | |
var _inner_getter = _getter_paths[0]; | |
getter = function($cur_key, $pre_path_str, $full_path_str) { | |
if (!res.L) { | |
getter_context.$cur_key = $cur_key; | |
getter_context.$pre_path_str = $pre_path_str; | |
getter_context.$full_path_str = $full_path_str; | |
getter_context.$cur_value = res.V; | |
res.L = $TRUE; | |
res.V = _inner_getter(this); | |
res.L = $FALSE; | |
} | |
return res.V; | |
} | |
} else if (!$.isF(getter)) { | |
throw TypeError("getter" + type_err) | |
} | |
if (!setter) { | |
setter = function($cur_key, $new_value) { | |
res.V = $new_value | |
}; | |
} else if ($.isS(setter) || ($.isA(setter) && $.isS(setter[0]))) { | |
var setter_context = { | |
//关键字占位 | |
$cur_key: $NULL, | |
$new_value: $NULL, | |
$old_value: $NULL, | |
$pre_path_str: $NULL, | |
$full_path_str: $NULL | |
}; | |
if ($.isS(setter)) { | |
var path_str = setter; | |
} else { | |
var path_str = setter[0]; | |
// 无需关注值的顺序,后面运行的时候会动态赋值 | |
setter_context = $.assign(setter_context, setter[1]); | |
} | |
var _setter_paths = MP.formatKey("[" + path_str + "]", setter_context); | |
setter_context = _setter_paths.__context; | |
var _inner_setter = _setter_paths[0]; | |
setter = function($cur_key, $new_value, $pre_path_str, $full_path_str) { | |
if (res.L) { | |
res.V = $new_value | |
} else { | |
setter_context.$cur_key = $cur_key; | |
setter_context.$new_value = $new_value; | |
setter_context.$old_value = res.V; | |
setter_context.$pre_path_str = $pre_path_str; | |
setter_context.$full_path_str = $full_path_str; | |
var setter_key = _inner_setter(this); | |
if (setter_key === $UNDEFINED || setter_key === $NULL || setter_key === $FALSE) { | |
res.V = $new_value | |
} else { | |
res.L = $TRUE; | |
vm.set(setter_key, $new_value); | |
res.L = $FALSE; | |
} | |
} | |
} | |
} else if (!$.isF(setter)) { | |
throw TypeError("setter" + type_err) | |
} | |
var res = { | |
T: CTK, | |
G: getter, | |
S: setter, | |
//lock,避免setter、getter运行无限循环 | |
L: $FALSE, | |
// 用来缓存上一次Setter、Getter返回的数据 | |
V: $NULL | |
}; | |
return res; | |
}; | |
MP.CustomType = CustomType; | |
return MP; | |
})); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment