@litejs/ui 24.7.0 → 25.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ui.js CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /* litejs.com/MIT-LICENSE.txt */
3
3
 
4
- /* global xhr, navigator */
4
+ /* global escape, navigator, xhr */
5
5
 
6
6
  /*** debug ***/
7
7
  console.log("LiteJS is in debug mode, but it's fine for production")
@@ -11,18 +11,23 @@ console.log("LiteJS is in debug mode, but it's fine for production")
11
11
  window.El = El
12
12
  asEmitter(window.LiteJS = LiteJS)
13
13
 
14
- var UNDEF, lastExp, parser, pushBase, styleNode
14
+ var UNDEF, parser, pushBase, styleNode
15
+ , NUL = null
15
16
  , html = document.documentElement
16
17
  , body = document.body
17
18
  , splitRe = /[,\s]+/
18
19
  , emptyArr = []
20
+ , plugins = {}
21
+ , sources = []
19
22
  , assign = Object.assign
23
+ , bind = El.bind.bind(El.call)
20
24
  , create = Object.create
25
+ , hasOwn = bind(plugins.hasOwnProperty)
21
26
  , isArr = Array.isArray
22
27
  , slice = emptyArr.slice
23
- , elReplace = Function("a,b,c", "(c=a&&b&&a.parentNode)&&c.replaceChild(b,a)")
24
- , elRm = Function("e,k", "(k=e&&e.parentNode)&&k.removeChild(e)")
25
- , getAttr = Function("e,k", "return e&&e.getAttribute&&e.getAttribute(k)")
28
+ , elReplace = Function("a,b,c", "a&&b&&(c=a.parentNode)&&c.replaceChild(b,a)")
29
+ , elRm = Function("a,b", "a&&(b=a.parentNode)&&b.removeChild(a)")
30
+ , getAttr = Function("a,b", "return a&&a.getAttribute&&a.getAttribute(b)")
26
31
  , replace = Function("a,b,c", "return a.replace(b,c)")
27
32
 
28
33
  // JScript engine in IE8 and below does not recognize vertical tabulation character `\v`.
@@ -32,24 +37,22 @@ console.log("LiteJS is in debug mode, but it's fine for production")
32
37
  , BIND_ATTR = "data-bind"
33
38
  , elSeq = 0
34
39
  , elCache = {}
