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