2017年12月29日 星期五
GreenBackbone view 的 ajax(template | component)
--------------------------------------------------------------------------
// component的例子
<div>
{{x}}
</div>
<script>
// 把上面的 html內容傳進 fun
function(template) {
return Backbone.BForView.extend({
templateContent: template
});
}
</script>
--------------------------------------------------------------------------
// template的例子
<div>
{{x}}
</div>
<script>
function(view) {
// template mounted
}
</script>
<script>
function(view) {
// template removed }
</script>
--------------------------------------------------------------------------
// template 改進
<div>
{{x}}
</div>
<script>
function() {
return {
mounted: fun(view),
unmouted: fun(view)
}
}
</script>
--------------------------------------------------------------------------
2017年12月23日 星期六
Backbone.Reactive 重要的例子
var d_1 = [{
age: 12,
selected: true
}, {
age: 1,
selected: false
}, {
age: 2,
selected: false
}];
var d_2 = Backbone.Reactive(d_1, {
link: function(parent, child) {
parent.listenTo(child, 'all', function(e, event, options) {
debugger;
var info = 'parent => ' + event + '|' + JSON.stringify(arguments);
for (var i = 0; i < this.value.length; i++) {
debugger;
var v = this.value[i];
var ob = v._bc_ob_;
if (v._bc_ob_.cid !== e.target.cid) {
ob.triggerOptions = {
exclude: [this] // 防止事件無窮回圈
}
v.selected = false;
}
}
$('#msg_1').html(JSON.stringify(this));
})
}
});
/*--------------------------*/
d_2._bc_ob_.on('change', function() {
var info = 'parent => ' + event + '|' + JSON.stringify(arguments);
alert(info);
});
function set() {
var index = Math.floor(Math.random() * d_2.length);
index = (index >= d_2.length ? (d_2.length - 1) : index);
d_2[index].selected = true;
}
age: 12,
selected: true
}, {
age: 1,
selected: false
}, {
age: 2,
selected: false
}];
var d_2 = Backbone.Reactive(d_1, {
link: function(parent, child) {
parent.listenTo(child, 'all', function(e, event, options) {
debugger;
var info = 'parent => ' + event + '|' + JSON.stringify(arguments);
for (var i = 0; i < this.value.length; i++) {
debugger;
var v = this.value[i];
var ob = v._bc_ob_;
if (v._bc_ob_.cid !== e.target.cid) {
ob.triggerOptions = {
exclude: [this] // 防止事件無窮回圈
}
v.selected = false;
}
}
$('#msg_1').html(JSON.stringify(this));
})
}
});
/*--------------------------*/
d_2._bc_ob_.on('change', function() {
var info = 'parent => ' + event + '|' + JSON.stringify(arguments);
alert(info);
});
function set() {
var index = Math.floor(Math.random() * d_2.length);
index = (index >= d_2.length ? (d_2.length - 1) : index);
d_2[index].selected = true;
}
2017年12月22日 星期五
javascript Map
Properties
Map.prototype.size
Methods
Map.prototype.clear()
Map.prototype.delete()
Map.prototype.entries()
Map.prototype.forEach()
Map.prototype.get()
Map.prototype.has()
Map.prototype.keys()
Map.prototype.set()
Map.prototype.values()
Map.prototype[@@iterator]()
-----------------------------------------------------------------
使用
使用
透過
透過
與
Map.prototype.size
Methods
Map.prototype.clear()
Map.prototype.delete()
Map.prototype.entries()
Map.prototype.forEach()
Map.prototype.get()
Map.prototype.has()
Map.prototype.keys()
Map.prototype.set()
Map.prototype.values()
Map.prototype[@@iterator]()
-----------------------------------------------------------------
範例
區段
使用 Map
物件
區段
var myMap = new Map();
var keyString = 'a string',
keyObj = {},
keyFunc = function() {};
// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, 'value associated with keyObj');
myMap.set(keyFunc, 'value associated with keyFunc');
myMap.size; // 3
// getting the values
myMap.get(keyString); // "value associated with 'a string'"
myMap.get(keyObj); // "value associated with keyObj"
myMap.get(keyFunc); // "value associated with keyFunc"
myMap.get('a string'); // "value associated with 'a string'"
// because keyString === 'a string'
myMap.get({}); // undefined, because keyObj !== {}
myMap.get(function() {}) // undefined, because keyFunc !== function () {}
使用 NaN
作為 Map
的鍵
區段
NaN
can also be used as a key. Even though every NaN
is not equal to itself (NaN !== NaN
is true), the following example works because NaN
s are indistinguishable from each other:var myMap = new Map();
myMap.set(NaN, 'not a number');
myMap.get(NaN); // "not a number"
var otherNaN = Number('foo');
myMap.get(otherNaN); // "not a number"
透過 for..of
迭代 Maps
區段
Maps can be iterated using afor..of
loop:var myMap = new Map();
myMap.set(0, 'zero');
myMap.set(1, 'one');
for (var [key, value] of myMap) {
console.log(key + ' = ' + value);
}
// 0 = zero
// 1 = one
for (var key of myMap.keys()) {
console.log(key);
}
// 0
// 1
for (var value of myMap.values()) {
console.log(value);
}
// zero
// one
for (var [key, value] of myMap.entries()) {
console.log(key + ' = ' + value);
}
// 0 = zero
// 1 = one
透過 forEach()
迭代 Maps
區段
Maps can be iterated using theforEach()
method:myMap.forEach(function(value, key) {
console.log(key + ' = ' + value);
});
// Will show 2 logs; first with "0 = zero" and second with "1 = one"
與 Array
物件關聯
區段
var kvArray = [['key1', 'value1'], ['key2', 'value2']];
// Use the regular Map constructor to transform a 2D key-value Array into a map
var myMap = new Map(kvArray);
myMap.get('key1'); // returns "value1"
// Use the Array.from function to transform a map into a 2D key-value Array
console.log(Array.from(myMap)); // Will show you exactly the same Array as kvArray
// Or use the keys or values iterators and convert them to an array
console.log(Array.from(myMap.keys())); // Will show ["key1", "key2"]
2017年12月21日 星期四
2017年12月14日 星期四
2017年12月11日 星期一
drupal 簡單 model_example
<?php
// $Id$
function gt_site_menu() {
$items['site/%'] = array(
'title' => 'test',
'page callback' => 'goPage',
'access callback' => true
);
return $items;
}
function goPage() {
$res = theme('home', array());
return $res;
}
function gt_site_theme() {
// 取得 gt_site 該 model 的 path
$path = drupal_get_path('module', 'gt_site');
$path .= '/themes';
return array(
'home' => array(
'template' => 'home',
'path' => $path)
);
}
/**
* Implements hook_help().
*/
function gt_site_help($path, $arg) {
$res = array();
foreach ($_GET as $key => $value) {
$res[] = sprintf('%s => %s', $key, $value);
}
$res[] = $path = drupal_get_path('module', 'gt_site');
$res = implode(' | ', $res);
return t($res);
}
// $Id$
function gt_site_menu() {
$items['site/%'] = array(
'title' => 'test',
'page callback' => 'goPage',
'access callback' => true
);
return $items;
}
function goPage() {
$res = theme('home', array());
return $res;
}
function gt_site_theme() {
// 取得 gt_site 該 model 的 path
$path = drupal_get_path('module', 'gt_site');
$path .= '/themes';
return array(
'home' => array(
'template' => 'home',
'path' => $path)
);
}
/**
* Implements hook_help().
*/
function gt_site_help($path, $arg) {
$res = array();
foreach ($_GET as $key => $value) {
$res[] = sprintf('%s => %s', $key, $value);
}
$res[] = $path = drupal_get_path('module', 'gt_site');
$res = implode(' | ', $res);
return t($res);
}
2017年12月5日 星期二
js 筆記
let x = 1;
y = (++x, ++x);
console.log(y);
y=3
-----------------------------------------------------------------------------
js 基本類型
boolean, number, string, undefined
null is object
-----------------------------------------------------------------------------
string.match() => //正則是否用/g差別很大,/g將不顯示捕獲組,但會將符合的化為 array
regular.exec()
y = (++x, ++x);
console.log(y);
y=3
-----------------------------------------------------------------------------
js 基本類型
boolean, number, string, undefined
null is object
-----------------------------------------------------------------------------
js一些預設方法
string.slice(s, e), array.slice(s, e) => slice(切片)
string.split(符號) 分割成 array => split (分裂)
array.splice(s, l, data_1, data_2)=>splice (拼接)
string.match() => //正則是否用/g差別很大,/g將不顯示捕獲組,但會將符合的化為 array
regular.exec()
以 proxy 為主的 react 只適用於 backbone
(function (Backbone) {
var hasProto = ('__proto__' in {});
/*========================================================================*/
// debugger;
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
var arrayMethodsNameList = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
(function () {
arrayMethodsNameList.forEach(function (method) {
// cache original method
var original = arrayProto[method];
def(arrayMethods, method, function mutator() {
debugger;
var ob = this._bc_ob_;
ob.arrayMethod = method;
var args = [];
var len = arguments.length;
var canTrigger = true;
while (len--) {
args[len] = arguments[len];
}
// 遇到 [undefined].pop() 會出問題
var result = original.apply(this, args);
var add = [];
var remove = [];
debugger;
switch (method) {
case 'push':
case 'unshift':
add = args;
break;
case 'pop':
case 'shift':
remove = result;
break;
case 'splice':
add = args.slice(2);
remove = result;
break;
}
if (!Array.isArray(add)) {
add = (add === undefined ? [] : [add]);
}
if (!Array.isArray(remove)) {
remove = (remove === undefined ? [] : [remove]);
}
// 為了 proxy 而設
ob.arrayMethod = '';
ob.notify_1([method], {
add: add,
remove: remove,
key: undefined
});
return result;
});
});
})();
var arrayKeys = Object.getOwnPropertyNames(arrayMethods);
/*========================================================================*/
// Define a property
// 預設是不可枚舉
function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
};
/*========================================================================*/
function hasOwn(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}
/*========================================================================*/
/**
* Quick object check - this is primarily used to tell
* Objects from primitive values when we know the value
* is a JSON-compliant type.
*/
function isObject(obj) {
return (obj !== null && typeof obj === 'object');
}
/*========================================================================*/
// Augment an target Object or Array by intercepting
// the prototype chain using __proto__
function protoAugment(target, src, keys) {
/* eslint-disable no-proto */
target.__proto__ = src;
/* eslint-enable no-proto */
};
/*========================================================================*/
// Augment an target Object or Array by defining
// hidden properties.
function copyAugment(target, src, keys) {
debugger;
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
def(target, key, src[key]);
}
};
/*========================================================================*/
/**
* Attempt to create an observer instance for a value,
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
*/
function _observe(value, parent) {
debugger;
if (!isObject(value)) {
return value;
}
var ob;
if (hasOwn(value, '_bc_ob_') && value._bc_ob_ instanceof Backbone.Observer) {
ob = value._bc_ob_;
} else if ((Array.isArray(value) || _.isPlainObject(value)) && Object.isExtensible(value)) {
value = Backbone.Reactive(value, parent.options);
ob = value._bc_ob_;
}
ob.$link(parent);
return value;
}
////////////////////////////////////////////////////////////////////////////
Backbone.$buildBackbone(factory);
////////////////////////////////////////////////////////////////////////////
//
// Backbone.Reactive
//
// 事件有(sort, update, remove(model發出), reset)
//
////////////////////////////////////////////////////////////////////////////
function factory(root, Backbone, _, $) {
// here
Backbone.Reactive = makeDataReactive;
function makeDataReactive(value, options) {
// debugger;
if (value == null || (typeof value !== 'object')) {
throw new Error('cant be observer');
}
/*--------------------------*/
var observe = new Backbone.Observer(value, options);
def(value, '_bc_ob_', observe);
// debugger;
/*--------------------------*/
var proxy;
if (Array.isArray(value)) {
var augment = hasProto ? protoAugment : copyAugment;
// 替換 [] 的原型方法
augment(value, arrayMethods, arrayKeys);
// 插入 proxy
proxy = new Proxy(value, {
set: function (target, key, value, p) {
debugger;
var ob = target._bc_ob_;
var eventName = 'change';
var eventNameList = [];
var _value = _observe(value, ob);
/*====================================*/
if (key === 'length') {
eventName = 'reset';
}
/*====================================*/
target[key] = _value;
eventNameList.push(eventName);
if (ob.arrayMethod) {
eventNameList.push(ob.arrayMethod);
}
/*--------------------------*/
if (!ob.arrayMethod) {
ob.notify_2(key, eventNameList, {
key: key,
add: [],
remove: []
});
}
return true;
}
});
observe.proxy = proxy;
observe.$walkArray(value);
} else {
proxy = new Proxy(value, {
set: function (t, k, v, p) {
debugger;
var ob = t._bc_ob_;
var prevValue = t[k];
var newValue = _observe(v, ob);
t[k] = newValue
ob.notify_2(t, ['change'], {
key: k
});
return true;
}
});
observe.proxy = proxy;
observe.$walkPlainObject(value);
}
return proxy;
}
////////////////////////////////////////////////////////////////////////
(function (fn) {
fn.del = function () {
};
})(makeDataReactive);
////////////////////////////////////////////////////////////////////////////
//
// 數據的背後觀察者
// 放在數據的(_bc_ob_)裏,並且不可枚舉
//
// 要加入 controller 找數據底下的 _bc_ob_
//
////////////////////////////////////////////////////////////////////////////
Backbone.Observer = Observer;
function Observer(value, options) {
this.cid = _.uniqueId('observer');
/*--------------------------*/
this.triggerOptions = {};
/*--------------------------*/
this.$linkParent = new Map();
/*--------------------------*/
this.proxy;
this.value; // 只能是 object
this.prevValue;
/*--------------------------*/
this.keys = []; // 偵測 remove
this.prevKeys = [];
/*--------------------------*/
this.$listenerList = [];
this.arrayMethod = '';
this.eventName; // 只有是 array 的方法才會用到
this.options = {};
/*--------------------------*/
this.$prevChildList = [];
(function () {
// debugger;
this.options = options || {};
this.value = value;
this.keys = Object.keys(this.value);
this.$cloneValue();
}).call(this);
};
_.extend(Observer.prototype, Backbone.Events);
////////////////////////////////////////////////////////////////////////
(function () {
// proxy
this.$walkPlainObject = function (obj) {
var observe = obj._bc_ob_;
for (var key in obj) {
var child = obj[key];
var newValue = _observe(child, observe);
obj[key] = newValue
}
};
/*====================================================================*/
/**
* Observer a list of Array items.
*/
this.$walkArray = function observeArray(items) {
var observe = items._bc_ob_;
for (var i = 0, l = items.length; i < l; i++) {
var child = items[i];
var newValue = _observe(child, observe);
items[i] = newValue;
}
};
/*================================================================*/
}).call(Observer.prototype);
(function () {
this.$link = function (parent) {
// debugger;
if (!this.$linkParent.has(parent) && this.options.link) {
this.$linkParent.set(parent, true);
this.options.link.call(this, parent, this);
}
};
/*====================================================================*/
this.$cloneValue = function () {
// debugger;
if (Array.isArray(this.value)) {
this.prevValue = this.value.slice();
} else {
this.prevValue = _.extend({}, this.value);
}
this.prevKeys = Object.keys(this.value);
};
/*====================================================================*/
this.notify_1 = function (eventList, options) {
this.keys = Object.keys(this.value);
this._$notify_1(eventList, options);
this.$cloneValue();
this.reset();
};
/*====================================================================*/
// call by item
this.notify_2 = function (key, eventList, options) {
this.keys = Object.keys(this.value);
this._$notify_2(key, eventList, options);
if (!this.arrayMethod) {
this.$cloneValue();
}
this.reset();
}
/*====================================================================*/
// options =>(key, add, remove)
this._$notify_1 = function (eventList, options) {
debugger;
options = options || {};
options.eventList = eventList;
var add = options.add || [];
var remove = options.remove || [];
var key = options.key;
/*--------------------------*/
var triggerEventName = '';
// arrayMethod
if ((eventList.indexOf('sort') >= 0) || (eventList.indexOf('reverse') >= 0)) {
// sort, reverse
triggerEventName = 'sort';
} else {
triggerEventName = 'update';
}
/*--------------------------*/
var changePosition = [];
var wantTrigger = this.$compareAllValue(changePosition);
/*--------------------------*/
if (!wantTrigger) {
return;
}
/*--------------------------*/
remove.forEach(function (value, key) {
debugger;
if (_.isPlainObject(value) && (typeof value._bc_ob_ !== 'undefined')) {
var ob = value._bc_ob_;
ob.remove(this.triggerOptions, options);
}
}, this);
/*--------------------------*/
options.changePosition = changePosition;
// 通知 controller
this.$notifyListener(this.triggerOptions, triggerEventName, options);
// 通知其他註冊者
this.trigger(this.triggerOptions, triggerEventName, options);
};
/*====================================================================*/
this._$notify_2 = function (key, eventList, options) {
debugger;
options = options || {};
options.eventList = eventList;
var add = options.add || [];
var remove = options.remove || [];
var key = options.key;
var wantTrigger = true;
/*--------------------------*/
var triggerEventName = '';
// item發起
if (eventList.indexOf('change') >= 0) {
if ((eventList.indexOf('sort') >= 0) || (eventList.indexOf('reverse') >= 0)) {
wantTrigger = false;
} else {
triggerEventName = 'change';
}
} else if (eventList.indexOf('reset') >= 0) {
triggerEventName = 'reset';
remove = remove.concat(this.prevValue);
}
/*--------------------------*/
if (!wantTrigger) {
return;
}
if (this.arrayMethod) {
return;
}
var _remove = [];
if (key === 'length') {
this.$compareAllValue(_remove);
} else {
wantTrigger = this.$compareValue(key, _remove);
}
/*--------------------------*/
if (!wantTrigger) {
return;
}
/*--------------------------*/
_remove.forEach(function (_k, key) {
debugger;
var value = this.prevValue[_k];
if (_.isPlainObject(value) && (typeof value._bc_ob_ !== 'undefined')) {
var ob = value._bc_ob_;
ob.remove(this.triggerOptions, options);
}
}, this);
/*--------------------------*/
options.changePosition = _remove;
// 通知 controller
this.$notifyListener(this.triggerOptions, triggerEventName, options);
// 通知其他註冊者
this.trigger(this.triggerOptions, triggerEventName, options);
};
/*====================================================================*/
this.toJSON = function () {
return this.proxy;
};
/*================================================================*/
this.remove = function (e, options) {
debugger;
// 叫那些 controller
this.$notifyListener(e, 'remove');
this.$listenerList.length = 0;
// 叫那些跟隨者
this.trigger(e, 'remove', {});
this.stopListening();
this.off();
};
/*================================================================*/
this.reset = function () {
// debugger;
if (!this.arrayMethod) {
this.triggerOptions = {};
}
};
/*====================================================================*/
this.$compareAllValue = function (remove) {
// debugger;
/*--------------------------*/
var wantTriggerList = [];
if (Array.isArray(this.value)) {
var length = ((this.prevValue.length - this.value.length > 0) ? this.prevValue.length : this.value.length);
for (var i = 0; i < length; i++) {
debugger;
var wantTrigger = this.$compareValue(i, remove);
if (wantTrigger) {
wantTriggerList.push(i);
}
}
} else {
var keyLMap = {};
this.keys.forEach(function (k) {
keyLMap[k] = true;
});
this.prevKeys.forEach(function (k) {
keyLMap[k] = true;
});
for (var key in keyLMap) {
debugger;
var wantTrigger = this.$compareValue(key, remove);
if (wantTrigger) {
wantTriggerList.push(i);
}
}
}
/*--------------------------*/
if (wantTriggerList.length || remove.length) {
return true;
} else {
return false;
}
};
/*====================================================================*/
this.$compareValue = function (key, remove) {
// debugger;
var wantTrigger = false;
var prev = this.prevValue[key];
var current = this.value[key];
if (typeof prev !== typeof current) {
wantTrigger = true;
remove.push(key);
} else if ((prev == null && current == null) ||
(typeof prev._bc_ob_ === 'undefined' && typeof current._bc_ob_ === 'undefined')) {
// 都是簡單數據
if (!_.isEqual(prev, current)) {
wantTrigger = true;
remove.push(key);
}
} else if (prev != null && current != null &&
typeof prev._bc_ob_ !== 'undefined' &&
typeof current._bc_ob_ !== 'undefined') {
// 都不是簡單數據
if (prev._bc_ob_.cid !== current._bc_ob_.cid) {
wantTrigger = true;
remove.push(key);
}
} else {
wantTrigger = true;
remove.push(key);
}
return wantTrigger;
};
/*====================================================================*/
this.$addListener = function (controller) {
this.$listenerList.push(controller);
};
/*====================================================================*/
this.$removeListener = function (controller) {
if (controller == null) {
this.$listenerList.length = 0;
return;
}
var remain = [];
for (var i = 0, c; c = this.$listenerList[i]; i++) {
if (c.cid === controller.cid) {
continue;
}
remain.push(c);
}
this.$listenerList = remain;
};
/*====================================================================*/
this.$notifyListener = function (e, eventName, options) {
this.$listenerList.forEach(function (c, index) {
c.update(e, eventName, options);
}, this);
};
}).call(Observer.prototype);
}
})(Backbone);
var hasProto = ('__proto__' in {});
/*========================================================================*/
// debugger;
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
var arrayMethodsNameList = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
(function () {
arrayMethodsNameList.forEach(function (method) {
// cache original method
var original = arrayProto[method];
def(arrayMethods, method, function mutator() {
debugger;
var ob = this._bc_ob_;
ob.arrayMethod = method;
var args = [];
var len = arguments.length;
var canTrigger = true;
while (len--) {
args[len] = arguments[len];
}
// 遇到 [undefined].pop() 會出問題
var result = original.apply(this, args);
var add = [];
var remove = [];
debugger;
switch (method) {
case 'push':
case 'unshift':
add = args;
break;
case 'pop':
case 'shift':
remove = result;
break;
case 'splice':
add = args.slice(2);
remove = result;
break;
}
if (!Array.isArray(add)) {
add = (add === undefined ? [] : [add]);
}
if (!Array.isArray(remove)) {
remove = (remove === undefined ? [] : [remove]);
}
// 為了 proxy 而設
ob.arrayMethod = '';
ob.notify_1([method], {
add: add,
remove: remove,
key: undefined
});
return result;
});
});
})();
var arrayKeys = Object.getOwnPropertyNames(arrayMethods);
/*========================================================================*/
// Define a property
// 預設是不可枚舉
function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
};
/*========================================================================*/
function hasOwn(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}
/*========================================================================*/
/**
* Quick object check - this is primarily used to tell
* Objects from primitive values when we know the value
* is a JSON-compliant type.
*/
function isObject(obj) {
return (obj !== null && typeof obj === 'object');
}
/*========================================================================*/
// Augment an target Object or Array by intercepting
// the prototype chain using __proto__
function protoAugment(target, src, keys) {
/* eslint-disable no-proto */
target.__proto__ = src;
/* eslint-enable no-proto */
};
/*========================================================================*/
// Augment an target Object or Array by defining
// hidden properties.
function copyAugment(target, src, keys) {
debugger;
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
def(target, key, src[key]);
}
};
/*========================================================================*/
/**
* Attempt to create an observer instance for a value,
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
*/
function _observe(value, parent) {
debugger;
if (!isObject(value)) {
return value;
}
var ob;
if (hasOwn(value, '_bc_ob_') && value._bc_ob_ instanceof Backbone.Observer) {
ob = value._bc_ob_;
} else if ((Array.isArray(value) || _.isPlainObject(value)) && Object.isExtensible(value)) {
value = Backbone.Reactive(value, parent.options);
ob = value._bc_ob_;
}
ob.$link(parent);
return value;
}
////////////////////////////////////////////////////////////////////////////
Backbone.$buildBackbone(factory);
////////////////////////////////////////////////////////////////////////////
//
// Backbone.Reactive
//
// 事件有(sort, update, remove(model發出), reset)
//
////////////////////////////////////////////////////////////////////////////
function factory(root, Backbone, _, $) {
// here
Backbone.Reactive = makeDataReactive;
function makeDataReactive(value, options) {
// debugger;
if (value == null || (typeof value !== 'object')) {
throw new Error('cant be observer');
}
/*--------------------------*/
var observe = new Backbone.Observer(value, options);
def(value, '_bc_ob_', observe);
// debugger;
/*--------------------------*/
var proxy;
if (Array.isArray(value)) {
var augment = hasProto ? protoAugment : copyAugment;
// 替換 [] 的原型方法
augment(value, arrayMethods, arrayKeys);
// 插入 proxy
proxy = new Proxy(value, {
set: function (target, key, value, p) {
debugger;
var ob = target._bc_ob_;
var eventName = 'change';
var eventNameList = [];
var _value = _observe(value, ob);
/*====================================*/
if (key === 'length') {
eventName = 'reset';
}
/*====================================*/
target[key] = _value;
eventNameList.push(eventName);
if (ob.arrayMethod) {
eventNameList.push(ob.arrayMethod);
}
/*--------------------------*/
if (!ob.arrayMethod) {
ob.notify_2(key, eventNameList, {
key: key,
add: [],
remove: []
});
}
return true;
}
});
observe.proxy = proxy;
observe.$walkArray(value);
} else {
proxy = new Proxy(value, {
set: function (t, k, v, p) {
debugger;
var ob = t._bc_ob_;
var prevValue = t[k];
var newValue = _observe(v, ob);
t[k] = newValue
ob.notify_2(t, ['change'], {
key: k
});
return true;
}
});
observe.proxy = proxy;
observe.$walkPlainObject(value);
}
return proxy;
}
////////////////////////////////////////////////////////////////////////
(function (fn) {
fn.del = function () {
};
})(makeDataReactive);
////////////////////////////////////////////////////////////////////////////
//
// 數據的背後觀察者
// 放在數據的(_bc_ob_)裏,並且不可枚舉
//
// 要加入 controller 找數據底下的 _bc_ob_
//
////////////////////////////////////////////////////////////////////////////
Backbone.Observer = Observer;
function Observer(value, options) {
this.cid = _.uniqueId('observer');
/*--------------------------*/
this.triggerOptions = {};
/*--------------------------*/
this.$linkParent = new Map();
/*--------------------------*/
this.proxy;
this.value; // 只能是 object
this.prevValue;
/*--------------------------*/
this.keys = []; // 偵測 remove
this.prevKeys = [];
/*--------------------------*/
this.$listenerList = [];
this.arrayMethod = '';
this.eventName; // 只有是 array 的方法才會用到
this.options = {};
/*--------------------------*/
this.$prevChildList = [];
(function () {
// debugger;
this.options = options || {};
this.value = value;
this.keys = Object.keys(this.value);
this.$cloneValue();
}).call(this);
};
_.extend(Observer.prototype, Backbone.Events);
////////////////////////////////////////////////////////////////////////
(function () {
// proxy
this.$walkPlainObject = function (obj) {
var observe = obj._bc_ob_;
for (var key in obj) {
var child = obj[key];
var newValue = _observe(child, observe);
obj[key] = newValue
}
};
/*====================================================================*/
/**
* Observer a list of Array items.
*/
this.$walkArray = function observeArray(items) {
var observe = items._bc_ob_;
for (var i = 0, l = items.length; i < l; i++) {
var child = items[i];
var newValue = _observe(child, observe);
items[i] = newValue;
}
};
/*================================================================*/
}).call(Observer.prototype);
(function () {
this.$link = function (parent) {
// debugger;
if (!this.$linkParent.has(parent) && this.options.link) {
this.$linkParent.set(parent, true);
this.options.link.call(this, parent, this);
}
};
/*====================================================================*/
this.$cloneValue = function () {
// debugger;
if (Array.isArray(this.value)) {
this.prevValue = this.value.slice();
} else {
this.prevValue = _.extend({}, this.value);
}
this.prevKeys = Object.keys(this.value);
};
/*====================================================================*/
this.notify_1 = function (eventList, options) {
this.keys = Object.keys(this.value);
this._$notify_1(eventList, options);
this.$cloneValue();
this.reset();
};
/*====================================================================*/
// call by item
this.notify_2 = function (key, eventList, options) {
this.keys = Object.keys(this.value);
this._$notify_2(key, eventList, options);
if (!this.arrayMethod) {
this.$cloneValue();
}
this.reset();
}
/*====================================================================*/
// options =>(key, add, remove)
this._$notify_1 = function (eventList, options) {
debugger;
options = options || {};
options.eventList = eventList;
var add = options.add || [];
var remove = options.remove || [];
var key = options.key;
/*--------------------------*/
var triggerEventName = '';
// arrayMethod
if ((eventList.indexOf('sort') >= 0) || (eventList.indexOf('reverse') >= 0)) {
// sort, reverse
triggerEventName = 'sort';
} else {
triggerEventName = 'update';
}
/*--------------------------*/
var changePosition = [];
var wantTrigger = this.$compareAllValue(changePosition);
/*--------------------------*/
if (!wantTrigger) {
return;
}
/*--------------------------*/
remove.forEach(function (value, key) {
debugger;
if (_.isPlainObject(value) && (typeof value._bc_ob_ !== 'undefined')) {
var ob = value._bc_ob_;
ob.remove(this.triggerOptions, options);
}
}, this);
/*--------------------------*/
options.changePosition = changePosition;
// 通知 controller
this.$notifyListener(this.triggerOptions, triggerEventName, options);
// 通知其他註冊者
this.trigger(this.triggerOptions, triggerEventName, options);
};
/*====================================================================*/
this._$notify_2 = function (key, eventList, options) {
debugger;
options = options || {};
options.eventList = eventList;
var add = options.add || [];
var remove = options.remove || [];
var key = options.key;
var wantTrigger = true;
/*--------------------------*/
var triggerEventName = '';
// item發起
if (eventList.indexOf('change') >= 0) {
if ((eventList.indexOf('sort') >= 0) || (eventList.indexOf('reverse') >= 0)) {
wantTrigger = false;
} else {
triggerEventName = 'change';
}
} else if (eventList.indexOf('reset') >= 0) {
triggerEventName = 'reset';
remove = remove.concat(this.prevValue);
}
/*--------------------------*/
if (!wantTrigger) {
return;
}
if (this.arrayMethod) {
return;
}
var _remove = [];
if (key === 'length') {
this.$compareAllValue(_remove);
} else {
wantTrigger = this.$compareValue(key, _remove);
}
/*--------------------------*/
if (!wantTrigger) {
return;
}
/*--------------------------*/
_remove.forEach(function (_k, key) {
debugger;
var value = this.prevValue[_k];
if (_.isPlainObject(value) && (typeof value._bc_ob_ !== 'undefined')) {
var ob = value._bc_ob_;
ob.remove(this.triggerOptions, options);
}
}, this);
/*--------------------------*/
options.changePosition = _remove;
// 通知 controller
this.$notifyListener(this.triggerOptions, triggerEventName, options);
// 通知其他註冊者
this.trigger(this.triggerOptions, triggerEventName, options);
};
/*====================================================================*/
this.toJSON = function () {
return this.proxy;
};
/*================================================================*/
this.remove = function (e, options) {
debugger;
// 叫那些 controller
this.$notifyListener(e, 'remove');
this.$listenerList.length = 0;
// 叫那些跟隨者
this.trigger(e, 'remove', {});
this.stopListening();
this.off();
};
/*================================================================*/
this.reset = function () {
// debugger;
if (!this.arrayMethod) {
this.triggerOptions = {};
}
};
/*====================================================================*/
this.$compareAllValue = function (remove) {
// debugger;
/*--------------------------*/
var wantTriggerList = [];
if (Array.isArray(this.value)) {
var length = ((this.prevValue.length - this.value.length > 0) ? this.prevValue.length : this.value.length);
for (var i = 0; i < length; i++) {
debugger;
var wantTrigger = this.$compareValue(i, remove);
if (wantTrigger) {
wantTriggerList.push(i);
}
}
} else {
var keyLMap = {};
this.keys.forEach(function (k) {
keyLMap[k] = true;
});
this.prevKeys.forEach(function (k) {
keyLMap[k] = true;
});
for (var key in keyLMap) {
debugger;
var wantTrigger = this.$compareValue(key, remove);
if (wantTrigger) {
wantTriggerList.push(i);
}
}
}
/*--------------------------*/
if (wantTriggerList.length || remove.length) {
return true;
} else {
return false;
}
};
/*====================================================================*/
this.$compareValue = function (key, remove) {
// debugger;
var wantTrigger = false;
var prev = this.prevValue[key];
var current = this.value[key];
if (typeof prev !== typeof current) {
wantTrigger = true;
remove.push(key);
} else if ((prev == null && current == null) ||
(typeof prev._bc_ob_ === 'undefined' && typeof current._bc_ob_ === 'undefined')) {
// 都是簡單數據
if (!_.isEqual(prev, current)) {
wantTrigger = true;
remove.push(key);
}
} else if (prev != null && current != null &&
typeof prev._bc_ob_ !== 'undefined' &&
typeof current._bc_ob_ !== 'undefined') {
// 都不是簡單數據
if (prev._bc_ob_.cid !== current._bc_ob_.cid) {
wantTrigger = true;
remove.push(key);
}
} else {
wantTrigger = true;
remove.push(key);
}
return wantTrigger;
};
/*====================================================================*/
this.$addListener = function (controller) {
this.$listenerList.push(controller);
};
/*====================================================================*/
this.$removeListener = function (controller) {
if (controller == null) {
this.$listenerList.length = 0;
return;
}
var remain = [];
for (var i = 0, c; c = this.$listenerList[i]; i++) {
if (c.cid === controller.cid) {
continue;
}
remain.push(c);
}
this.$listenerList = remain;
};
/*====================================================================*/
this.$notifyListener = function (e, eventName, options) {
this.$listenerList.forEach(function (c, index) {
c.update(e, eventName, options);
}, this);
};
}).call(Observer.prototype);
}
})(Backbone);
訂閱:
文章 (Atom)