1 |
|
2 |
|
3 |
|
4 | !function(exports, Object) {
|
5 | "use strict";
|
6 | var arrProto = Array.prototype
|
7 | , getFns = Object.create(null)
|
8 | , setFns = Object.create(null)
|
9 | , filterFns = Object.create(null)
|
10 | , escRe = /['\n\r\u2028\u2029]|\\(?!x2e)/g
|
11 | , pathRe = /(^$|.+?)(?:\[([^\]]*)\]|\{([^}]*)})?(\.(?=[^.])|$)/g
|
12 | , reEscRe = /[.+^=:${}()|\/\\]/g
|
13 | , globRe = /[?*]|\[\w+\]/
|
14 | , globReplace = /\?|(?=\*)/g
|
15 | , globGroup = /\[!(?=.*\])/g
|
16 | , primitiveRe = /^(-?(\d*\.)?\d+|true|false|null)$/
|
17 | , valRe = /("|')(\\?.)*?\1|(\w*)\{(("|')(?:\\?.)*?\5|\w*\{(?:("|')(?:\\?.)*?\6|[^}])*?\}|.)*?\}|(@?)[^,]+/g
|
18 | , filterRe = /(!?)(\$?)((?:[-+:.\/\w]+|\[[^\]]+\]|\{[^}]+}|\\x2e)+)(\[]|\{}|)(?:(!(?=\1)==?|(?=\1)[<>=]=?)((?:("|')(?:\\?.)*?\7|\w*\{(?:("|')(?:\\?.)*?\8|\w*\{(?:("|')(?:\\?.)*?\9|[^}])*?\}|.)*?\}|[^|&()])*))?(?=[)|&]|$)|((&|\|)\11*|([()])|.)/g
|
19 | , onlyFilterRe = RegExp("^(?:([<>=])\\d+|" + filterRe.source.slice(0, -10) + "))+$")
|
20 | , cleanRe = /(\(o=d\)&&(?!.*o=o).*)\(o=d\)&&/g
|
21 | , nameRe = / (\w+)/
|
22 | , fns = {
|
23 | "==": "e",
|
24 | "===": "s",
|
25 | ">": "g",
|
26 | ">=": "ge",
|
27 | "<": "l",
|
28 | "<=": "le"
|
29 | }
|
30 | , equal = Fn("a->b->a==b")
|
31 | , strictEqual = Fn("a->b->a===b")
|
32 | , greater = Fn("a->b->a<b")
|
33 | , greaterEqual = Fn("a->b->a<=b")
|
34 | , less = Fn("a->b->a>b")
|
35 | , lessEqual = Fn("a->b->a>=b")
|
36 | , reMatch = Fn("a->b->typeof b==='string'&&a.test(b)")
|
37 | , tmpDate = new Date()
|
38 | , isArray = Array.isArray
|
39 |
|
40 | exports.Item = Item
|
41 | exports.List = List
|
42 |
|
43 | Item.filterFn = filterFn
|
44 | filterFn.re = filterRe
|
45 | Item.copy = copy
|
46 | Item.extend = List.extend = extend
|
47 | Item.cache = {}
|
48 | List.cache = {}
|
49 |
|
50 | Item.get = function(obj, pointer) {
|
51 | return pathFn(pointer)(obj)
|
52 | }
|
53 | Item.get.str = pathStr
|
54 |
|
55 | Item.set = function(obj, pointer, value) {
|
56 | return pathFn(pointer, true)(obj, value)
|
57 | }
|
58 |
|
59 | function escFn(str) {
|
60 | return escape(str).replace(/%u/g, "\\u").replace(/%/g, "\\x")
|
61 | }
|
62 |
|
63 | function extend(a, b, c, d, e) {
|
64 |
|
65 | var fn = this
|
66 | , str = fn.toString()
|
67 | , clone = Function(str + ";return " + str.match(nameRe)[1])()
|
68 | , _super = fn.prototype
|
69 | clone.prototype = Object.assign(
|
70 | isArray(_super) ? [] : Object.create(null),
|
71 | _super, a, b, c, d, e, {_super: _super, constructor: clone}
|
72 | )
|
73 | clone.extend = extend
|
74 | clone.cache = clone.prototype.cache || fn.cache
|
75 | return clone
|
76 | }
|
77 |
|
78 |
|
79 | function Item(attrs, opts) {
|
80 | var item = this
|
81 | if (attrs instanceof Item) {
|
82 | return attrs
|
83 | }
|
84 | if (!(item instanceof Item)) {
|
85 | return Item.cache[attrs.id] || (Item.cache[attrs.id] = new Item(attrs, opts))
|
86 | }
|
87 | item.data = attrs
|
88 | item.lists = []
|
89 | if (item.init) {
|
90 | item.init(attrs, opts)
|
91 | }
|
92 | item.set(attrs)
|
93 | }
|
94 |
|
95 | Item.prototype = {
|
96 | constructor: Item,
|
97 | set: function(key, val, opts) {
|
98 | var item = this
|
99 | , changed = []
|
100 | , previous = {}
|
101 | , data = key
|
102 |
|
103 | if (typeof key !== "object") {
|
104 | pathFn(key, true)(data = {}, val)
|
105 | } else {
|
106 | opts = val
|
107 | }
|
108 |
|
109 | if (item.validate(data, opts)) {
|
110 | JSON.mergePatch(item.data, data, changed, previous)
|
111 | }
|
112 |
|
113 | if (changed.length) {
|
114 | item.emit("change", changed, item, opts, previous)
|
115 | changed.each(function(pointer, idx) {
|
116 | item.emit("change:" + pointer, item.get(pointer), item, opts, previous[pointer])
|
117 | })
|
118 | }
|
119 | return changed
|
120 | },
|
121 | get: function(name, fallback) {
|
122 | var val = pathFn(name)(this.data)
|
123 | return val == void 0 ? fallback : val
|
124 | },
|
125 | validate: returnTrue,
|
126 | destroy: function(next) {
|
127 | var item = this
|
128 | , arr = item.lists.slice()
|
129 | , len = arr.length
|
130 |
|
131 | for (; len--; ) {
|
132 | arr[len].remove(item)
|
133 | }
|
134 |
|
135 | item.emit("destroy")
|
136 | item.off()
|
137 | item.constructor.cache[item.get("id")] = null
|
138 |
|
139 | },
|
140 | matches: function(expr) {
|
141 | return filterFn(expr)(this.data)
|
142 | },
|
143 | each: function(path, fn, scope) {
|
144 | Object.each(this.get(path), fn, scope || this)
|
145 | return this
|
146 | },
|
147 | then: _then,
|
148 | toJSON: function(attrs) {
|
149 | return isArray(attrs) ? copy({}, this.data, attrs) : this.data
|
150 | }
|
151 | }
|
152 | Event.asEmitter(Item.prototype)
|
153 |
|
154 |
|
155 | function List(name, opts) {
|
156 | var list = this
|
157 | if (!(list instanceof List)) {
|
158 | return List.cache[name] || (List.cache[name] = new List(name, opts))
|
159 | }
|
160 | list.name = name
|
161 | if (list.init) {
|
162 | list.init(name, opts)
|
163 | }
|
164 | list.on("newListener", function(type, fn, scope, origin) {
|
165 | if (type.split(":")[0] === "change") {
|
166 | list.eachLive(add, remove)
|
167 | list.on("removeListener", off)
|
168 | }
|
169 | function add(item) {
|
170 | item.on(type, fn, scope, origin)
|
171 | }
|
172 | function remove(item) {
|
173 | item.off(type, fn, scope, origin)
|
174 | }
|
175 | function off(_type, _fn, _scope, _origin) {
|
176 | if (_type === type && _fn == fn && _scope == scope && _origin == origin) {
|
177 | list.each(remove).off("add", add).off("remove", remove)
|
178 | list.off("removeListener", off)
|
179 | }
|
180 | }
|
181 | })
|
182 | }
|
183 |
|
184 | Object.assign(List.prototype = [], Event.Emitter.prototype, {
|
185 | item: Item,
|
186 | wait: Fn.hold,
|
187 | syncMethods: [
|
188 | "add", "emit", "filterFn", "merge",
|
189 | "item", "paged", "pluck", "remove", "removeAll", "sortFn"
|
190 | ],
|
191 | add: function(item) {
|
192 | var pos
|
193 | , list = this
|
194 |
|
195 | if (item) {
|
196 | if (item.constructor === Object) item = list.item(item)
|
197 |
|
198 | if (item.lists.indexOf(list) == -1) {
|
199 | pos = indexFor(list, item, list.sortFn)
|
200 | if (list.filterFn(item, pos)) {
|
201 | item.lists.push(list)
|
202 | list.splice(pos , 0, item)
|
203 | list.emit("add", item, pos, true)
|
204 | }
|
205 | }
|
206 | }
|
207 | return list
|
208 | },
|
209 | filterFn: returnTrue,
|
210 | remove: function(item) {
|
211 | var list = this
|
212 | if (item && item.lists && item.lists.remove(list) > -1) {
|
213 | list.emit("remove", item, arrProto.remove.call(list, item), false)
|
214 | }
|
215 | return list
|
216 | },
|
217 | removeAll: function(){
|
218 | for (var list = this, len = list.length; len--; ) {
|
219 | list.remove(list[len])
|
220 | }
|
221 | return list
|
222 | },
|
223 | each: function(fn, scope) {
|
224 | arrProto.each.call(this, fn, scope || this)
|
225 | return this
|
226 | },
|
227 | eachLive: function(start, stop, scope) {
|
228 | this
|
229 | .on("add", start, scope)
|
230 | .on("remove", stop, scope)
|
231 | .slice(0)
|
232 | .each(start, scope)
|
233 | return this
|
234 | },
|
235 | get: function(id, next, scope) {
|
236 | var list = this
|
237 | , item = list.item.cache[id]
|
238 |
|
239 | if (item && item.lists.indexOf(list) != -1) {
|
240 | return next ?
|
241 | (next.call(scope || list, null, item), list) :
|
242 | item
|
243 | }
|
244 |
|
245 | next && next.call(scope || list, Error("Item[#" + id + "] not found in " + list))
|
246 | },
|
247 | at: function(index, next, scope) {
|
248 | var list = this
|
249 | , item = list[index < 0 ? list.length + index : index]
|
250 | , err = item ? null : Error("Item not found at " + index + " in " + list)
|
251 |
|
252 | return next ?
|
253 | (next.call(scope || list, err, item), list) :
|
254 | item
|
255 | },
|
256 | then: _then,
|
257 | pluck: function(field, next, scope) {
|
258 | var list = this
|
259 | , arr = arrProto.map.call(list, function(item) {
|
260 | return item.get(field)
|
261 | })
|
262 | return next ?
|
263 | (next.call(scope || list, arr), list) :
|
264 | arr
|
265 | },
|
266 | set: function(key, val) {
|
267 | this.each(function(item) {
|
268 | item.set(key, val)
|
269 | })
|
270 | return this
|
271 | },
|
272 | merge: function(mergeList) {
|
273 | var list = this
|
274 | , lists = list.lists || (list.lists = [])
|
275 |
|
276 | if (mergeList && lists.indexOf(mergeList) == -1) {
|
277 | lists.push(mergeList)
|
278 |
|
279 | mergeList
|
280 | .eachLive(list.add, _remove, list)
|
281 | .on("change", _change, list)
|
282 | .then(list.wait())
|
283 | }
|
284 |
|
285 | return list
|
286 | },
|
287 | unmerge: function(mergedList) {
|
288 | var list = this
|
289 | , lists = list.lists
|
290 | , idx = lists && lists.indexOf(mergedList)
|
291 |
|
292 | if (lists && mergedList && idx != -1) {
|
293 | lists.splice(idx, 1)
|
294 |
|
295 | mergedList
|
296 | .each(_remove, list)
|
297 | .off("add", list.add, list)
|
298 | .off("remove", _remove, list)
|
299 | .off("change", _change, list)
|
300 | }
|
301 | return list
|
302 | },
|
303 | paged: function(num) {
|
304 | var list = this
|
305 | , view = new List()
|
306 | , start = 0
|
307 | , end = num
|
308 |
|
309 | view.filterFn = function(item, pos) {
|
310 | pos = list.indexOf(item)
|
311 | return pos >= start && pos < end
|
312 | }
|
313 |
|
314 | view.pages = new List()
|
315 | view.setPage = function(newPage) {
|
316 | var lastPage = 0 | (list.length / num)
|
317 | if (newPage > lastPage) {
|
318 | newPage = lastPage
|
319 | }
|
320 | view.page = newPage
|
321 | start = num * newPage
|
322 | end = start + num
|
323 | view.reFilter()
|
324 | }
|
325 |
|
326 | view.merge(list)
|
327 |
|
328 | return view
|
329 | },
|
330 | reFilter: function() {
|
331 | var list = this
|
332 | , lists = list.lists || (list.lists = [])
|
333 |
|
334 | lists.each(function(parent) {
|
335 | parent.each(function(item) {
|
336 | _change.call(list, null, item)
|
337 | })
|
338 | })
|
339 | },
|
340 | reSort: function(fn) {
|
341 | var item
|
342 | , list = this
|
343 | , len = list.length
|
344 | , removed = []
|
345 |
|
346 | if (typeof fn === "string") {
|
347 | fn = [fn]
|
348 | }
|
349 |
|
350 | if (isArray(fn)) {
|
351 | fn = Fn("a b->" + fn.map(function(key) {
|
352 | return "(A=a.get('" + key + "'))<(B=b.get('" + key + "'))?-1:A>B?1:0"
|
353 | }).join("||"))
|
354 | }
|
355 |
|
356 | fn = list.sortFn = fn || list.sortFn
|
357 |
|
358 | for (; len > 1; ) {
|
359 | item = list[--len - 1]
|
360 | if (fn(item, list[len]) > 0) {
|
361 | removed.push(item)
|
362 | list.remove(item)
|
363 | }
|
364 | }
|
365 | removed.each(list.add, list)
|
366 | },
|
367 | toJSON: function() {
|
368 | return this.slice(0)
|
369 | }
|
370 | })
|
371 |
|
372 | function _remove(item) {
|
373 | if (item.lists.every(notIn, this.lists)) {
|
374 | this.remove(item)
|
375 | }
|
376 | }
|
377 |
|
378 | function _change(changed, item) {
|
379 | var list = this
|
380 | , matches = list.filterFn(item)
|
381 | , inList = item.lists.indexOf(list) > -1
|
382 |
|
383 | if (matches && !inList) {
|
384 | list.add(item)
|
385 | }
|
386 | if (!matches && inList) {
|
387 | list.remove(item)
|
388 | }
|
389 | }
|
390 |
|
391 | function pathStr(str, set) {
|
392 | return (
|
393 | str.charAt(0) === "/" ?
|
394 | str.slice(1).replace(/\./g, "\\x2e").replace(/\//g, ".").replace(/~1/g, "/").replace(/~0/g, "~") :
|
395 | str
|
396 | )
|
397 | .replace(escRe, escFn)
|
398 | .replace(pathRe, set === true ? pathSet : pathGet)
|
399 | }
|
400 |
|
401 | function pathGet(str, path, arr, obj, dot) {
|
402 | var v = dot ? "(o=" : "(c="
|
403 | , sub = arr || obj
|
404 | if (sub && !(sub = onlyFilterRe.exec(sub))) throw Error("Invalid GET filter")
|
405 | return (
|
406 | sub ?
|
407 | pathGet(0, path, 0, 0, 1) + (arr ? "i" : "j") + "(o)&&" + v + (
|
408 | sub[1] ? (arr ? "o" : "Object.keys(o)") + ".length" + (sub[1] == "=" ? "=" : "") + sub[0] :
|
409 | +arr == arr ? "o[" + (arr < 0 ? "o.length" + arr : arr) + "]" :
|
410 | (arr ? "I" : "J") + "(o,f('" + sub[0] + "'))"
|
411 | ) + ")" :
|
412 | v + "o['" + path + (
|
413 | arr === "" ? "'])&&i(c)&&c" :
|
414 | obj === "" ? "'])&&j(c)&&c" :
|
415 | "'])"
|
416 | )
|
417 | ) + (dot ? "&&" : "")
|
418 | }
|
419 |
|
420 | function pathSet(str, path, arr, obj, dot) {
|
421 | var op = "o['" + path + "']"
|
422 | , out = ""
|
423 | , sub = arr || obj
|
424 | if (sub) {
|
425 | out = "(o="+(arr?"i":"j")+"(o['" + path + "'])?o['" + path + "']:(o['" + path + "']="+(arr?"[]":"{}")+"))&&"
|
426 | if (arr === "-") {
|
427 | op = "o[o.length]"
|
428 | } else if (+arr == arr) {
|
429 | op = "o[" + (arr < 0 ? "o.length" + arr : arr) + "]"
|
430 | } else {
|
431 | if (!onlyFilterRe.test(arr)) throw Error("Invalid SET filter")
|
432 | op = "o[t]"
|
433 | out += "(t="+(arr?"I":"J")+"(o,f('" + sub + "'),1))!=null&&"
|
434 | }
|
435 | }
|
436 | return out + (dot ?
|
437 | "(o=typeof " + op + "==='object'&&" + op + "||(" + op + "={}))&&" :
|
438 | "((c=" + op + "),(" + op + "=v),c)"
|
439 | )
|
440 | }
|
441 |
|
442 | function pathFn(str, set) {
|
443 | var map = set === true ? setFns : getFns
|
444 | return (
|
445 | map[str] ||
|
446 | (map[str] = Function("i,j,I,J,f", "return function(d,v){var c,o,t;return (o=d)&&" + pathStr(str, set) + "||c}")(
|
447 | isArray, isObject, inArray, inObject, filterFn
|
448 | ))
|
449 | )
|
450 | }
|
451 |
|
452 | function copy(a, b, attrs) {
|
453 | var len = b && attrs && attrs.length
|
454 | return len ? (
|
455 | copy[len] ||
|
456 | (copy[len] = Function("a,b,k,g", "var v,u;return " + Object.keys(attrs).map(copyFnI) + ",a"))
|
457 | )(a, b, attrs, pathFn) : a
|
458 | }
|
459 |
|
460 | function copyFnI(i) {
|
461 | return "(v=g(k[" + i + "])(b))!==u&&g(k[" + i + "],true)(a,v)"
|
462 | }
|
463 |
|
464 | function returnTrue() {
|
465 | return true
|
466 | }
|
467 |
|
468 | function _then(next, scope) {
|
469 | if (typeof next === "function") {
|
470 | next.call(scope || this)
|
471 | }
|
472 | return this
|
473 | }
|
474 |
|
475 | function notIn(v) {
|
476 | return this.indexOf(v) == -1
|
477 | }
|
478 |
|
479 | function indexFor(arr, el, compFn) {
|
480 | var o
|
481 | , i = 0
|
482 | , l = arr.length
|
483 |
|
484 |
|
485 | if (compFn && l && compFn(el, arr[l-1]) < 1) {
|
486 |
|
487 | for (; i < l; ) {
|
488 | compFn(el, arr[o=(i+l)>>1]) < 0 ? l=o : i=o+1
|
489 | }
|
490 | }
|
491 | return l
|
492 | }
|
493 |
|
494 | function filterFn(str, prefix, opts, getter, tmp) {
|
495 | var optimized
|
496 | , arr = []
|
497 | , key = (prefix || "") + filterStr(str, opts, arr, getter)
|
498 | , fn = filterFns[key]
|
499 | if (!fn) {
|
500 | for (optimized = key; optimized != (optimized = optimized.replace(cleanRe, "$1")); );
|
501 | fn = filterFns[key] = Function(
|
502 | "a,i,j,I,J,f,p,e,s,g,ge,l,le,m,t",
|
503 | "return function(d){var o;return " + optimized + "}"
|
504 | )
|
505 | }
|
506 | return fn(
|
507 | arr, isArray, isObject, inArray, inObject, filterFn, pathFn,
|
508 | equal, strictEqual, greater, greaterEqual, less, lessEqual, reMatch, tmp
|
509 | )
|
510 | }
|
511 | filterFn.str = filterStr
|
512 | filterFn.valRe = valRe
|
513 |
|
514 |
|
515 |
|
516 |
|
517 |
|
518 |
|
519 |
|
520 |
|
521 |
|
522 |
|
523 | filterFn.date = function(str) {
|
524 | return filterFn(str, "(t.setTime(+d)>=0)&&", null, dateGetter, tmpDate)
|
525 | }
|
526 |
|
527 | function dateGetter(name) {
|
528 | return "(t.get" + Date.fnMap[name] + ")"
|
529 | }
|
530 |
|
531 | function filterStr(qs, opts, arr, getter) {
|
532 | return qs.replace(filterRe, worker).replace(/^[1&]&+|&+1?$/g, "") || "1"
|
533 |
|
534 | function worker(all, not, isOption, attr, isArray, op, val, q1, q2, q3, ext, ok2, ok1) {
|
535 | if (ext) {
|
536 | if (!ok2 && !ok1) {
|
537 | throw new Error("Invalid filter: " + qs)
|
538 | }
|
539 | return ok2 ? ok2 + ok2 : ok1
|
540 | }
|
541 | if (isOption) {
|
542 | if (opts) opts[attr] = val
|
543 | return "1"
|
544 | }
|
545 |
|
546 | var idd, m, v, isRe
|
547 | , a = []
|
548 | , pre = "(o=d)&&"
|
549 |
|
550 | attr = (getter || pathStr)(attr)
|
551 |
|
552 | if (m = attr.match(/\(c=(.*?)\)$/)) {
|
553 | if (m.index) pre += attr.slice(0, m.index)
|
554 | attr = m[1]
|
555 | }
|
556 |
|
557 | if (op == "!=" || op == "!==") {
|
558 | not = "!"
|
559 | op = op.slice(1)
|
560 | }
|
561 | if (isArray) {
|
562 | pre += not + (isArray === "[]" ? "i(" : "j(") + attr + ")"
|
563 | }
|
564 |
|
565 | if (!op) {
|
566 | return isArray === "" ? pre + not + attr : pre
|
567 | }
|
568 |
|
569 | if (op == "=" || op == "==") op += "="
|
570 | if (val === "") val="''"
|
571 | for (; m = valRe.exec(val); ) {
|
572 | isRe = 0
|
573 | idd = arr.indexOf(v =
|
574 | m[1] || m[4] ? m[0].slice(m[3] ? m[3].length + 1 : 1, -1) :
|
575 | m[7] ? m[0].slice(1) :
|
576 | primitiveRe.test(m[0]) ? JSON.parse(m[0]) :
|
577 | (isRe = globRe.test(m[0])) ? RegExp(
|
578 | "^" + m[0]
|
579 | .replace(reEscRe, "\\$&")
|
580 | .replace(globReplace, ".")
|
581 | .replace(globGroup, "[^") + "$",
|
582 | op === "==" ? "i" : ""
|
583 | ) :
|
584 | m[0]
|
585 | )
|
586 | v = "a[" + (idd > -1 ? idd : arr.push(v) - 1) + "]"
|
587 | idd = (
|
588 | m[3] ? "f." + m[3].toLowerCase() :
|
589 | m[4] ? "f" :
|
590 | isRe ? "m" : fns[op]
|
591 | ) + "(" + v
|
592 | a.push(
|
593 | isArray ? (isArray === "[]" ? "I(" : "J(") + attr + "," + idd + "))" :
|
594 | isRe ? "typeof " + attr + "==='string'&&" + v + ".test(" + attr + ")" :
|
595 | m[3] || m[4] ? idd + ")(" + attr + ")" :
|
596 | m[7] ? attr + "!==void 0&&" + attr + op + "p(" + v + ")(o)" :
|
597 | attr + op + v
|
598 | )
|
599 | }
|
600 |
|
601 | return pre + (
|
602 | isArray ? (not ? "||" : "&&") : ""
|
603 | ) + not + "(" + a.join("||") + ")"
|
604 | }
|
605 | }
|
606 |
|
607 | function isObject(obj) {
|
608 | return obj != null && obj.constructor === Object
|
609 | }
|
610 |
|
611 | function inArray(a, fn, idx) {
|
612 | for (var i = -1, len = a.length; ++i < len; ) {
|
613 | if (fn(a[i])) return idx == null ? a[i] : i
|
614 | }
|
615 | return idx != null && len
|
616 | }
|
617 |
|
618 | function inObject(o, fn, idx) {
|
619 | for (var key in o) {
|
620 | if (fn(o[key])) return idx == null ? o[key] : key
|
621 | }
|
622 | return null
|
623 | }
|
624 | }(this, Object)
|
625 |
|
626 |
|
627 |
|