UNPKG

15.4 kBJavaScriptView Raw
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 // IE8: var a=function L(){a!==L}
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 /* istanbul ignore next */
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 //delete item.constructor.cache[item.get("id")]
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 /* istanbul ignore next */
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 // Fast lookup for last position
485 if (compFn && l && compFn(el, arr[l-1]) < 1) {
486 // Otherwise binary search
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 // Date{day=1,2}
515 // sliceable[start:stop:step]
516 // Geo{distance=200km&lat=40&lon=-70}
517 // ?pos=Geo{distance=200km&lat=@lat&lon=@lon}
518 // [lon, lat] in The GeoJSON Format RFC 7946
519 // IP{net=127.0.0.1/30}
520 // var re = /^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])(?:\.(?=.)|$)){4}$/
521 // ["1.2.3.4", "127.0.0.1", "192.175.255.254."].map(re.test, re)
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