1 | var _ = require("underscore");
|
2 |
|
3 |
|
4 | var isPlainObject =
|
5 | exports.isPlainObject = function(obj) {
|
6 | return obj != null && obj.__proto__ === Object.prototype;
|
7 | }
|
8 |
|
9 |
|
10 | var subclass =
|
11 | exports.subclass = function(protoProps, staticProps) {
|
12 | var parent = this;
|
13 | var child;
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | if (protoProps && _.has(protoProps, 'constructor')) {
|
19 | child = protoProps.constructor;
|
20 | } else {
|
21 | child = function(){ return parent.apply(this, arguments); };
|
22 | }
|
23 |
|
24 |
|
25 | _.extend(child, parent, staticProps);
|
26 |
|
27 |
|
28 |
|
29 | var Surrogate = function(){ this.constructor = child; };
|
30 | Surrogate.prototype = parent.prototype;
|
31 | child.prototype = new Surrogate;
|
32 |
|
33 |
|
34 |
|
35 | if (protoProps) _.extend(child.prototype, protoProps);
|
36 |
|
37 |
|
38 |
|
39 | child.__super__ = parent.prototype;
|
40 |
|
41 | return child;
|
42 | }
|
43 |
|
44 |
|
45 | var sanitizePathParts =
|
46 | exports.sanitizePathParts = function(parts) {
|
47 | return parts.filter(function(a) {
|
48 | return a != null && a !== "";
|
49 | }).map(function(a) {
|
50 | var s = a.toString();
|
51 | if (s[0] === ".") s = s.substr(1);
|
52 | if (s.substr(-1) === ".") s = s.substr(0, s.length - 1);
|
53 | return s;
|
54 | });
|
55 | }
|
56 |
|
57 |
|
58 | var splitPath =
|
59 | exports.splitPath = function(path) {
|
60 | var parts = _.isArray(path) ? path : _.isString(path) ? path.split(".") : [ path ];
|
61 | if (parts.length > 1 && parts[0] === "") parts[0] = "this";
|
62 | return sanitizePathParts(parts);
|
63 | }
|
64 |
|
65 |
|
66 | var parsePath =
|
67 | exports.parsePath = function(path) {
|
68 | return splitPath(path).map(function(part) {
|
69 | if (part.indexOf("*") > -1 && part !== "**") {
|
70 | return new RegExp("^" + part.split("*").join("([^\\.]*)") + "$");
|
71 | }
|
72 |
|
73 | return part;
|
74 | });
|
75 | }
|
76 |
|
77 |
|
78 | var joinPathParts =
|
79 | exports.joinPathParts = function() {
|
80 | return sanitizePathParts(_.flatten(_.toArray(arguments))).join(".");
|
81 | }
|
82 |
|
83 |
|
84 | var get =
|
85 | exports.get = function(obj, parts, getter) {
|
86 | parts = splitPath(parts);
|
87 |
|
88 |
|
89 | if (!_.isFunction(getter)) {
|
90 | getter = function(obj, path) { return obj[path]; }
|
91 | }
|
92 |
|
93 | while (parts.length) {
|
94 | if (obj == null) return;
|
95 | obj = getter(obj, parts.shift());
|
96 | }
|
97 |
|
98 | return obj;
|
99 | }
|
100 |
|
101 |
|
102 | var findShallowestUniquePaths =
|
103 | exports.findShallowestUniquePaths = function(paths) {
|
104 | return paths.reduce(function(m, keys) {
|
105 |
|
106 | if (m.some(function(k) {
|
107 | return arrayStartsWith(keys, k);
|
108 | })) return m;
|
109 |
|
110 |
|
111 | m.slice(0).forEach(function(k, index) {
|
112 | if (arrayStartsWith(k, keys)) m.splice(index, 1);
|
113 | });
|
114 |
|
115 |
|
116 | m.push(keys);
|
117 | return m;
|
118 | }, []);
|
119 | }
|
120 |
|
121 |
|
122 |
|
123 | var arrayStartsWith =
|
124 | exports.arrayStartsWith = function(a1, a2) {
|
125 | var max = a2.length;
|
126 | return max <= a1.length && _.isEqual(a2, a1.slice(0, max));
|
127 | }
|
128 |
|
129 |
|
130 | var mutatorMethods = [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift' ];
|
131 |
|
132 |
|
133 | var patchArray =
|
134 | exports.patchArray = function(arr) {
|
135 | if (arr._patched) return arr;
|
136 |
|
137 | var patchedArrayProto = [],
|
138 | observers = [];
|
139 |
|
140 | Object.defineProperty(patchedArrayProto, "_patched", { value: true });
|
141 | Object.defineProperty(patchedArrayProto, "_observers", { value: [] });
|
142 |
|
143 | Object.defineProperty(patchedArrayProto, "observe", {
|
144 | value: function(fn) {
|
145 | if (typeof fn !== "function") throw new Error("Expecting function to observe with.");
|
146 | this._observers.push(fn);
|
147 | return this;
|
148 | }
|
149 | });
|
150 |
|
151 | Object.defineProperty(patchedArrayProto, "stopObserving", {
|
152 | value: function(fn) {
|
153 | var index = this._observers.indexOf(fn);
|
154 | if (index > -1) this._observers.splice(index, 1);
|
155 | return this;
|
156 | }
|
157 | });
|
158 |
|
159 | mutatorMethods.forEach(function(methodName) {
|
160 | Object.defineProperty(patchedArrayProto, methodName, {
|
161 | value: method
|
162 | });
|
163 |
|
164 | function method() {
|
165 | var spliceEquivalent, summary, start,
|
166 | original, size, i, index, result;
|
167 |
|
168 |
|
169 |
|
170 | spliceEquivalent = getSpliceEquivalent(this, methodName, _.toArray(arguments));
|
171 | summary = summariseSpliceOperation(this, spliceEquivalent);
|
172 |
|
173 |
|
174 | if (summary != null) {
|
175 | start = summary.start;
|
176 | original = Array.prototype.slice.call(this, start, !summary.balance ? start + summary.added : void 0);
|
177 | size = (summary.balance > 0 ? summary.added : 0) + original.length;
|
178 | } else {
|
179 | start = 0;
|
180 | original = Array.prototype.slice.call(this, 0);
|
181 | size = original.length;
|
182 | }
|
183 |
|
184 |
|
185 | result = Array.prototype[methodName].apply(this, arguments);
|
186 |
|
187 |
|
188 | for (i = 0; i < size; i++) {
|
189 | index = i + start;
|
190 | this._observers.forEach(function(fn) {
|
191 | fn.call(this, index, this[index], original[i]);
|
192 | }, this);
|
193 | }
|
194 |
|
195 | return result;
|
196 | };
|
197 | });
|
198 |
|
199 | if (({}).__proto__) arr.__proto__ = patchedArrayProto;
|
200 | else {
|
201 | mutatorMethods.forEach(function(methodName) {
|
202 | Object.defineProperty(arr, methodName, {
|
203 | value: patchedArrayProto[methodName],
|
204 | configurable: true
|
205 | });
|
206 | });
|
207 | }
|
208 |
|
209 | return arr;
|
210 | }
|
211 |
|
212 |
|
213 | var getSpliceEquivalent =
|
214 | exports.getSpliceEquivalent = function ( array, methodName, args ) {
|
215 | switch ( methodName ) {
|
216 | case 'splice':
|
217 | return args;
|
218 |
|
219 | case 'sort':
|
220 | case 'reverse':
|
221 | return null;
|
222 |
|
223 | case 'pop':
|
224 | if ( array.length ) {
|
225 | return [ -1 ];
|
226 | }
|
227 | return null;
|
228 |
|
229 | case 'push':
|
230 | return [ array.length, 0 ].concat( args );
|
231 |
|
232 | case 'shift':
|
233 | return [ 0, 1 ];
|
234 |
|
235 | case 'unshift':
|
236 | return [ 0, 0 ].concat( args );
|
237 | }
|
238 | }
|
239 |
|
240 |
|
241 | var summariseSpliceOperation =
|
242 | exports.summariseSpliceOperation = function ( array, args ) {
|
243 | var start, addedItems, removedItems, balance;
|
244 |
|
245 | if ( !args ) {
|
246 | return null;
|
247 | }
|
248 |
|
249 |
|
250 | start = +( args[0] < 0 ? array.length + args[0] : args[0] );
|
251 |
|
252 |
|
253 | addedItems = Math.max( 0, args.length - 2 );
|
254 | removedItems = ( args[1] !== undefined ? args[1] : array.length - start );
|
255 |
|
256 |
|
257 |
|
258 |
|
259 | removedItems = Math.min( removedItems, array.length - start );
|
260 |
|
261 | balance = addedItems - removedItems;
|
262 |
|
263 | return {
|
264 | start: start,
|
265 | balance: balance,
|
266 | added: addedItems,
|
267 | removed: removedItems
|
268 | };
|
269 | }
|
270 |
|
271 |
|
272 | exports.matchSelector = function(node, selector) {
|
273 | var nodes, i;
|
274 |
|
275 | nodes = ( node.parentNode || node.document ).querySelectorAll( selector );
|
276 |
|
277 | i = nodes.length;
|
278 | while ( i-- ) {
|
279 | if ( nodes[i] === node ) {
|
280 | return true;
|
281 | }
|
282 | }
|
283 |
|
284 | return false;
|
285 | }
|
286 |
|
287 |
|
288 | exports.parseTag = function(str) {
|
289 | return require("./parse/tag-parser.pegjs").parse(str);
|
290 | }
|
291 |
|
292 |
|
293 |
|
294 | exports.changeType = function(nval, oval) {
|
295 | return _.isUndefined(oval) ? "add" : _.isUndefined(nval) ? "delete" : "update";
|
296 | } |
\ | No newline at end of file |