35
- , formatRe = /{((?:("|')(?:\\\2|[\s\S])*?\2|[^"'{}])+?)}/g
36
- , renderRe = /[;\s]*([-\w$]+)(?:([ :!])((?:(["'\/])(?:\\.|[^\\])*?\4|[^;])*))?/g
40
+ , renderRe = /[;\s]*([-.\w$]+)(?:(!?)[ :]*((?:(["'\/])(?:\\.|[^\\])*?\4|[^;])*))?/g
37
41
  , selectorRe = /([.#:[])([-\w]+)(?:([~^$*|]?)=(("|')(?:\\.|[^\\])*?\5|[-\w]+))?]?/g
38
- , templateRe = /([ \t]*)(%?)((?:("|')(?:\\.|[^\\])*?\4|[-\w:.#[\]~^$*|]=?)*) ?([\/>+=@^;]|)(([\])}]?).*?([[({]?))(?=\x1f|\n|$)+/g
39
42
  , fnCache = {}
40
43
  , fnRe = /('|")(?:\\.|[^\\])*?\1|\/(?:\\.|[^\\])+?\/[gim]*|\$el\b|\$[aos]\b|\b(?:false|in|if|new|null|this|true|typeof|void|function|var|else|return)\b|\.\w+|\w+:/g
41
44
  , wordRe = /[a-z_$][\w$]*/ig
42
45
  , camelRe = /\-([a-z])/g
43
46
  // innerText is implemented in IE4, textContent in IE9, Node.text in Opera 9-10
44
47
  // Safari 2.x innerText results an empty string when style.display=="none" or Node is not in DOM
45
- , txtAttr = "textContent" in html ? "textContent" : "innerText"
48
+ , txtAttr = "textContent" in html ? "textContent" /* c8 ignore next */ : "innerText"
46
49
  , bindingsCss = acceptMany(function(el, key, val) {
47
50
  el.style[replace(key, camelRe, camelFn)] = val
48
51
  })
49
52
  , bindingsOn = acceptMany(addEvent, function(el, val, selector, data) {
50
53
  return isStr(val) ? function(e) {
51
54
  var target = selector ? closest(e.target, selector) : el
52
- if (target) emit.apply(elScope(el).$ui, [val, e, target].concat(data))
55
+ if (target) emit.apply(target, [elScope(el).$ui, val, e, target].concat(data))
53
56
  } :
54
57
  selector ? function(e, a1, a2) {
55
58
  if (matches(e.target, selector)) val(e, a1, a2)
@@ -63,14 +66,10 @@ console.log("LiteJS is in debug mode, but it's fine for production")
63
66
  set: acceptMany(setAttr),
64
67
  txt: elTxt,
65
68
  val: elVal,
66
- view: function(el, url) {
67
- setAttr(el, "href", (pushBase || "#") + expand(url || ""))
68
- }
69
69
  }
70
+ , bindOnce = []
70
71
  , globalScope = {
71
72
  El: El,
72
- _: format,
73
- _f: format,
74
73
  $b: bindings
75
74
  }
76
75
  , elArr = {
@@ -89,47 +88,96 @@ console.log("LiteJS is in debug mode, but it's fine for production")
89
88
  return deep
90
89
  }
91
90
  }
92
- , plugins = {}
93
- , sources = []
94
- , hasOwn = plugins.hasOwnProperty
95
91
 
96
92
  , Event = window.Event || window
97
93
  , fixEv = Event.fixEv || (Event.fixEv = {})
98
94
  , fixFn = Event.fixFn || (Event.fixFn = {})
99
95
 
96
+ /*** markup ***/
97
+ , blockRe = /^(?:(=+|>| -) ([\s\S]+)|\[! *(\S*) *!] ?(.*))/
98
+ , tags = {
99
+ " -": "ul",
100
+ "!": "a",
101
+ "*": "b",
102
+ "+": "ins",
103
+ ",": "sub",
104
+ "-": "del",
105
+ "/": "i",
106
+ ":": "mark",
107
+ ";": "span",
108
+ ">": "blockquote",
109
+ "@": "time",
110
+ "^": "sup",
111
+ "_": "u",
112
+ "`": "code",
113
+ "~": "s"
114
+ }
115
+ function inline(tag, op, text, name, link, attr) {
116
+ return op && !isArr(text) ? "<" +
117
+ (tag = tags[op] || "h" + op.length) +
118
+ (tag == "a" ? " href=\"" + (link || text) + "\"" : op == "@" ? " datetime=\"" + name + "\"" : "") +
119
+ (attr ? " class=\"" + attr.slice(1) + "\">" : ">") +
120
+ (
121
+ op === ">" ? doc(replace(text, /^> ?/gm, "")) :
122
+ tag == "ul" ? "<li>" + text.split(/\n - (?=\S)/).map(inline).join("</li>\n<li>") + "</li>" :
123
+ inline(tag == "a" ? replace(name, /^\w+:\/{0,2}/, "") : text)
124
+ ) +
125
+ "</" + tag + ">" :
126
+ replace(tag, /\[([-!*+,/:;@^_`~])((.+?)(?: (\S+?))?)\1(\.[.\w]+)?]/g, inline)
127
+ }
128
+ function block(tag, op, text, media, alt) {
129
+ return op && !isArr(text) ? inline(tag, op, text) :
130
+ media ? "<img src=\"" + media + "\" alt=\"" + alt + "\">" :
131
+ blockRe.test(tag) ? replace(tag, blockRe, block) :
132
+ tag === "---" ? "<hr>" : "<p>" + inline(tag) + "</p>"
133
+ }
134
+ function doc(txt) {
135
+ return replace(txt.trim(), /^ \b/gm, "<br>").split(/\n\n+/).map(block).join("\n")
136
+ }
137
+ bindings.t = function(el, text) {
138
+ el.innerHTML = inline(replace(text, /</g, "&lt;"))
139
+ }
140
+ bindings.d = function(el, text) {
141
+ el.innerHTML = doc(replace(text, /</g, "&lt;"))
142
+ }
143
+ /**/
144
+
100
145
  /*** svg ***/
101
146
  bindings.xlink = function(el, href) {
102
147
  // In SVG2, xlink namespace is not needed, plain href can be used (Chrome50 2016, Firefox51 2017).
103
148
  el.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", href)
104
149
  }
105
150
  if (window.SVGElement) {
106
- each("animate animateMotion animateTransform circle clipPath defs desc ellipse feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap feDistantLight feDropShadow feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage feMerge feMergeNode feMorphology feOffset fePointLight feSpecularLighting feSpotLight feTile feTurbulence filter foreignObject g image line linearGradient marker mask metadata mpath path pattern polygon polyline radialGradient rect script set stop svg switch symbol text textPath tspan use view", function(name) {
151
+ each("animate animateMotion animateTransform circle clipPath defs ellipse feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap feDistantLight feDropShadow feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage feMerge feMergeNode feMorphology feOffset fePointLight feSpecularLighting feSpotLight feTile feTurbulence g image line linearGradient marker mask mpath path pattern polygon polyline radialGradient rect set stop svg text textPath tspan use", function(name) {
107
152
  elCache[name] = document.createElementNS("http://www.w3.org/2000/svg", name)
108
153
  })
109
154
  // a style title
110
155
  }
111
156
  /**/
112
157
 
113
- Event.asEmitter = asEmitter
114
- Event.stop = eventStop
115
-
116
158
  xhr.css = injectCss
117
- xhr.ui = sources.push.bind(sources)
159
+ xhr.ui = function(src) {
160
+ sources.push(src)
161
+ }
118
162
 
119
163
  function asEmitter(obj) {
120
164
  obj.on = on
121
165
  obj.off = off
122
166
  obj.one = one
123
- obj.emit = emit
167
+ obj.emit = wrap(emit)
124
168
  // emitNext, emitLate
169
+ function wrap(fn) {
170
+ return function(a, b, c, d, e) {
171
+ return fn(this, a, b, c, d, e)
172
+ }
173
+ }
125
174
  }
126
175
 
127
176
  function on(type, fn, scope, _origin) {
128
177
  var emitter = this === window ? emptyArr : this
129
- , events = emitter._e || (emitter._e = create(null))
178
+ , events = emitter._e || (emitter._e = create(NUL))
130
179
  if (type && fn) {
131
- if (isStr(fn)) fn = emit.bind(emitter, fn)
132
- emit.call(emitter, "newListener", type, fn, scope, _origin)
180
+ emit(emitter, "newListener", type, fn, scope, _origin)
133
181
  ;(events[type] || (events[type] = [])).unshift(scope, _origin, fn)
134
182
  }
135
183
  return this
@@ -143,7 +191,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
143
191
  for (i = events.length - 2; i > 0; i -= 3) {
144
192
  if ((events[i + 1] === fn || events[i] === fn) && events[i - 1] == scope) {
145
193
  args = events.splice(i - 1, 3)
146
- emit.call(emitter, "removeListener", type, args[2], args[0], args[1])
194
+ emit(emitter, "removeListener", type, args[2], args[0], args[1])
147
195
  if (fn) break
148
196
  }
149
197
  }
@@ -153,6 +201,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
153
201
 
154
202
  function one(type, fn, scope) {
155
203
  var emitter = this === window ? emptyArr : this
204
+
156
205
  function remove() {
157
206
  off.call(emitter, type, fn, scope)
158
207
  off.call(emitter, type, remove, scope)
@@ -162,13 +211,13 @@ console.log("LiteJS is in debug mode, but it's fine for production")
162
211
  return this
163
212
  }
164
213
 
165
- function emit(type) {
214
+ function emit(emitter, type) {
215
+ if (emitter === window) emitter = emptyArr
166
216
  var args, i
167
- , emitter = this === window ? emptyArr : this
168
217
  , _e = emitter._e
169
218
  , arr = _e ? (_e[type] || emptyArr).concat(_e["*"] || emptyArr) : emptyArr
170
219
  if ((_e = arr.length)) {
171
- for (i = _e - 1, args = slice.call(arguments, 1); i > 1; i -= 3) {
220
+ for (i = _e - 1, args = slice.call(arguments, 2); i > 1; i -= 3) {
172
221
  if (arr[i]) arr[i].apply(arr[i - 2] || emitter, args)
173
222
  }
174
223
  }
@@ -214,7 +263,6 @@ console.log("LiteJS is in debug mode, but it's fine for production")
214
263
 
215
264
  function LiteJS(opts) {
216
265
  opts = assign({
217
- path: "",
218
266
  /*** breakpoints ***/
219
267
  breakpoints: {
220
268
  sm: 0,
@@ -255,45 +303,6 @@ console.log("LiteJS is in debug mode, but it's fine for production")
255
303
 
256
304
  asEmitter(View)
257
305
  asEmitter(View.prototype = {
258
- show: function(_params) {
259
- var parent
260
- , params = lastParams = _params || {} // jshint ignore:line
261
- , view = lastView = this // jshint ignore:line
262
- , tmp = params._v || view // Continue bubbleUp from _v
263
-
264
- params._c = view.o ? view : params._c
265
- for (View.route = view.r; tmp; tmp = parent) {
266
- viewEmit(syncResume = params._v = tmp, "ping", params, View)
267
- syncResume = UNDEF
268
- if (lastParams !== params) return
269
- if ((parent = tmp.p)) {
270
- if (parent.c && parent.c !== tmp) {
271
- params._c = parent.c
272
- }
273
- parent.c = tmp
274
- }
275
- if (tmp.f) {
276
- return xhr.load(
277
- replace(tmp.f, /^|,/g, "$&" + View.path).split(","),
278
- readTemplates.bind(view, view.wait(tmp.f = ""))
279
- )
280
- } else if (!tmp.e) {
281
- if (tmp.r === "404") {
282
- viewParse("%view 404 #\nh2 Not found")
283
- }
284
- return View("404").show({origin:params})
285
- }
286
- }
287
-
288
- for (tmp in params) {
289
- if (tmp.charAt(0) !== "_" && (syncResume = hasOwn.call(paramCb, tmp) && paramCb[tmp] || paramCb["*"])) {
290
- syncResume(params[tmp], tmp, view, params)
291
- syncResume = UNDEF
292
- }
293
- }
294
- viewEmit(view, "nav")
295
- bubbleDown(params)
296
- },
297
306
  wait: function() {
298
307
  var params = lastParams
299
308
  params._p = 1 + (params._p | 0) // pending
@@ -302,14 +311,13 @@ console.log("LiteJS is in debug mode, but it's fine for production")
302
311
  if (params._d) {
303
312
  bubbleDown(params)
304
313
  } else if (params._v) {
305
- lastView.show(params)
314
+ viewPing(lastView, params)
306
315
  }
307
316
  }
308
317
  }
309
318
  })
310
319
 
311
- var root = opts.root
312
- , viewFn, lastView, lastUrl, syncResume
320
+ var viewFn, lastView, lastUrl, syncResume
313
321
  , fnStr = ""
314
322
  , reStr = ""
315
323
  , reEsc = /[.*+?^${}()|[\]/\\]/g
@@ -319,11 +327,12 @@ console.log("LiteJS is in debug mode, but it's fine for production")
319
327
  , views = View.views = {}
320
328
  , paramCb = {}
321
329
  , lastParams = paramCb
322
- , $d = elScope(View("#", root).e, root)
330
+ , root = View("#", opts.root).e
331
+ , $d = elScope(root, root)
323
332
 
324
333
  $d.$ui = assign(View, {
325
- $: find.bind(View, root),
326
- $$: findAll.bind(View, root),
334
+ $: bind(find, View, root),
335
+ $$: bind(findAll, View, root),
327
336
  $d: $d,
328
337
  def: viewDef,
329
338
  get: viewGet,
@@ -349,14 +358,6 @@ console.log("LiteJS is in debug mode, but it's fine for production")
349
358
  }
350
359
  })
351
360
 
352
- function appendBind(el, val, sep, q) {
353
- var current = getAttr(el, BIND_ATTR)
354
- setAttr(el, BIND_ATTR, (current ? (
355
- q === "^" ?
356
- val + sep + current :
357
- current + sep + val
358
- ) : val))
359
- }
360
361
  function bubbleDown(params) {
361
362
  var view = params._v
362
363
  , close = params._c
@@ -370,7 +371,9 @@ console.log("LiteJS is in debug mode, but it's fine for production")
370
371
  render(view.o)
371
372
  viewEmit(parent, "openChild", view, close)
372
373
  viewEmit(view, "open", params)
374
+ /*** kb ***/
373
375
  addKb(view.kb)
376
+ /**/
374
377
  params._c = UNDEF
375
378
  }
376
379
  if ((params._d = params._v = view.c)) {
@@ -388,7 +391,9 @@ console.log("LiteJS is in debug mode, but it's fine for production")
388
391
  viewClose(view.c)
389
392
  elKill(view.o)
390
393
  view.o = UNDEF
394
+ /*** kb ***/
391
395
  rmKb(view.kb)
396
+ /**/
392
397
  viewEmit(view, "close")
393
398
  }
394
399
  }
@@ -404,15 +409,15 @@ console.log("LiteJS is in debug mode, but it's fine for production")
404
409
  }
405
410
  }
406
411
  function viewEmit(view, event, a, b) {
407
- view.emit(event, a, b)
408
- View.emit(event, view, a, b)
409
- LiteJS.emit(event, view, a, b)
412
+ emit(view, event, a, b)
413
+ emit(View, event, view, a, b)
414
+ emit(LiteJS, event, view, a, b)
410
415
  }
411
416
  function viewEval(str, scope) {
412
417
  try {
413
418
  Function("$s,$ui,$d,$,$$", str)(scope, View, $d, View.$, View.$$)
414
419
  } catch(e) {
415
- logErr(e, "viewEval: " + str)
420
+ throw e + "\nviewEval: " + str
416
421
  }
417
422
  }
418
423
  function viewGet(url, params) {
@@ -428,6 +433,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
428
433
  var parent = El("div")
429
434
  , stack = [-1]
430
435
  , parentStack = []
436
+ , templateRe = /([ \t]*)(%?)((?:("|')(?:\\.|[^\\])*?\4|[-\w:.#[\]~^$*|]=?)*) ?([\/>=@^;]|)(([\])}]?).*?([[({]?))(?=\x1f|$)/gm
431
437
 
432
438
  function work(all, indent, plugin, sel, q, op, text, mapEnd, mapStart, offset) {
433
439
  if (offset && all === indent) return
@@ -462,13 +468,13 @@ console.log("LiteJS is in debug mode, but it's fine for production")
462
468
  append(parent, parent = El(sel))
463
469
  }
464
470
  if (text && op != "/") {
465
- if (op === ">" || op === "+") {
466
- replace(indent + (op === "+" ? text : " " + text), templateRe, work)
471
+ if (op === ">") {
472
+ replace(indent + " " + text, templateRe, work)
467
473
  } else if (op === "=") {
468
474
  append(parent, text) // + "\n")
469
475
  } else {
470
476
  if (op === "") {
471
- text = "txt _('" + replace(text, /'/g, "\\'") + "',$s)"
477
+ text = "txt _(" + quote(text) + ",$s)"
472
478
  }
473
479
  appendBind(parent, text, ";", op)
474
480
  }
@@ -488,17 +494,55 @@ console.log("LiteJS is in debug mode, but it's fine for production")
488
494
  histStart(viewShow)
489
495
  }
490
496
  }
491
- function viewShow(url, _params) {
497
+ function viewPing(view, params) {
498
+ var parent
499
+ , tmp = params._v || view // Continue bubbleUp from _v
500
+ lastParams = params
501
+ lastView = view
502
+ params._c = view.o ? view : params._c
503
+ for (View.route = view.r; tmp; tmp = parent) {
504
+ viewEmit(syncResume = params._v = tmp, "ping", params, View)
505
+ syncResume = UNDEF
506
+ if (lastParams !== params) return
507
+ if ((parent = tmp.p)) {
508
+ if (parent.c && parent.c !== tmp) {
509
+ params._c = parent.c
510
+ }
511
+ parent.c = tmp
512
+ }
513
+ if (tmp.f) {
514
+ return xhr.load(
515
+ replace(tmp.f, /^|,/g, "$&" + (View.path || "")).split(","),
516
+ bind(readTemplates, view, view.wait(tmp.f = ""))
517
+ )
518
+ } else if (!tmp.e) {
519
+ if (tmp.r === "404") {
520
+ viewParse("%view 404 #\nh2 Not found")
521
+ }
522
+ return viewShow("404")
523
+ }
524
+ }
525
+
526
+ for (tmp in params) {
527
+ if (tmp.charAt(0) !== "_" && (syncResume = hasOwn(paramCb, tmp) && paramCb[tmp] || paramCb["*"])) {
528
+ syncResume(params[tmp], tmp, view, params)
529
+ syncResume = UNDEF
530
+ }
531
+ }
532
+ viewEmit(view, "nav")
533
+ bubbleDown(params)
534
+ }
535
+ function viewShow(url) {
492
536
  if (url === true) {
493
537
  if (lastParams._p > 0) return
494
538
  url = lastUrl
495
539
  lastUrl = 0
496
540
  }
497
- var params = _params || {}
541
+ var params = $d.params = { _t: Date.now() }
498
542
  , view = viewGet(url, params)
499
543
  if (!view.o || lastUrl !== url) {
500
- $d.url = lastExp = lastUrl = url
501
- view.show($d.params = params)
544
+ $d.url = lastUrl = expand(url)
545
+ viewPing(view, params)
502
546
  }
503
547
  }
504
548
 
@@ -523,7 +567,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
523
567
  plugin.e.p = plugin
524
568
  }
525
569
  }
526
- if (proto.r) proto.d = Function("p", "p.r(p.o||p.t)")
570
+ if (proto.r) proto.d = Function("p", "p.r(p.o+p.t)")
527
571
  assign(Plugin.prototype, proto)
528
572
  }
529
573
  function usePluginContent(plugin) {
@@ -562,40 +606,36 @@ console.log("LiteJS is in debug mode, but it's fine for production")
562
606
  addPlugin("def", { r: viewDef })
563
607
  addPlugin("js", { r: viewEval })
564
608
  addPlugin("each", {
565
- r: function(params) {
609
+ r: function() {
566
610
  var txt = this.t
567
- each(params, function(param) {
611
+ each(this.o, function(param) {
568
612
  viewParse(replace(txt, /{key}/g, param))
569
613
  })
570
614
  }
571
615
  })
572
616
  addPlugin("el", {
573
617
  c: 1,
574
- d: function(plugin) {
575
- var parent = plugin.u
576
- , el = usePluginContent(plugin)
618
+ d: function(plugin, el) {
619
+ el = usePluginContent(plugin)
577
620
  elCache[plugin.n] = el
578
- return parent
579
621
  }
580
622
  })
581
623
  plugins.svg = plugins.el
582
624
  addPlugin("map", {
583
- r: function() {
625
+ r: function(txt) {
584
626
  var plugin = this
585
- , txt = plugin.o + plugin.t
586
627
  appendBind(plugin.u, plugin.s ? txt.slice(1) : txt, plugin.s)
587
628
  }
588
629
  })
589
630
  addPlugin("view", {
590
631
  c: 1,
591
632
  d: function(plugin) {
592
- var bind = getAttr(plugin.e, BIND_ATTR)
633
+ var expr = getAttr(plugin.e, BIND_ATTR)
593
634
  , view = View(plugin.n, usePluginContent(plugin), plugin.x)
594
- if (bind) {
595
- bind = replace(bind, renderRe, function(_, name, op, args) {
635
+ if (expr) {
636
+ viewEval(replace(expr, renderRe, function(_, name, op, args) {
596
637
  return "($s." + name + (isFn(view[name]) ? "(" + (args || "") + ")" : "=" + args) + "),"
597
- }) + "1"
598
- viewEval(bind, view)
638
+ }) + "1", view)
599
639
  }
600
640
  }
601
641
  })
@@ -625,7 +665,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
625
665
  cls(html, lastOrient = next)
626
666
  }
627
667
 
628
- View.emit("resize")
668
+ emit(View, "resize")
629
669
  }, 99)
630
670
 
631
671
  if (breakpoints) {
@@ -635,26 +675,257 @@ console.log("LiteJS is in debug mode, but it's fine for production")
635
675
  /**/
636
676
 
637
677
  /*** i18n ***/
638
- var iData = {}
639
- , iFormat = create(null)
640
- , iGlobals = assignDeep(create(null), opts.locales)
641
- each(iGlobals, function(translations, iKey) {
642
- translations = iData[iKey] = assignDeep(create(iGlobals), opts[iKey])
643
- iFormat[iKey] = function(str, data) {
644
- return format(get(translations, str, str || ""), data)
678
+ globalScope._ = format
679
+ var iFormat = create(NUL)
680
+ each(opts.locales || { en: "en" }, function(translations, lang, locales) {
681
+ translations = formatGet.t = assignDeep(assignDeep(create(opts.globals || NUL), locales), opts[lang])
682
+ formatGet.g = getExt
683
+ iFormat[lang] = formatGet
684
+ var iAlias = {
685
+ "#": "num", "num": "#",
686
+ "*": "plural", "plural": "*",
687
+ "?": "pick", "pick": "?",
688
+ "@": "date", "date": "@",
689
+ "~": "pattern", "pattern": "~"
690
+ }
691
+ , cache = create(NUL)
692
+ , dateRe = /([Md])\1\1\1?|([YMDdHhmswSZ])(\2?)|[uUaSoQ]|'((?:''|[^'])*)'|(["\\\n\r\u2028\u2029])/g
693
+ , date1 = new Date()
694
+ , date2 = new Date()
695
+ , iExt = formatGet.ext = {
696
+ date: function(input, _mask, _zone) {
697
+ var undef
698
+ , offset = 4294967295
699
+ , d = input * (input > offset || input < -offset ? 1 : 1000) || Date.parse(input)
700
+ , t = translations["@"] || {}
701
+ , mask = t[_mask] || _mask || "UTC:Y-MM-DD'T'HH:mm:ss'Z'"
702
+ , zone = _zone != undef ? _zone : Date._tz != undef ? Date._tz : undef
703
+ , utc = mask.slice(0, 4) == "UTC:"
704
+ if (zone != undef && !utc) {
705
+ offset = 60 * zone
706
+ date1.setTime(d + offset * 6e4)
707
+ utc = mask = "UTC:" + mask
708
+ } else {
709
+ date1.setTime(d)
710
+ offset = utc ? 0 : -date1.getTimezoneOffset()
711
+ }
712
+ return isNaN(d) ? "" + date1 : (
713
+ cache[mask] || (cache[mask] = Function("d,a,o,l", "var t;return \"" + dateStr(mask, utc) + "\"")))(
714
+ date1,
715
+ date2,
716
+ offset,
717
+ t
718
+ )
719
+ },
720
+ lo: function(str) {
721
+ return isStr(str) ? str.toLowerCase() : ""
722
+ },
723
+ map: function(input, str, sep, lastSep) {
724
+ var arr = []
725
+ each(input, function(val) {
726
+ arr.push(formatGet(str, val))
727
+ })
728
+ lastSep = lastSep && arr.length > 1 ? lastSep + arr.pop() : ""
729
+ return arr.join(sep || ", ") + lastSep
730
+ },
731
+ num: function(input, format) {
732
+ var t = translations["#"] || {}
733
+ return (
734
+ cache[format = t[format] || "#" + format] || (cache[format] = Function("d", "var N=d<0&&(d=-d),n,r,o;return " + numStr(format, t)))
735
+ )(input)
736
+ },
737
+ pattern: function(str, re) {
738
+ var values = []
739
+ , t = translations["~"] || {}
740
+ , key = replace(str, RegExp(re || t[""] || "[\\d.]+", "g"), function(a) {
741
+ values.push(a)
742
+ return "#"
743
+ })
744
+ return str != key && t[key] ? replace(t[key], /#/g, bind(values.shift, values)) : str
745
+ },
746
+ pick: function(val, word) {
747
+ for (var t = translations["?"] || {}, arr = replace((t[word] || word), /([^;=,]+?)\?/g, "$1=$1;").split(/[;=,]/), i = 1|arr.length; i > 0; ) {
748
+ if ((i-=2) < 0 || arr[i] && (arr[i] == "" + val || +arr[i] <= val)) {
749
+ return arr[i + 1] ? replace(arr[i + 1], "#", val) : ""
750
+ }
751
+ }
752
+ },
753
+ plural: function(n, word, expr) {
754
+ var t = translations["*"] || {}
755
+ return (
756
+ cache[expr = t[""] || "n!=1"] || (cache[expr] = Function("a,n", "return (a[+(" + expr + ")]||a[0]).replace('#',n)"))
757
+ )((t[word] || "# " + word).split(";"), n)
758
+ },
759
+ up: function(str) {
760
+ return isStr(str) ? str.toUpperCase() : ""
761
+ }
762
+ }
763
+
764
+ function dateStr(mask, utc) {
765
+ var get = "d.get" + (utc ? "UTC" : "")
766
+ , dateMap = {
767
+ d: "Day()||7",
768
+ M: "Month()+1",
769
+ D: "Date()",
770
+ H: "Hours()",
771
+ h: "Hours()%12||12",
772
+ m: "Minutes()",
773
+ s: "Seconds()",
774
+ S: "Milliseconds()"
775
+ }
776
+ , setA = "a.setTime(+d+((4-(" + get + dateMap.d + "))*864e5))"
777
+ return replace((utc ? mask.slice(4) : mask), dateRe, function(match, MD, single, pad, text, esc) {
778
+ mask = (
779
+ esc ? replace(replace(escape(esc), /%u/g, "\\u"), /%/g, "\\x") :
780
+ text !== UNDEF ? replace(text, /''/g, "'") :
781
+ MD || match == "dd" ? "l[''][" + get + (MD == "M" ? "Month()+" + (match == "MMM" ? 14 : 26) : "Day()" + (pad ? (pad = "") : "+7")) + "]" :
782
+ match == "u" ? "(d/1000)>>>0" :
783
+ match == "U" ? "+d" :
784
+ match == "Q" ? "((" + get + "Month()/3)|0)+1" :
785
+ match == "a" ? "l[" + get + dateMap.H + ">11?'pm':'am']" :
786
+ match == "o" ? setA + ",a" + get.slice(1) + "FullYear()" :
787
+ single == "Y" ? get + "FullYear()" + (pad == "Y" ? "%100" : "") :
788
+ single == "Z" ? "(t=o)?(t<0?((t=-t),'-'):'+')+(t<600?'0':'')+(0|(t/60))" + (pad ? (pad = "") : "+':'") + "+((t%=60)>9?t:'0'+t):'Z'" :
789
+ single == "w" ? "Math.ceil(((" + setA + "-a.s" + get.slice(3) + "Month(0,1))/864e5+1)/7)" :
790
+ get + dateMap[single || match]
791
+ )
792
+ return text !== UNDEF || esc ? mask : "\"+(" + (
793
+ match == "SS" ? "(t=" + mask + ")>9?t>99?t:'0'+t:'00'+t" :
794
+ pad ? "(t=" + mask + ")>9?t:'0'+t" :
795
+ mask
796
+ ) + ")+\""
797
+ })
798
+ }
799
+
800
+ function numStr(format, t) {
801
+ // format;NaN;negFormat;0;Infinity;-Infinity;roundPoint
802
+ // 🯰🯱🯲🯳🯴🯵🯶🯷🯸🯹
803
+ var conf = format.split(";")
804
+ , nan_value = conf[1] || "-"
805
+ , o = (t.ordinal||"").split(";")
806
+ , pre = {
807
+ a: "(o+=d<1e3?'':d<1e6?(d/=1e3,'k'):d<1e9?(d/=1e6,'M'):d<1e12?(d/=1e9,'G'):d<1e15?(d/=1e12,'T'):d<1e18?(d/=1e15,'P'):(d/=1e18,'E')),"
808
+ }
809
+ , post = {
810
+ o: "r+(o=" + JSON.stringify(o.slice(0,-1)) + "," + o.pop() + ")"
811
+ }
812
+ , m2 = /([^\d#]*)([\d# .,_·']*\/?\d+)(?:(\s*)([a-z%]+)(\d*))?(.*)/.exec(conf[0])
813
+ , m3 = /([.,\/])(\d*)$/.exec(m2[2])
814
+ , decimals = m3 && m3[2].length || 0
815
+ , full = m3 ? m2[2].slice(0, m3.index) : m2[2]
816
+ , num = replace(full, /\D+/g, "")
817
+ , sLen = num.length
818
+ , step = decimals ? +(m3[1] === "/" ? 1 / m3[2] : num + "." + m3[2]) : num
819
+ , decSep = m3 && m3[1]
820
+ , fn = "d===Infinity?(N?" + quote(conf[5]||nan_value) + ":" + quote(conf[4]||nan_value) + "):d>0||d===0?(o=" + quote(m2[3]) + "," + (pre[m2[4]] || "") + "n=" + (
821
+ // Use exponential notation to fix float rounding
822
+ // Math.round(1.005*100)/100 = 1 instead of 1.01
823
+ decimals ?
824
+ "d>1e-" + (decimals + 1) + "?(n=(d+'e" + decimals + "')/" + (step + "e" + decimals) + "":
825
+ "d>"+num+"e-1?(n=d/" + num
826
+ ) + ",Math.floor(n" + (
827
+ conf[6] == 1 ? "%1?n+1:n" : "+" + (conf[6] || 0.5)
828
+ ) + ")*" + step + "):0,r=" + (
829
+ m2[5] ? "(''+(+n.toPrecision(" + (m2[5]) + ")))" :
830
+ decimals ? "n.toFixed(" + decimals + ")" :
831
+ "n+''"
832
+ )
833
+
834
+ if (decimals) {
835
+ if (decSep == "/") {
836
+ fn += ".replace(/\\.\\d+/,'" + (
837
+ m3[2] == 5 ?
838
+ "⅕⅖⅗⅘'.charAt(5" :
839
+ "⅛¼⅜½⅝¾⅞'.charAt(8"
840
+ ) + "*(n%1)-1))"
841
+ } else if (decSep != ".") {
842
+ fn += ".replace('.','" + decSep + "')"
843
+ }
844
+ if (sLen === 0) {
845
+ fn += ",n<1&&(r=r.slice(1)||'0')"
846
+ }
847
+ }
848
+ if (sLen > 1) {
849
+ if (decimals) sLen += decimals + 1
850
+ fn += ",r=(r.length<" + sLen + "?(1e15+r).slice(-" + sLen + "):r)"
851
+ }
852
+
853
+ if ((num = full.match(/[^\d#][\d#]+/g))) {
854
+ fn += ",r=" + numJunk(num.length - 1, 0, decimals ? decimals + 1 : 0)
855
+ }
856
+
857
+ fn += (
858
+ (m2[4] ? ",r=" + (post[m2[4]] || "r+o") : "") +
859
+ // negative format
860
+ ",N&&n>0?" + replace(quote(conf[2] || "-#"), "#", "'+r+'") + ":" +
861
+ (conf[3] ? "n===0?" + quote(conf[3]) + ":" : "") +
862
+ (m2[1] ? quote(m2[1]) + "+r" : "r") +
863
+ (m2[6] ? "+" + quote(m2[6]) : "")
864
+ )
865
+
866
+ return fn + "):" + quote(nan_value)
867
+
868
+ function numJunk(i, lastLen, dec) {
869
+ var len = lastLen + num[i].length - 1
870
+
871
+ return "(n<1e" + len + (
872
+ lastLen ? "?r.slice(0,-" + (lastLen + dec) + "):" : "?r:"
873
+ ) + (
874
+ len < 16 ? numJunk(i?i-1:i, len, dec) : "r.slice(0,-" + (lastLen + dec) + ")"
875
+ ) + "+" + quote(num[i].charAt(0)) + "+r.slice(-" + (len + dec) + (
876
+ lastLen ? ",-" + (lastLen + dec) : ""
877
+ ) + "))"
878
+ }
879
+ }
880
+
881
+ function formatGet(str, data) {
882
+ return format(iGet(translations, str, str || ""), data, getExt)
883
+ }
884
+ function getExt(obj, str) {
885
+ var fn = cache[str] || (cache[str] = (replace(replace(str, /;\s*([#*?@~])(.*)/, function(_, op, arg) {
886
+ return ";" + iAlias[op] + " " + quote(arg)
887
+ }), renderRe, function(_, name, op, args) {
888
+ fn = (_ === name) ? name : "$el." + name + "(" + fn + (args ? "," + args : "") + ")"
889
+ }), fn === str ? str : makeFn(fn, fn)))
890
+ return str == "$" ? obj : isStr(fn) ? iGet(obj, str, "") : isFn(fn) ? fn(iExt, obj, translations) : ""
645
891
  }
646
892
  })
647
- assignDeep(iGlobals, opts.globals)
648
- iSet([localStorage.lang, navigator.language, navigator.userLanguage].concat(navigator.languages, opts.lang).find(iResolve))
649
- $d.locales = Object.keys(iFormat)
650
- View.lang = iSet
651
- function iResolve(lang) {
652
- return lang && (iFormat[lang = ("" + lang).toLowerCase()] || iFormat[lang = lang.split("-")[0]]) && lang
893
+ ;[localStorage.lang, opts.lang, navigator.language].concat(navigator.languages, html.lang, $d.locales = Object.keys(iFormat))
894
+ .find(View.lang = function(lang, translations) {
895
+ if (lang && (iFormat[lang = ("" + lang).toLowerCase()] || iFormat[lang = lang.split("-")[0]])) {
896
+ assignDeep(iFormat[html.lang = $d.lang = localStorage.lang = lang].t, translations)
897
+ return ($d._ = iFormat[lang])
898
+ }
899
+ })
900
+ function format(str, data, getter) {
901
+ for (var char, inQuote, inExpr, depth = 0, pos = 0, len = str.length; pos < len; ) {
902
+ char = str.charAt(pos++)
903
+ if (char == "'" || char == "\"") { // '"
904
+ inQuote = (!inExpr || char === inQuote) ? "" : char
905
+ } else if (inQuote) {
906
+ if (char == "\\") pos++
907
+ } else if (char == "{" && depth++ < 1) {
908
+ inExpr = pos
909
+ } else if (char == "}" && inExpr && --depth < 1) {
910
+ char = getter(data, str.slice(inExpr, pos - 1), "")
911
+ str = str.slice(0, inExpr - 1) + char + str.slice(pos)
912
+ pos = inExpr + char.length - 1
913
+ len = str.length
914
+ }
915
+ }
916
+ return str
653
917
  }
654
- function iSet(lang, translations) {
655
- assignDeep(iData[lang = html.lang = $d.lang = localStorage.lang = iResolve(lang) || $d.lang || opts.lang], translations)
656
- return ($d._ = iFormat[lang] || format)
918
+ function iGet(obj, path, fallback) {
919
+ return isStr(path) ? (
920
+ isStr(obj[path]) ? obj[path] :
921
+ (path = path.split("."))[1] && isObj(obj = obj[path[0]]) && isStr(obj[path[1]]) ? obj[path[1]] :
922
+ fallback
923
+ ) :
924
+ isArr(path) ? iGet(obj, path[0]) || iGet(obj, path[1]) || iGet(obj, path[2], fallback) :
925
+ fallback
657
926
  }
927
+ /*/
928
+ globalScope._ = String
658
929
  /**/
659
930
 
660
931
  return View
@@ -663,7 +934,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
663
934
  function setUrl(url, rep, checkUrl) {
664
935
  /*** pushState ***/
665
936
  if (pushBase) {
666
- history[rep ? "replaceState" : "pushState"](null, null, pushBase + url)
937
+ history[rep ? "replaceState" : "pushState"](NUL, NUL, pushBase + url)
667
938
  } else {
668
939
  /**/
669
940
  location[rep ? "replace" : "assign"]("#" + url)
@@ -678,20 +949,12 @@ console.log("LiteJS is in debug mode, but it's fine for production")
678
949
  function histStart(cb) {
679
950
  /*** pushState ***/
680
951
  // Chrome5, Firefox4, IE10, Safari5, Opera11.50
681
- var histLast, url
682
- , base = find(html, "base")
683
- LiteJS.base = replace((base || location).href, /[^\/]*(#.*)?$/, "")
684
- if (base) base = replace(base.href, /.*:\/\/[^/]*|[^\/]*$/g, "")
685
- if (base && !history.pushState) {
686
- url = location.pathname.slice(base.length)
687
- if (url) {
688
- location.replace(base + "#" + url)
689
- }
690
- }
691
- if (base && history.pushState) {
692
- pushBase = base
952
+ var histLast
953
+ , baseEl = find(html, "base")
954
+ , url = getUrl()
955
+ if (baseEl && history.pushState) {
956
+ pushBase = replace(baseEl.href, /.*:\/\/[^/]*|[^\/]*$/g, "")
693
957
 
694
- url = location.href.split("#")[1]
695
958
  if (url && !getUrl()) {
696
959
  setUrl(url, 1)
697
960
  }
@@ -716,8 +979,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
716
979
  /*** pushState ***/
717
980
  pushBase ? location.pathname.slice(pushBase.length) :
718
981
  /**/
719
- // bug in Firefox where location.hash is decoded
720
- // bug in Safari where location.pathname is decoded
982
+ // NOTE: in Firefox location.hash is decoded; in Safari location.pathname is decoded
721
983
  location.href.split("#")[1] || "", /^[#\/\!]+|[\s\/]+$/g, "")
722
984
  }
723
985
  }
@@ -764,7 +1026,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
764
1026
  }
765
1027
 
766
1028
  assign(El, bindings, {
767
- emit: elEmit,
1029
+ emit: emit,
768
1030
  empty: elEmpty,
769
1031
  kill: elKill,
770
1032
  off: acceptMany(rmEvent),
@@ -796,82 +1058,97 @@ console.log("LiteJS is in debug mode, but it's fine for production")
796
1058
  })
797
1059
 
798
1060
  assign(El, {
799
- append: append,
800
1061
  $b: assign(bindings, {
801
1062
  each: function(el, name, list) {
1063
+ /*** debug ***/
1064
+ if (el._li) throw "Binding each must be type of once: each!" + name
1065
+ /**/
1066
+
802
1067
  var comm = Comm("each " + name, up)
803
1068
  , pos = 0
804
1069
  , nodes = []
1070
+
805
1071
  comm.$s = this
806
1072
  elReplace(el, comm)
807
- up()
808
- return { a: add, r: remove, u: up }
1073
+ each(list, add)
1074
+ return { a: add, u: up }
809
1075
 
810
1076
  function add(item) {
811
1077
  var clone = nodes[pos] = el.cloneNode(true)
812
1078
  , subScope = elScope(clone, comm)
1079
+ append(comm.parentNode, clone, (pos ? nodes[pos - 1] : comm).nextSibling)
813
1080
  subScope.$i = pos++
814
1081
  subScope.$len = list.length
815
1082
  subScope[name] = item
816
1083
  clone[BIND_ATTR] = el[BIND_ATTR]
817
- append(comm.parentNode, clone, comm)
1084
+ /*** debug ***/
1085
+ clone._li = up
1086
+ /**/
818
1087
  render(clone)
819
1088
  }
820
- function remove(i) {
821
- elKill(nodes.splice(i, 1)[0])
822
- }
823
1089
  function up() {
824
- for (; pos; ) remove(--pos)
825
- each(list, add)
1090
+ for (var i = list.length; pos > i; ) elKill(nodes[--pos])
1091
+ for (nodes.length = i; pos < i; ) add(list[pos])
1092
+ for (; i--; ) nodes[i].$s[name] = list[i]
826
1093
  }
827
1094
  },
828
1095
  el: function(el, tag, fallback) {
829
- var child = El(elCache[tag] ? tag : fallback)
830
- , tmp = getAttr(el, BIND_ATTR)
831
- , tmp2 = getAttr(child, BIND_ATTR)
832
- if (tmp) setAttr(child, BIND_ATTR, tmp2 ? tmp + ";" + tmp2 : tmp)
833
- if ((tmp = el.className)) cls(child, tmp)
834
- child.$s = el.$s
835
- elReplace(el, child)
836
- render(child)
837
- return child
1096
+ tag = elCache[tag] ? tag : fallback
1097
+ if (el._el !== tag) {
1098
+ var child = El(tag)
1099
+ , tmp = child._elb = el._el ? el._elb : el[BIND_ATTR]
1100
+ if (tmp) appendBind(child, tmp, ";", "^")
1101
+ child.$s = el.$s
1102
+ child._el = tag
1103
+ elReplace(el, child)
1104
+ if ((tmp = child._elc = el._el ? (elKill(el), el._elc) : el.className)) cls(child, tmp)
1105
+ render(child)
1106
+ return true
1107
+ }
838
1108
  },
839
1109
  "if": function(el, enabled) {
840
1110
  if (enabled) {
841
- elReplace(el._if, el)
1111
+ elReplace(el._r, el)
842
1112
  } else {
843
- elReplace(el, el._if || (el._if = Comm("if", render.bind(el, el))))
1113
+ elReplace(el, el._r || (el._r = Comm("if", bind(render, el, el, this))))
844
1114
  return true
845
1115
  }
846
1116
  },
847
1117
  is: function(el, val, opts, prefix) {
848
1118
  if (!prefix) prefix = "is-"
849
- var match = format(val, opts)
1119
+ var match = elScope(el)._.ext.pick(val, opts)
850
1120
  cls(el, el[prefix + opts], 0)
851
1121
  cls(el, el[prefix + opts] = match && prefix + match)
852
1122
  },
1123
+ name: function(el, name) {
1124
+ setAttr(el, "name", expand(name, 1))
1125
+ },
853
1126
  ref: function(el, name) {
854
1127
  this[name] = el
855
1128
  },
856
1129
  $s: function(el) {
857
- var scope = elScope(el, el)
1130
+ var scope = this
858
1131
  each(slice.call(arguments, 1), function(args) {
859
1132
  each(args, function(arg, i) {
860
1133
  if (isStr(i)) scope[i] = arg
861
1134
  else scope[arg] = setAttr(el, arg, "")
862
1135
  })
863
1136
  })
1137
+ },
1138
+ view: function(el, url) {
1139
+ setAttr(el, "href", (pushBase || "#") + expand(url || ""))
864
1140
  }
865
1141
  }),
1142
+ $d: globalScope,
1143
+ append: append,
1144
+ asEmitter: asEmitter,
866
1145
  blur: blur,
867
1146
  cache: elCache,
868
1147
  closest: closest,
869
- $d: globalScope,
870
1148
  get: getAttr,
871
1149
  hasClass: hasClass,
872
1150
  matches: matches,
873
- next: walk.bind(El, "nextSibling"),
874
- prev: walk.bind(El, "previousSibling"),
1151
+ nearest: nearest,
875
1152
  rate: rate,
876
1153
  replace: elReplace,
877
1154
  scope: elScope,
@@ -885,7 +1162,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
885
1162
  var current = getAttr(el, key)
886
1163
 
887
1164
  /*** ie8 ***/
888
- // Bug: IE5-7 doesn't set styles and removes events when you try to set them.
1165
+ // NOTE: IE5-7 doesn't set styles and removes events when you try to set them.
889
1166
  // IE6 label with a for attribute will re-select the first option of SELECT list instead of just giving focus.
890
1167
  // http://webbugtrack.blogspot.com/2007/09/bug-116-for-attribute-woes-in-ie6.html
891
1168
  // IE8 and below have a bug where changed 'name' not accepted on form submit
@@ -941,7 +1218,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
941
1218
  isNum(before) ? el.childNodes[before < 0 ? el.childNodes.length - before - 2 : before] :
942
1219
  isArr(before) ? before[0] :
943
1220
  before
944
- ) || null)
1221
+ ) || NUL)
945
1222
  /*** debug ***/
946
1223
  if (el.namespaceURI && child.namespaceURI && el.namespaceURI !== child.namespaceURI && el.tagName !== "foreignObject" && child.tagName !== "svg") {
947
1224
  console.error("NAMESPACE CHANGE!", el, child)
@@ -951,6 +1228,15 @@ console.log("LiteJS is in debug mode, but it's fine for production")
951
1228
  }
952
1229
  }
953
1230
 
1231
+ function appendBind(el, val, sep, q) {
1232
+ var current = getAttr(el, BIND_ATTR)
1233
+ setAttr(el, BIND_ATTR, (current ? (
1234
+ q === "^" ?
1235
+ val + sep + current :
1236
+ current + sep + val
1237
+ ) : val))
1238
+ }
1239
+
954
1240
  function hasClass(el, name) {
955
1241
  var current = el.className || ""
956
1242
 
@@ -990,9 +1276,6 @@ console.log("LiteJS is in debug mode, but it's fine for production")
990
1276
  }
991
1277
  }
992
1278
 
993
- function elEmit(el) {
994
- emit.apply(el, slice.call(arguments, 1))
995
- }
996
1279
  function elEmpty(el) {
997
1280
  for (; el.lastChild; elKill(el.lastChild));
998
1281
  }
@@ -1004,15 +1287,16 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1004
1287
  if (isObj(tr)) bindingsCss(el, tr)
1005
1288
  tr = "transitionend"
1006
1289
  // transitionend fires for each property transitioned
1007
- if ("on" + tr in el) return addEvent(el, tr, elKill.bind(el, el, el = UNDEF))
1290
+ if ("on" + tr in el) return addEvent(el, tr, bind(elKill, el, el, el = UNDEF))
1008
1291
  }
1009
1292
  if (el._e) {
1010
- emit.call(el, "kill")
1293
+ emit(el, "kill")
1011
1294
  el._e = UNDEF
1012
1295
  }
1013
1296
  elRm(el)
1014
- el.$s = UNDEF
1015
1297
  if (el.nodeType < 2) {
1298
+ el.$s = UNDEF
1299
+ elKill(el._r) // Replacement element like comment from if binding
1016
1300
  elEmpty(el)
1017
1301
  if (el.valObject !== UNDEF) {
1018
1302
  el.valObject = UNDEF
@@ -1089,12 +1373,12 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1089
1373
  }
1090
1374
 
1091
1375
  return checkbox && !el.checked ?
1092
- (type === "radio" ? UNDEF : null) :
1376
+ (type === "radio" ? UNDEF : NUL) :
1093
1377
  el.valObject !== UNDEF ? el.valObject : el.value
1094
1378
 
1095
1379
  function replacer(_, _key, offset) {
1096
1380
  if (step == opts) key = key.slice(0, offset)
1097
- step = step[key] || (step[key] = step[key] === null || _key && +_key != _key ? {} : [])
1381
+ step = step[key] || (step[key] = step[key] === NUL || _key && +_key != _key ? {} : [])
1098
1382
  key = _key
1099
1383
  }
1100
1384
  }
@@ -1112,7 +1396,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1112
1396
  return
1113
1397
  }
1114
1398
 
1115
- var bind, next
1399
+ var el, next
1116
1400
  , scope = node.$s || $s || closestScope(node)
1117
1401
 
1118
1402
  /*** ie8 ***/
@@ -1122,40 +1406,35 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1122
1406
  /**/
1123
1407
 
1124
1408
  if (hydrate(node, BIND_ATTR, scope)) return
1125
- for (bind = node.firstChild; bind; bind = next) {
1126
- next = bind.nextSibling
1127
- render(bind, scope)
1409
+ for (el = node.firstChild; el; el = next) {
1410
+ next = el.nextSibling
1411
+ render(el, scope)
1128
1412
  }
1129
1413
  hydrate(node, "data-out", scope)
1130
1414
  }
1131
1415
 
1132
1416
  function hydrate(node, attr, scope) {
1133
1417
  var fn
1134
- , bind = node[attr] || (node[attr] = setAttr(node, attr, "") || true)
1135
- if (bind !== true) try {
1136
- fn = fnCache[bind] || (fnCache[bind] = makeFn(bind))
1137
- return fn(node, scope, attr, fn.o)
1418
+ , expr = node[attr] || (node[attr] = setAttr(node, attr, "") || true)
1419
+ if (expr !== true) try {
1420
+ fn = fnCache[expr] || (fnCache[expr] = makeFn(expr))
1421
+ return fn(node, scope, attr, bindOnce)
1138
1422
  } catch (e) {
1139
- logErr(e, attr + ": " + bind, node)
1423
+ throw e + "\n" + attr + ": " + expr
1140
1424
  }
1141
1425
  }
1142
- function makeFn(fn) {
1143
- var i = 0
1144
- , bindOnce = []
1145
- fn = "$s&&(" + replace(fn, renderRe, function(match, name, op, args) {
1426
+ function makeFn(fn, raw, i) {
1427
+ fn = raw || "$s&&(" + replace(fn, renderRe, function(match, name, op, args) {
1146
1428
  return (
1147
- (op === "!" && (bindOnce[i] = match)) ?
1148
- "($el[$a]=$el[$a].replace($o[" + (i++)+ "],''),0)||" :
1149
- ""
1150
- ) + "$b['" + (bindings[name] ? name + "'].call($s,$el" : "set']($el,'" + name + "'") + (args ? "," + replace(args, /^\s*(\w+) in /,"'$1',") : "") + ")||"
1429
+ op ? "($el[$a]=$el[$a].replace($o[" + (i = bindOnce.indexOf(match), i < 0 ? bindOnce.push(match) - 1 : i)+ "],''),0)||" : ""
1430
+ ) + "$b['" + (bindings[name] ? name + "'].call($s" + (name == "$s" ? "=El.scope($el,$el)": "") + ",$el" : "set']($el,'" + name + "'") + (args ? "," + args : "") + ")||"
1151
1431
  }) + "$r)"
1152
1432
  var vars = replace(fn, fnRe, "").match(wordRe) || []
1153
1433
  for (i = vars.length; i--; ) {
1154
1434
  if (vars.indexOf(vars[i]) !== i) vars.splice(i, 1)
1155
1435
  else vars[i] += "=$s." + vars[i]
1156
1436
  }
1157
- fn = Function("$el,$s,$a,$o,$r", "var " + vars + ";return " + fn)
1158
- fn.o = bindOnce
1437
+ fn = Function("$el,$s,$a,$o,$r", (vars[0] ? "var " + vars : "") + ";return " + fn)
1159
1438
  return fn
1160
1439
  }
1161
1440
 
@@ -1170,7 +1449,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1170
1449
  if (map) {
1171
1450
  kbMaps.unshift(map)
1172
1451
  if (killEl) {
1173
- addEvent(killEl, "kill", rmKb.bind(map, map))
1452
+ addEvent(killEl, "kill", bind(rmKb, map, map))
1174
1453
  }
1175
1454
  }
1176
1455
  }
@@ -1178,19 +1457,6 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1178
1457
  map = kbMaps.indexOf(map)
1179
1458
  if (map > -1) kbMaps.splice(map, 1)
1180
1459
  }
1181
- function runKb(e, code, chr) {
1182
- var fn, map
1183
- , i = 0
1184
- , el = e.target
1185
- , input = /INPUT|TEXTAREA|SELECT/i.test((el.nodeType < 2 ? el : el.parentNode).tagName)
1186
-
1187
- for (; (map = kbMaps[i++]) && (
1188
- !(fn = !input || map.input ? map[code] || map[chr] || map.num && code > 47 && code < 58 && (chr|=0, map.num) || map.all : fn) &&
1189
- map.bubble
1190
- ););
1191
- if (isStr(fn)) setUrl(fn)
1192
- if (isFn(fn)) fn(e, chr, el)
1193
- }
1194
1460
 
1195
1461
  addEvent(document, "keydown", function(e) {
1196
1462
  if (kbMaps[0]) {
@@ -1203,18 +1469,30 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1203
1469
  if (code == 8 && kbMaps[0].backspace) {
1204
1470
  eventStop(e)
1205
1471
  }
1206
- runKb(e, code, key)
1207
- if (e.shiftKey && code != 16) runKb(e, code, "shift+" + key)
1472
+ runKb(key)
1473
+ if (e.shiftKey && code != 16) runKb("shift+" + key)
1208
1474
  // people in Poland use Right-Alt+S to type in Ś.
1209
1475
  // Right-Alt+S is mapped internally to Ctrl+Alt+S.
1210
1476
  // THANKS: Marcin Wichary - disappearing Polish Ś [https://medium.engineering/fa398313d4df]
1211
1477
  if (e.altKey) {
1212
- if (code != 18) runKb(e, code, "alt+" + key)
1478
+ if (code != 18) runKb("alt+" + key)
1213
1479
  } else if (code != 17) {
1214
- if (e.ctrlKey) runKb(e, code, "ctrl+" + key)
1215
- if (e[kbMod] && code != 91) runKb(e, code, "mod+" + key)
1480
+ if (e.ctrlKey) runKb("ctrl+" + key)
1481
+ if (e[kbMod] && code != 91) runKb("mod+" + key)
1216
1482
  }
1217
1483
  }
1484
+ function runKb(chr) {
1485
+ for (
1486
+ var fn, map
1487
+ , i = 0
1488
+ , el = e.target
1489
+ , input = /INPUT|TEXTAREA|SELECT/i.test((el.nodeType < 2 ? el : el.parentNode).tagName);
1490
+ (map = kbMaps[i++]) && !(
1491
+ fn = !input || map.input ? map[code] || map[chr] || map.num && code > 47 && code < 58 && (chr|=0, map.num) || map.all : fn
1492
+ ) && map.bubble; );
1493
+ if (isStr(fn)) setUrl(fn)
1494
+ if (isFn(fn)) fn(e, chr, el)
1495
+ }
1218
1496
  })
1219
1497
  /**/
1220
1498
 
@@ -1243,7 +1521,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1243
1521
  var len = e ? touches.push(e) : touches.length
1244
1522
  , MOVE = "pointermove"
1245
1523
  if (touchMode || len < 1) {
1246
- elEmit(touchEl, touchMode ? touchMode + END : "tap", e2, touchEv, touchEl)
1524
+ emit(touchEl, touchMode ? touchMode + END : "tap", e2, touchEv, touchEl)
1247
1525
  touchMode = UNDEF
1248
1526
  }
1249
1527
  if (len < 0) {
@@ -1270,7 +1548,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1270
1548
  function touchPos(name, offset) {
1271
1549
  var val = (
1272
1550
  touchEl.getBBox ?
1273
- touchEl.getAttributeNS(null, name == "top" ? "y":"x") :
1551
+ touchEl.getAttributeNS(NUL, name == "top" ? "y":"x") :
1274
1552
  touchEl.style[name]
1275
1553
  )
1276
1554
  touchEv[name] = parseInt(val, 10) || 0
@@ -1291,9 +1569,10 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1291
1569
  function touchWheel(e) {
1292
1570
  // IE10 enabled pinch-to-zoom gestures from multi-touch trackpad’s as mousewheel event with ctrlKey.
1293
1571
  // Chrome M35 and Firefox 55 followed up.
1572
+ // alt+wheel may be OS level zoom, use shiftKey as alternative
1294
1573
  if (!touches[0]) {
1295
- var ev = e.ctrlKey ? "pinch" : e.altKey ? "rotate" : UNDEF
1296
- if (ev && emit.call(e.currentTarget || e.target, ev, e, e.deltaY/20, 0)) {
1574
+ var ev = e.ctrlKey ? "pinch" : e.altKey || e.shiftKey ? "rotate" : UNDEF
1575
+ if (ev && emit(e.currentTarget || e.target, ev, e, e.deltaY/20, 0)) {
1297
1576
  return eventStop(e)
1298
1577
  }
1299
1578
  }
@@ -1315,9 +1594,9 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1315
1594
  )
1316
1595
  if (!touchMode) return
1317
1596
  clearTimeout(touchTick)
1318
- elEmit(touchEl, touchMode + START, e, touchEv, touchEl)
1597
+ emit(touchEl, touchMode + START, e, touchEv, touchEl)
1319
1598
  }
1320
- elEmit(touchEl, touchMode, e, touchEv, touchEl)
1599
+ emit(touchEl, touchMode, e, touchEv, touchEl)
1321
1600
  function haveEv(name, set) {
1322
1601
  return set && (evs[name] || evs[name + START] || evs[name + END]) && name
1323
1602
  }
@@ -1332,12 +1611,12 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1332
1611
 
1333
1612
  if (touchDist !== UNDEF) {
1334
1613
  diff = dist - touchDist
1335
- if (diff) elEmit(touchEl, "pinch", e, diff, angle)
1614
+ if (diff) emit(touchEl, "pinch", e, diff, angle)
1336
1615
  // GestureEvent onGestureChange: function(e) {
1337
1616
  // e.target.style.transform =
1338
1617
  // 'scale(' + e.scale + startScale + ') rotate(' + e.rotation + startRotation + 'deg)'
1339
1618
  diff = angle - touchAngle
1340
- if (diff) elEmit(touchEl, "rotate", e, diff * (180/Math.PI))
1619
+ if (diff) emit(touchEl, "rotate", e, diff * (180/Math.PI))
1341
1620
  }
1342
1621
  touchDist = dist
1343
1622
  touchAngle = angle
@@ -1345,6 +1624,9 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1345
1624
  }
1346
1625
  /**/
1347
1626
 
1627
+ function closest(el, sel) {
1628
+ return el && html.closest.call(el.nodeType < 2 ? el : el.parentNode, sel)
1629
+ }
1348
1630
  function find(root, sel, startNode) {
1349
1631
  return html.querySelector.call(startNode || root, sel)
1350
1632
  }
@@ -1354,28 +1636,29 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1354
1636
  function matches(el, sel) {
1355
1637
  return el && html.matches.call(el, sel)
1356
1638
  }
1357
- function closest(el, sel) {
1358
- return el && html.closest.call(el.nodeType < 2 ? el : el.parentNode, sel)
1359
- }
1360
- function walk(attr, el, sel) {
1361
- for (; el && !matches(el = el[attr], sel); );
1362
- return el
1639
+ function nearest(el, sel) {
1640
+ return el ? find(el, sel) || nearest(el.parentNode, sel) : NUL
1363
1641
  }
1364
1642
  function acceptMany(fn, prepareVal) {
1365
1643
  return function f(el, name, val, selector, delay, data) {
1366
1644
  if (el && name) {
1367
1645
  var i = arguments.length
1368
- if (i > 3 && i < 6 && isNum(selector)) {
1369
- data = delay
1370
- delay = selector
1371
- selector = UNDEF
1646
+ if (i > 3 && i < 6) {
1647
+ if (isArr(selector)) {
1648
+ data = selector
1649
+ delay = selector = UNDEF
1650
+ } else if (isNum(selector)) {
1651
+ data = delay
1652
+ delay = selector
1653
+ selector = UNDEF
1654
+ }
1372
1655
  }
1373
1656
  if (delay > 0) {
1374
1657
  setTimeout(f, delay, el, name, val, selector, 0, data)
1375
1658
  return
1376
1659
  }
1377
1660
  if (isObj(name)) {
1378
- for (delay in name) if (hasOwn.call(name, delay)) {
1661
+ for (delay in name) if (hasOwn(name, delay)) {
1379
1662
  f(el, delay, name[delay], selector, 0, data)
1380
1663
  }
1381
1664
  return
@@ -1392,17 +1675,16 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1392
1675
  }
1393
1676
  }
1394
1677
  function assignDeep(target, map) {
1395
- if (map) for (var k in map) if (hasOwn.call(map, k)) {
1396
- target[k] = isObj(map[k]) && isObj(target[k]) ? assignDeep(target[k], map[k]) : map[k]
1678
+ if (map) for (var k in map) if (hasOwn(map, k)) {
1679
+ if (isObj(map[k]) && isObj(target[k]) && hasOwn(target, k)) assignDeep(target[k], map[k])
1680
+ else target[k] = map[k]
1397
1681
  }
1398
1682
  return target
1399
1683
  }
1400
1684
  function blur() {
1401
1685
  // IE8 can throw on accessing document.activeElement.
1402
1686
  try {
1403
- var el = document.activeElement
1404
- , tag = el.tagName
1405
- if (tag === "A" || tag === "BUTTON") el.blur()
1687
+ document.activeElement.blur()
1406
1688
  } catch(e) {}
1407
1689
  }
1408
1690
  function camelFn(_, a) {
@@ -1412,40 +1694,19 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1412
1694
  if (arr) {
1413
1695
  if (isStr(arr)) arr = arr.split(splitRe)
1414
1696
  if (isArr(arr)) arr.forEach(fn, scope)
1415
- else for (key in arr) if (hasOwn.call(arr, key)) fn.call(scope, arr[key], key, arr)
1697
+ else for (key in arr) if (hasOwn(arr, key)) fn.call(scope, arr[key], key, arr)
1416
1698
  }
1417
1699
  }
1418
- function expand(str) {
1700
+ function expand(str, ns) {
1419
1701
  var first = str.charAt(0)
1420
1702
  , rest = str.slice(1)
1703
+ , lastExp = expand[ns]
1421
1704
  return (
1422
1705
  first === "+" ? lastExp + rest :
1423
1706
  first === "%" ? ((first = lastExp.lastIndexOf(rest.charAt(0))), (first > 0 ? lastExp.slice(0, first) : lastExp)) + rest :
1424
- (lastExp = str)
1707
+ (expand[ns] = str)
1425
1708
  )
1426
1709
  }
1427
- function format(str, data) {
1428
- return replace(str, formatRe, function(all, path) {
1429
- return get(data, path, "")
1430
- })
1431
- }
1432
- function get(obj, path, fallback) {
1433
- return isStr(path) ? (
1434
- obj[path] !== UNDEF ? obj[path] :
1435
- (path = path.split("."))[1] && isObj(obj = obj[path[0]]) && obj[path[1]] !== UNDEF ? obj[path[1]] : fallback
1436
- ) :
1437
- isArr(path) ? get(obj, path[0]) || get(obj, path[1]) || get(obj, path[2], fallback) :
1438
- fallback
1439
- }
1440
- function logErr(e, source, node) {
1441
- /*** debug ***/
1442
- console.error(e)
1443
- console.error(source, node)
1444
- /**/
1445
- if (window.onerror) {
1446
- window.onerror(e.message, e.fileName, e.lineNumber)
1447
- }
1448
- }
1449
1710
  function injectCss(cssText) {
1450
1711
  if (!styleNode) {
1451
1712
  // Safari and IE6-8 requires dynamically created
@@ -1468,6 +1729,9 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1468
1729
  function isStr(str) {
1469
1730
  return typeof str === "string"
1470
1731
  }
1732
+ function quote(str) {
1733
+ return "'" + replace(replace(str || "", /'/g, "\\'"), /\n/g, "\\n") + "'"
1734
+ }
1471
1735
  // Maximum call rate for Function with optional leading edge and trailing edge
1472
1736
  function rate(fn, ms, onStart, onEnd) {
1473
1737
  var tick
@@ -1508,7 +1772,7 @@ console.log("LiteJS is in debug mode, but it's fine for production")
1508
1772
  elKill(el)
1509
1773
  return el.src
1510
1774
  }), function(res) {
1511
- res = res.concat(sources, (next || res).innerHTML).filter(Boolean)
1775
+ res = res.concat(sources, next && next.src && next.innerHTML).filter(Boolean)
1512
1776
  if (res[sources.length = 0]) {
1513
1777
  if (!parser) LiteJS.ui = LiteJS()
1514
1778
  each(res, parser)