/
qgettersetter.js
130 lines (120 loc) · 4.49 KB
/
qgettersetter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//////////////////////////////////////////////////////////////////////////////////
// Implement queuable getter setter //
//////////////////////////////////////////////////////////////////////////////////
/**
* by default __defineGetter__ support only one function. Same for __defineSetter
* This is a annoying limitation. This little library declares 2 functions
* Object.__defineQGetter__ and Object.__defineQGetter__.
* They behave the same as their native sibling but support multiple functions.
* Those functions are called in the same order they got registered.
*
* (I have no idea of the reasoning behind this limitation to one function. It seems
* useless to me. This remind me of onclick of the DOM instead of a proper .addEventListener)
*/
/**
* Class to implement queueable getter/setter
* @param {Object} baseObject The base object on which we operate
* @param {String} property The string of property
*/
var QGetterSetter = {};
/**
* Define a getter/setter for a property
*
* @param {Object} baseObject the base object which is used
* @param {String} property the name of the property
*/
QGetterSetter._Property = function(baseObject, property){
// sanity check
console.assert( typeof(baseObject) === 'object' || typeof(baseObject) === 'function' );
console.assert( typeof(property) === 'string' );
// backup the initial value
var originValue = baseObject[property];
// init some local variables
var _this = this;
this._getters = [];
this._setters = [];
// the storage value
Object.defineProperty(baseObject, '__' + property, {
enumerable : false,
writable : true,
value : baseObject[property],
})
// the accessed value
Object.defineProperty(baseObject, property, {
enumerable : true,
get : function getterHandler(){
var value = baseObject['__'+property];
for(var i = 0; i < _this._getters.length; i++){
value = _this._getters[i](value, getterHandler.caller, property)
}
return value;
},
set : function setterHandler(value){
for(var i = 0; i < _this._setters.length; i++){
value = _this._setters[i](value, setterHandler.caller, property)
}
baseObject['__'+property] = value;
},
})
};
// export the class in node.js - if running in node.js
if( typeof(window) === 'undefined' ) module.exports = QGetterSetter;
/**
* init baseObject to be able to ahndle qGetterSetter
* @param {Object} baseObject the base object to modify
* @param {String} property the property which is handled
* @return {String} the created property name
*/
QGetterSetter._initObjectIfNeeded = function(baseObject, property){
var name = "__bjsGetSet_" + property;
// define the property to store all the getters/setter
if( baseObject[name] === undefined ){
Object.defineProperty(baseObject, name, {
enumerable : false,
value : new QGetterSetter._Property(baseObject, property)
});
}
return name
}
/**
* define a getter
*
* @param {Obejct} baseObject the object containing the property
* @param {string} property the property name which gonna get the getter
* @param {Function} getterFn function which handle the getter
*/
QGetterSetter.defineGetter = function(baseObject, property, getterFn){
// init QGetterSetter on this property if needed
var name = QGetterSetter._initObjectIfNeeded(baseObject, property)
// setup the new getter
baseObject[name]._getters.push(getterFn)
}
/**
* define a setter
*
* @param {Object} baseObject the object containing the property
* @param {string} property the property name which gonna get the setter
* @param {Function} setterFn function which handle the setter
*/
QGetterSetter.defineSetter = function(baseObject, property, setterFn){
// init QGetterSetter on this property if needed
var name = QGetterSetter._initObjectIfNeeded(baseObject, property)
// setup the new setter
baseObject[name]._setters.push(setterFn)
}
//////////////////////////////////////////////////////////////////////////////////
// .overloadObjectPrototype() //
//////////////////////////////////////////////////////////////////////////////////
/**
* overload the Object.prototype with .__defineQGetter__ and .__defineQSetter__
*
* TODO put that in example/js ?
*/
QGetterSetter.overloadObjectPrototype = function(){
Object.prototype.__defineQGetter__ = function(property, getterFn){
QGetterSetter.defineGetter(this, property, getterFn);
};
Object.prototype.__defineQSetter__ = function(property, setterFn){
QGetterSetter.defineSetter(this, property, setterFn);
};
}