@litejs/ui 24.0.0-rc.6 → 24.7.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.
@@ -1,73 +1,77 @@
1
1
 
2
2
  /* litejs.com/MIT-LICENSE.txt */
3
3
 
4
- /* global xhr, getComputedStyle, navigator */
4
+ /* global xhr, navigator */
5
5
 
6
- !function(window, document, history, location, Function, Object) {
6
+ /*** debug ***/
7
+ console.log("LiteJS is in debug mode, but it's fine for production")
8
+ /**/
9
+
10
+ !function(window, document, history, localStorage, location, navigator, Function, Object) {
7
11
  window.El = El
8
- window.LiteJS = LiteJS
12
+ asEmitter(window.LiteJS = LiteJS)
9
13
 
10
- var UNDEF, lastExp, parser, styleNode
14
+ var UNDEF, lastExp, parser, pushBase, styleNode
11
15
  , html = document.documentElement
12
16
  , body = document.body
13
- , histBase, histCb, histLast
14
17
  , splitRe = /[,\s]+/
15
18
  , emptyArr = []
16
19
  , assign = Object.assign
17
20
  , create = Object.create
18
21
  , isArr = Array.isArray
19
22
  , 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)")
26
+ , replace = Function("a,b,c", "return a.replace(b,c)")
20
27
 
21
28
  // JScript engine in IE8 and below does not recognize vertical tabulation character `\v`.
22
29
  // http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
23
30
  , ie678 = !+"\v1" // jshint ignore:line
24
- // The documentMode is an IE only property, supported in IE8 and up.
25
- , ie67 = ie678 && (document.documentMode | 0) < 8 // jshint ignore:line
26
-
27
- , escapeRe = /[.*+?^=!:${}()|\[\]\/\\]/g
28
- , routeRe = /\{([\w%.]+?)\}|.[^{\\]*?/g
29
31
 
30
32
  , BIND_ATTR = "data-bind"
31
33
  , elSeq = 0
32
34
  , elCache = {}
33
- , renderRe = /[;\s]*([-\w$]+)(?:([ :!])((?:(["'\/])(?:\\.|[^\\])*?\3|[^;])*))?/g
35
+ , formatRe = /{((?:("|')(?:\\\2|[\s\S])*?\2|[^"'{}])+?)}/g
36
+ , renderRe = /[;\s]*([-\w$]+)(?:([ :!])((?:(["'\/])(?:\\.|[^\\])*?\4|[^;])*))?/g
34
37
  , selectorRe = /([.#:[])([-\w]+)(?:([~^$*|]?)=(("|')(?:\\.|[^\\])*?\5|[-\w]+))?]?/g
35
38
  , templateRe = /([ \t]*)(%?)((?:("|')(?:\\.|[^\\])*?\4|[-\w:.#[\]~^$*|]=?)*) ?([\/>+=@^;]|)(([\])}]?).*?([[({]?))(?=\x1f|\n|$)+/g
36
39
  , fnCache = {}
37
- , fnRe = /('|")(?:\\.|[^\\])*?\1|\/(?:\\.|[^\\])+?\/[gim]*|\$el\b|\$[as]\b|\b(?:false|in|if|new|null|this|true|typeof|void|function|var|else|return)\b|\.\w+|\w+:/g
40
+ , 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
38
41
  , wordRe = /[a-z_$][\w$]*/ig
39
42
  , camelRe = /\-([a-z])/g
40
43
  // innerText is implemented in IE4, textContent in IE9, Node.text in Opera 9-10
41
44
  // Safari 2.x innerText results an empty string when style.display=="none" or Node is not in DOM
42
- , txtAttr = "textContent" in body ? "textContent" : "innerText"
45
+ , txtAttr = "textContent" in html ? "textContent" : "innerText"
43
46
  , bindingsCss = acceptMany(function(el, key, val) {
44
- el.style[key.replace(camelRe, camelFn)] = "" + val
47
+ el.style[replace(key, camelRe, camelFn)] = val
45
48
  })
46
- , bindingsOn = acceptMany(addEvent, function(el, selector, data, handler) {
47
- return isStr(handler) ? function(e) {
49
+ , bindingsOn = acceptMany(addEvent, function(el, val, selector, data) {
50
+ return isStr(val) ? function(e) {
48
51
  var target = selector ? closest(e.target, selector) : el
49
- if (target) emit.apply(elScope(el).$ui, [handler, e, target].concat(data))
52
+ if (target) emit.apply(elScope(el).$ui, [val, e, target].concat(data))
50
53
  } :
51
- selector ? function(e) {
52
- if (matches(e.target, selector)) handler(e)
54
+ selector ? function(e, a1, a2) {
55
+ if (matches(e.target, selector)) val(e, a1, a2)
53
56
  } :
54
- handler
57
+ val
55
58
  })
56
59
  , bindings = {
57
- attr: acceptMany(setAttr),
58
60
  cls: acceptMany(cls),
59
61
  css: bindingsCss,
60
62
  on: bindingsOn,
63
+ set: acceptMany(setAttr),
61
64
  txt: elTxt,
62
65
  val: elVal,
63
66
  view: function(el, url) {
64
- setAttr(el, "href", (histBase || "#") + expand(url||""))
67
+ setAttr(el, "href", (pushBase || "#") + expand(url || ""))
65
68
  }
66
69
  }
67
70
  , globalScope = {
68
71
  El: El,
69
- _: String,
70
- _b: bindings
72
+ _: format,
73
+ _f: format,
74
+ $b: bindings
71
75
  }
72
76
  , elArr = {
73
77
  append: function(el) {
@@ -89,11 +93,6 @@
89
93
  , sources = []
90
94
  , hasOwn = plugins.hasOwnProperty
91
95
 
92
- // After iOS 13 iPad with default enabled "desktop" option
93
- // is the only Macintosh with multi-touch
94
- , iOS = /^(Mac|iP)/.test(navigator.platform)
95
- // || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
96
-
97
96
  , Event = window.Event || window
98
97
  , fixEv = Event.fixEv || (Event.fixEv = {})
99
98
  , fixFn = Event.fixFn || (Event.fixFn = {})
@@ -114,11 +113,6 @@
114
113
  Event.asEmitter = asEmitter
115
114
  Event.stop = eventStop
116
115
 
117
- if (iOS) {
118
- // iOS doesn't support beforeunload, use pagehide instead
119
- fixEv.beforeunload = "pagehide"
120
- }
121
-
122
116
  xhr.css = injectCss
123
117
  xhr.ui = sources.push.bind(sources)
124
118
 
@@ -181,7 +175,7 @@
181
175
  return _e / 3
182
176
  }
183
177
 
184
- function addEvent(el, ev, fn) {
178
+ function addEvent(el, ev, fn, opts) {
185
179
  var fn2 = fixFn[ev] && fixFn[ev](el, fn, ev) || fn
186
180
  , ev2 = fixEv[ev] || ev
187
181
 
@@ -189,7 +183,7 @@
189
183
  // polyfilled addEventListener returns patched function
190
184
  // Since Chrome 56 touchstart/move have the { passive: true } by default.
191
185
  // preventDefault() won't work unless you set passive to false.
192
- fn2 = body.addEventListener.call(el, ev2, fn2, false) || fn2
186
+ fn2 = html.addEventListener.call(el, ev2, fn2, opts != UNDEF ? opts : false) || fn2
193
187
  }
194
188
 
195
189
  on.call(el, ev, fn2, el, fn)
@@ -204,7 +198,7 @@
204
198
  evs[id + 1]._rm()
205
199
  }
206
200
  if (ev2 !== "" && "on" + ev2 in el) {
207
- body.removeEventListener.call(el, ev2, evs[id + 1])
201
+ html.removeEventListener.call(el, ev2, evs[id + 1])
208
202
  }
209
203
  evs.splice(id - 1, 3)
210
204
  }
@@ -220,7 +214,7 @@
220
214
 
221
215
  function LiteJS(opts) {
222
216
  opts = assign({
223
- base: "",
217
+ path: "",
224
218
  /*** breakpoints ***/
225
219
  breakpoints: {
226
220
  sm: 0,
@@ -231,14 +225,6 @@
231
225
  home: "home",
232
226
  root: body
233
227
  }, opts)
234
- var root = opts.root
235
- , viewFn, lastView, lastUrl, syncResume
236
- , viewSeq = 1
237
- , fnStr = ""
238
- , reStr = ""
239
- , views = View.views = {}
240
- , paramCb = {}
241
- , lastParams = paramCb
242
228
 
243
229
  function View(route, el, parent) {
244
230
  var view = views[route]
@@ -256,15 +242,13 @@
256
242
  view.p = parent && View(parent)
257
243
 
258
244
  if (route.charAt(0) !== "#") {
259
- var params = "m[" + (view.s = viewSeq++) + "]?("
260
- , _re = route.replace(routeRe, function(_, expr) {
245
+ fnStr += "m[" + (view.s = routeSeq++) + "]?("
246
+ reStr += "|(" + replace(route, routeRe, function(_, expr) {
261
247
  return expr ?
262
- (params += "o['" + expr + "']=m[" + (viewSeq++) + "],") && "([^/]+?)" :
263
- _.replace(escapeRe, "\\$&")
264
- })
265
-
266
- fnStr += params + "'" + route + "'):"
267
- reStr += (reStr ? "|(" : "(") + _re + ")"
248
+ (fnStr += "p['" + expr + "']=m[" + (routeSeq++) + "],") && "([^/]+?)" :
249
+ replace(_, reEsc, "\\$&")
250
+ }) + ")"
251
+ fnStr += "'" + route + "'):"
268
252
  viewFn = 0
269
253
  }
270
254
  }
@@ -276,42 +260,39 @@
276
260
  , params = lastParams = _params || {} // jshint ignore:line
277
261
  , view = lastView = this // jshint ignore:line
278
262
  , tmp = params._v || view // Continue bubbleUp from _v
279
- , close = view.o && view
280
263
 
264
+ params._c = view.o ? view : params._c
281
265
  for (View.route = view.r; tmp; tmp = parent) {
282
266
  viewEmit(syncResume = params._v = tmp, "ping", params, View)
283
- syncResume = null
267
+ syncResume = UNDEF
284
268
  if (lastParams !== params) return
285
269
  if ((parent = tmp.p)) {
286
270
  if (parent.c && parent.c !== tmp) {
287
- close = parent.c
271
+ params._c = parent.c
288
272
  }
289
273
  parent.c = tmp
290
274
  }
291
- if (!tmp.e) {
292
- if (tmp.f) {
293
- xhr.load(
294
- tmp.f.replace(/^|,/g, "$&" + (View.base || "")).split(","),
295
- readTemplates.bind(view, view.wait(tmp.f = null))
296
- )
297
- } else {
298
- if (tmp.r === "404") {
299
- viewParse("%view 404 #\nh2 Not found")
300
- }
301
- View("404").show({origin:params})
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")
302
283
  }
303
- return
284
+ return View("404").show({origin:params})
304
285
  }
305
286
  }
306
287
 
307
288
  for (tmp in params) {
308
289
  if (tmp.charAt(0) !== "_" && (syncResume = hasOwn.call(paramCb, tmp) && paramCb[tmp] || paramCb["*"])) {
309
- syncResume.call(view, params[tmp], tmp, params, View)
310
- syncResume = null
290
+ syncResume(params[tmp], tmp, view, params)
291
+ syncResume = UNDEF
311
292
  }
312
293
  }
313
294
  viewEmit(view, "nav")
314
- bubbleDown(params, close)
295
+ bubbleDown(params)
315
296
  },
316
297
  wait: function() {
317
298
  var params = lastParams
@@ -327,10 +308,23 @@
327
308
  }
328
309
  })
329
310
 
330
- assign(View, {
311
+ var root = opts.root
312
+ , viewFn, lastView, lastUrl, syncResume
313
+ , fnStr = ""
314
+ , reStr = ""
315
+ , reEsc = /[.*+?^${}()|[\]/\\]/g
316
+ , routeRe = /\{([\w%.]+?)\}|.[^{\\]*?/g
317
+ , routeSeq = 1
318
+
319
+ , views = View.views = {}
320
+ , paramCb = {}
321
+ , lastParams = paramCb
322
+ , $d = elScope(View("#", root).e, root)
323
+
324
+ $d.$ui = assign(View, {
331
325
  $: find.bind(View, root),
332
326
  $$: findAll.bind(View, root),
333
- data: elScope(View("#", root).e, globalScope),
327
+ $d: $d,
334
328
  def: viewDef,
335
329
  get: viewGet,
336
330
  param: function(names, cb) {
@@ -344,7 +338,6 @@
344
338
  },
345
339
  show: viewShow
346
340
  })
347
- View.data.$ui = View
348
341
 
349
342
  each(opts, function(val, opt) {
350
343
  if (isFn(View[opt])) {
@@ -364,8 +357,9 @@
364
357
  current + sep + val
365
358
  ) : val))
366
359
  }
367
- function bubbleDown(params, close) {
360
+ function bubbleDown(params) {
368
361
  var view = params._v
362
+ , close = params._c
369
363
  , parent = view && view.p
370
364
  if (!view || params._p && /{/.test(view.r)) {
371
365
  return viewClose(close)
@@ -377,10 +371,10 @@
377
371
  viewEmit(parent, "openChild", view, close)
378
372
  viewEmit(view, "open", params)
379
373
  addKb(view.kb)
380
- close = null
374
+ params._c = UNDEF
381
375
  }
382
376
  if ((params._d = params._v = view.c)) {
383
- bubbleDown(params, close)
377
+ bubbleDown(params)
384
378
  }
385
379
  if ((lastView === view)) {
386
380
  viewEmit(view, "show", params)
@@ -393,7 +387,7 @@
393
387
  viewEmit(view.p, "closeChild", view, open)
394
388
  viewClose(view.c)
395
389
  elKill(view.o)
396
- view.o = null
390
+ view.o = UNDEF
397
391
  rmKb(view.kb)
398
392
  viewEmit(view, "close")
399
393
  }
@@ -412,15 +406,20 @@
412
406
  function viewEmit(view, event, a, b) {
413
407
  view.emit(event, a, b)
414
408
  View.emit(event, view, a, b)
409
+ LiteJS.emit(event, view, a, b)
415
410
  }
416
411
  function viewEval(str, scope) {
417
- Function("$s,$ui,$data,$,$$", str)(scope, View, View.data, View.$, View.$$)
412
+ try {
413
+ Function("$s,$ui,$d,$,$$", str)(scope, View, $d, View.$, View.$$)
414
+ } catch(e) {
415
+ logErr(e, "viewEval: " + str)
416
+ }
418
417
  }
419
418
  function viewGet(url, params) {
420
419
  if (!viewFn) {
421
420
  viewFn = Function(
422
421
  "var r=/^\\/?(?:" + reStr + ")[\\/\\s]*$/;" +
423
- "return function(i,o,d){var m=r.exec(i);return m!==null?(" + fnStr + "d):d}"
422
+ "return function(u,p,d){var m=r.exec(u);return m!==null?(" + fnStr + "d):d}"
424
423
  )()
425
424
  }
426
425
  return View(url ? viewFn(url, params || {}, "404") : View.home)
@@ -441,7 +440,9 @@
441
440
  parent = parentStack.pop()
442
441
  stack.shift()
443
442
  }
444
-
443
+ if (op === "@") {
444
+ text = replace(text, /([\w,.]+):?/, "on!'$1',")
445
+ }
445
446
  if (parent.r) {
446
447
  parent.t += "\n" + all
447
448
  } else if (plugin || mapStart && (sel = "map")) {
@@ -458,25 +459,23 @@
458
459
  if (sel) {
459
460
  parentStack.push(parent)
460
461
  stack.unshift(q)
461
- append(parent, parent = q = El(sel))
462
+ append(parent, parent = El(sel))
462
463
  }
463
464
  if (text && op != "/") {
464
465
  if (op === ">" || op === "+") {
465
- (indent + (op === "+" ? text : " " + text)).replace(templateRe, work)
466
+ replace(indent + (op === "+" ? text : " " + text), templateRe, work)
466
467
  } else if (op === "=") {
467
468
  append(parent, text) // + "\n")
468
469
  } else {
469
- if (op === "@") {
470
- text = text.replace(/([\w,]+):?/, "on!'$1',")
471
- } else if (op === "") {
472
- text = "txt _('" + text.replace(/'/g, "\\'") + "',$s)"
470
+ if (op === "") {
471
+ text = "txt _('" + replace(text, /'/g, "\\'") + "',$s)"
473
472
  }
474
473
  appendBind(parent, text, ";", op)
475
474
  }
476
475
  }
477
476
  }
478
477
  }
479
- str.replace(templateRe, work)
478
+ replace(str, templateRe, work)
480
479
  work("", "")
481
480
  if (parent.childNodes[0]) {
482
481
  append(root, parent.childNodes)
@@ -498,8 +497,8 @@
498
497
  var params = _params || {}
499
498
  , view = viewGet(url, params)
500
499
  if (!view.o || lastUrl !== url) {
501
- globalScope.url = lastExp = lastUrl = url
502
- view.show(globalScope.params = params)
500
+ $d.url = lastExp = lastUrl = url
501
+ view.show($d.params = params)
503
502
  }
504
503
  }
505
504
 
@@ -534,13 +533,13 @@
534
533
  , contentPos = el._cp
535
534
 
536
535
  if (contentPos > -1) {
537
- if (childNodes[contentPos].nodeType == 1 && el._sk) {
536
+ if (childNodes[contentPos].nodeType < 2 && el._sk) {
538
537
  setAttr(childNodes[contentPos], "data-slot", el._sk)
539
538
  }
540
539
  child._s = el._s
541
540
  }
542
541
  if (plugin.c) elCache = plugin.c
543
- el.p = plugin.e = plugin.u = null
542
+ el.p = plugin.e = plugin.u = UNDEF
544
543
  return child
545
544
  }
546
545
 
@@ -551,9 +550,9 @@
551
550
  d: function(plugin) {
552
551
  var slotName = plugin.n || ++elSeq
553
552
  , parent = plugin.u
554
- append(parent, Comm("%slot-" + slotName))
553
+ append(parent, Comm("slot" + slotName))
555
554
  // In IE6 root div is inside documentFragment
556
- for (; (parent.parentNode || plugin).nodeType === 1; parent = parent.parentNode);
555
+ for (; (parent.parentNode || plugin).nodeType < 2; parent = parent.parentNode);
557
556
  ;(parent._s || (parent._s = {}))[slotName] = parent.childNodes.length - 1
558
557
  if (!plugin.n) parent._s._ = parent._sk = slotName
559
558
  parent._cp = parent.childNodes.length - 1
@@ -566,7 +565,7 @@
566
565
  r: function(params) {
567
566
  var txt = this.t
568
567
  each(params, function(param) {
569
- viewParse(txt.replace(/{key}/g, param))
568
+ viewParse(replace(txt, /{key}/g, param))
570
569
  })
571
570
  }
572
571
  })
@@ -593,7 +592,7 @@
593
592
  var bind = getAttr(plugin.e, BIND_ATTR)
594
593
  , view = View(plugin.n, usePluginContent(plugin), plugin.x)
595
594
  if (bind) {
596
- bind = bind.replace(renderRe, function(_, name, op, args) {
595
+ bind = replace(bind, renderRe, function(_, name, op, args) {
597
596
  return "($s." + name + (isFn(view[name]) ? "(" + (args || "") + ")" : "=" + args) + "),"
598
597
  }) + "1"
599
598
  viewEval(bind, view)
@@ -604,14 +603,7 @@
604
603
  /*** breakpoints ***/
605
604
  var lastSize, lastOrient
606
605
  , breakpoints = opts.breakpoints
607
- , setBreakpointsRated = rate(setBreakpoints, 99)
608
-
609
- if (breakpoints) {
610
- setBreakpointsRated()
611
- bindingsOn(window, "load orientationchange resize", setBreakpointsRated)
612
- }
613
-
614
- function setBreakpoints() {
606
+ , setBreakpointsRated = rate(function() {
615
607
  // document.documentElement.clientWidth is 0 in IE5
616
608
  var point, next
617
609
  , width = html.offsetWidth
@@ -621,64 +613,75 @@
621
613
  next = point
622
614
  }
623
615
 
624
- if ( next != lastSize ) {
616
+ if (next != lastSize) {
625
617
  cls(html, lastSize, 0)
626
618
  cls(html, lastSize = next)
627
619
  }
628
620
 
629
621
  next = width > html.offsetHeight ? "land" : "port"
630
622
 
631
- if ( next != lastOrient) {
623
+ if (next != lastOrient) {
632
624
  cls(html, lastOrient, 0)
633
625
  cls(html, lastOrient = next)
634
626
  }
635
627
 
636
628
  View.emit("resize")
629
+ }, 99)
630
+
631
+ if (breakpoints) {
632
+ setBreakpointsRated()
633
+ bindingsOn(window, "orientationchange resize", setBreakpointsRated)
634
+ }
635
+ /**/
636
+
637
+ /*** 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)
645
+ }
646
+ })
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
653
+ }
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)
637
657
  }
638
658
  /**/
639
659
 
640
660
  return View
641
661
  }
642
662
 
643
- function getUrl() {
644
- return (
645
- /*** pushState ***/
646
- histBase ? location.pathname.slice(histBase.length) :
647
- /**/
648
- // bug in Firefox where location.hash is decoded
649
- // bug in Safari where location.pathname is decoded
650
- location.href.split("#")[1] || ""
651
- ).replace(/^[#\/\!]+|[\s\/]+$/g, "")
652
- }
653
- function setUrl(url, replace) {
663
+ function setUrl(url, rep, checkUrl) {
654
664
  /*** pushState ***/
655
- if (histBase) {
656
- history[replace ? "replaceState" : "pushState"](null, null, histBase + url)
665
+ if (pushBase) {
666
+ history[rep ? "replaceState" : "pushState"](null, null, pushBase + url)
657
667
  } else {
658
668
  /**/
659
- location[replace ? "replace" : "assign"]("#" + url)
669
+ location[rep ? "replace" : "assign"]("#" + url)
660
670
  /*** pushState ***/
661
671
  }
662
672
  /**/
663
- return checkUrl()
664
- }
665
- function checkUrl() {
666
- if (histLast != (histLast = getUrl())) {
667
- if (histCb) histCb(histLast)
668
- return true
669
- }
673
+ if (checkUrl) checkUrl()
670
674
  }
671
675
 
672
676
  LiteJS.go = setUrl
673
677
  LiteJS.start = histStart
674
678
  function histStart(cb) {
675
- histCb = cb
676
679
  /*** pushState ***/
677
680
  // Chrome5, Firefox4, IE10, Safari5, Opera11.50
678
- var url
681
+ var histLast, url
679
682
  , base = find(html, "base")
680
- LiteJS.base = (base || location).href.replace(/[^\/]*(#.*)?$/, "")
681
- if (base) base = base.href.replace(/.*:\/\/[^/]*|[^\/]*$/g, "")
683
+ LiteJS.base = replace((base || location).href, /[^\/]*(#.*)?$/, "")
684
+ if (base) base = replace(base.href, /.*:\/\/[^/]*|[^\/]*$/g, "")
682
685
  if (base && !history.pushState) {
683
686
  url = location.pathname.slice(base.length)
684
687
  if (url) {
@@ -686,7 +689,7 @@
686
689
  }
687
690
  }
688
691
  if (base && history.pushState) {
689
- histBase = base
692
+ pushBase = base
690
693
 
691
694
  url = location.href.split("#")[1]
692
695
  if (url && !getUrl()) {
@@ -705,9 +708,20 @@
705
708
  /**/
706
709
  window.onhashchange = checkUrl
707
710
  readTemplates(checkUrl)
711
+ function checkUrl() {
712
+ if (cb && histLast != (histLast = getUrl())) cb(histLast)
713
+ }
714
+ function getUrl() {
715
+ return replace(
716
+ /*** pushState ***/
717
+ pushBase ? location.pathname.slice(pushBase.length) :
718
+ /**/
719
+ // bug in Firefox where location.hash is decoded
720
+ // bug in Safari where location.pathname is decoded
721
+ location.href.split("#")[1] || "", /^[#\/\!]+|[\s\/]+$/g, "")
722
+ }
708
723
  }
709
724
 
710
-
711
725
  function Comm(name, render) {
712
726
  var comm = document.createComment(name)
713
727
  if (render) comm.render = render
@@ -716,7 +730,7 @@
716
730
  function El(name) {
717
731
  var attr
718
732
  , attrs = {}
719
- , el = name.replace(selectorRe, function(_, op, key, fn, val, quotation) {
733
+ , el = replace(name, selectorRe, function(_, op, key, fn, val, quotation) {
720
734
  attr = 1
721
735
  val = quotation ? val.slice(1, -1) : val || key
722
736
  attrs[op =
@@ -744,7 +758,7 @@
744
758
  }
745
759
  function ElWrap(nodes, clone) {
746
760
  for (var wrap = [], i = nodes.length; i--; ) {
747
- wrap[i] = clone < 2 ? nodes[i].cloneNode(clone) : nodes[i]
761
+ wrap[i] = clone ? nodes[i].cloneNode(clone) : nodes[i]
748
762
  }
749
763
  return assign(wrap, elArr)
750
764
  }
@@ -783,9 +797,9 @@
783
797
 
784
798
  assign(El, {
785
799
  append: append,
786
- bindings: assign(bindings, {
787
- "for": function(el, name, list) {
788
- var comm = Comm("for " + name, up)
800
+ $b: assign(bindings, {
801
+ each: function(el, name, list) {
802
+ var comm = Comm("each " + name, up)
789
803
  , pos = 0
790
804
  , nodes = []
791
805
  comm.$s = this
@@ -794,15 +808,14 @@
794
808
  return { a: add, r: remove, u: up }
795
809
 
796
810
  function add(item) {
797
- var clone = nodes[pos++] = el.cloneNode(true)
798
- , subScope = assign(elScope(clone, comm), {
799
- $i: pos,
800
- $len: list.length
801
- })
802
- clone[BIND_ATTR] = el[BIND_ATTR]
811
+ var clone = nodes[pos] = el.cloneNode(true)
812
+ , subScope = elScope(clone, comm)
813
+ subScope.$i = pos++
814
+ subScope.$len = list.length
803
815
  subScope[name] = item
816
+ clone[BIND_ATTR] = el[BIND_ATTR]
804
817
  append(comm.parentNode, clone, comm)
805
- render(clone, subScope)
818
+ render(clone)
806
819
  }
807
820
  function remove(i) {
808
821
  elKill(nodes.splice(i, 1)[0])
@@ -812,14 +825,31 @@
812
825
  each(list, add)
813
826
  }
814
827
  },
828
+ 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
838
+ },
815
839
  "if": function(el, enabled) {
816
840
  if (enabled) {
817
841
  elReplace(el._if, el)
818
842
  } else {
819
- elReplace(el, el._if || (el._if = Comm("if", render.bind(el, el, this))))
843
+ elReplace(el, el._if || (el._if = Comm("if", render.bind(el, el))))
820
844
  return true
821
845
  }
822
846
  },
847
+ is: function(el, val, opts, prefix) {
848
+ if (!prefix) prefix = "is-"
849
+ var match = format(val, opts)
850
+ cls(el, el[prefix + opts], 0)
851
+ cls(el, el[prefix + opts] = match && prefix + match)
852
+ },
823
853
  ref: function(el, name) {
824
854
  this[name] = el
825
855
  },
@@ -836,25 +866,21 @@
836
866
  blur: blur,
837
867
  cache: elCache,
838
868
  closest: closest,
839
- data: globalScope,
869
+ $d: globalScope,
840
870
  get: getAttr,
841
871
  hasClass: hasClass,
842
872
  matches: matches,
873
+ next: walk.bind(El, "nextSibling"),
874
+ prev: walk.bind(El, "previousSibling"),
843
875
  rate: rate,
844
876
  replace: elReplace,
845
877
  scope: elScope,
846
878
  scrollLeft: scrollLeft,
847
879
  scrollTop: scrollTop,
848
880
  step: step,
849
- style: function(el, key) {
850
- return getComputedStyle(el).getPropertyValue(key)
851
- }
881
+ stop: eventStop
852
882
  })
853
883
 
854
- function getAttr(el, key) {
855
- return el && el.getAttribute && el.getAttribute(key)
856
- }
857
-
858
884
  function setAttr(el, key, val) {
859
885
  var current = getAttr(el, key)
860
886
 
@@ -862,9 +888,9 @@
862
888
  // Bug: IE5-7 doesn't set styles and removes events when you try to set them.
863
889
  // IE6 label with a for attribute will re-select the first option of SELECT list instead of just giving focus.
864
890
  // http://webbugtrack.blogspot.com/2007/09/bug-116-for-attribute-woes-in-ie6.html
891
+ // IE8 and below have a bug where changed 'name' not accepted on form submit
865
892
  /* c8 ignore next 3 */
866
- if (ie67 && (key === "id" || key === "name" || key === "checked" || key === "style")) {
867
- // IE8 and below have a bug where changed 'name' not accepted on form submit
893
+ if (ie678 && (key === "id" || key === "name" || key === "checked" || key === "style")) {
868
894
  el.mergeAttributes(document.createElement("<INPUT " + key + "='" + val + "'>"), false)
869
895
  } else
870
896
  /**/
@@ -889,14 +915,14 @@
889
915
  , i = 0
890
916
  if (child) {
891
917
  if (isStr(child) || isNum(child)) child = document.createTextNode(child)
892
- else if ( !child.nodeType && (i = child.length) ) {
918
+ else if (!child.nodeType && (i = child.length)) {
893
919
  for (tmp = document.createDocumentFragment(); i--; ) append(tmp, child[i], 0)
894
920
  child = tmp
895
921
  }
896
922
 
897
923
  if (child.nodeType) {
898
924
  if ((i = setAttr(child, "slot", "") || getAttr(el, "data-slot"))) {
899
- i = "%slot-" + i
925
+ i = "slot" + i
900
926
  for (tmp = el.firstChild; tmp; tmp = next) {
901
927
  if (tmp.nodeType === 8 && tmp.nodeValue === i) {
902
928
  el = tmp
@@ -911,9 +937,11 @@
911
937
  before = el
912
938
  el = before.parentNode
913
939
  }
914
- el.insertBefore(child, (isNum(before) ? el.childNodes[
915
- before < 0 ? el.childNodes.length - before - 2 : before
916
- ] : before) || null)
940
+ el.insertBefore(child, (
941
+ isNum(before) ? el.childNodes[before < 0 ? el.childNodes.length - before - 2 : before] :
942
+ isArr(before) ? before[0] :
943
+ before
944
+ ) || null)
917
945
  /*** debug ***/
918
946
  if (el.namespaceURI && child.namespaceURI && el.namespaceURI !== child.namespaceURI && el.tagName !== "foreignObject" && child.tagName !== "svg") {
919
947
  console.error("NAMESPACE CHANGE!", el, child)
@@ -938,17 +966,19 @@
938
966
  // className is object on SVGElements
939
967
  var current = el.className || ""
940
968
  , useAttr = !isStr(current)
969
+ , SP = " "
941
970
 
942
971
  if (useAttr) {
943
- current = el.getAttribute("class") || ""
972
+ current = getAttr(el, "class") || ""
944
973
  }
945
974
 
946
975
  if (set === UNDEF || set) {
976
+ if (set && set !== el && set.nodeType < 2) cls(set, name, 0)
947
977
  if (current) {
948
- name = current.split(splitRe).indexOf(name) > -1 ? current : current + " " + name
978
+ name = current.split(SP).indexOf(name) > -1 ? current : current + SP + name
949
979
  }
950
980
  } else {
951
- name = current ? (" " + current + " ").replace(" " + name + " ", " ").trim() : current
981
+ name = current ? replace(SP + current + SP, SP + name + SP, SP).trim() : current
952
982
  }
953
983
 
954
984
  if (current != name) {
@@ -964,42 +994,34 @@
964
994
  emit.apply(el, slice.call(arguments, 1))
965
995
  }
966
996
  function elEmpty(el) {
967
- ElWrap(el.childNodes).kill()
997
+ for (; el.lastChild; elKill(el.lastChild));
968
998
  }
969
999
  function elKill(el, tr, delay) {
970
1000
  if (el) {
971
1001
  if (delay > 0) return setTimeout(elKill, delay, el, tr)
972
1002
  if (tr) {
973
- cls(el, tr, tr = "transitionend")
1003
+ if (isStr(tr)) cls(el, tr)
1004
+ if (isObj(tr)) bindingsCss(el, tr)
1005
+ tr = "transitionend"
974
1006
  // transitionend fires for each property transitioned
975
- if ("on" + tr in el) return addEvent(el, tr, elKill.bind(el, el, el = null))
1007
+ if ("on" + tr in el) return addEvent(el, tr, elKill.bind(el, el, el = UNDEF))
976
1008
  }
977
1009
  if (el._e) {
978
1010
  emit.call(el, "kill")
979
- for (delay in el._e) rmEvent(el, delay)
1011
+ el._e = UNDEF
980
1012
  }
981
1013
  elRm(el)
982
- if (el.nodeType != 1) {
983
- if (el.kill) el.kill()
984
- } else {
1014
+ el.$s = UNDEF
1015
+ if (el.nodeType < 2) {
985
1016
  elEmpty(el)
986
- if (el.$s !== UNDEF) {
987
- el.$s = UNDEF
988
- }
989
1017
  if (el.valObject !== UNDEF) {
990
1018
  el.valObject = UNDEF
991
1019
  }
1020
+ } else {
1021
+ if (el.kill) el.kill()
992
1022
  }
993
1023
  }
994
1024
  }
995
- function elReplace(el, newEl) {
996
- var parent = el && el.parentNode
997
- if (parent && newEl) return parent.replaceChild(newEl, el)
998
- }
999
- function elRm(el) {
1000
- var parent = el && el.parentNode
1001
- if (parent) parent.removeChild(el)
1002
- }
1003
1025
  function elScope(el, parent) {
1004
1026
  return el.$s || (
1005
1027
  parent ? ((parent = elScope(parent)), el.$s = assign(create(parent), { $up: parent })) :
@@ -1030,7 +1052,7 @@
1030
1052
  value = elVal(input, val != UNDEF ? val[key] : UNDEF)
1031
1053
  if (value !== UNDEF) {
1032
1054
  step = opts
1033
- key.replace(/\[(.*?)\]/g, replacer)
1055
+ replace(key, /\[(.*?)\]/g, replacer)
1034
1056
  step[key || step.length] = value
1035
1057
  }
1036
1058
  }
@@ -1039,8 +1061,7 @@
1039
1061
 
1040
1062
  if (val !== UNDEF) {
1041
1063
  if (opts) {
1042
- value = (isArr(val) ? val : [ val ]).map(String)
1043
- for (; (input = opts[i++]); ) {
1064
+ for (value = (isArr(val) ? val : [ val ]).map(String); (input = opts[i++]); ) {
1044
1065
  input.selected = value.indexOf(input.value) > -1
1045
1066
  }
1046
1067
  } else if (el.val) {
@@ -1086,15 +1107,14 @@
1086
1107
  }
1087
1108
 
1088
1109
  function render(node, $s) {
1089
- if (!node) return
1090
- var bind, next
1091
- , scope = node.$s || $s || closestScope(node)
1092
-
1093
- if (node.nodeType != 1) {
1094
- if (node.render) node.render(scope)
1110
+ if (!node || node.nodeType != 1) {
1111
+ if (node && node.render) node.render()
1095
1112
  return
1096
1113
  }
1097
1114
 
1115
+ var bind, next
1116
+ , scope = node.$s || $s || closestScope(node)
1117
+
1098
1118
  /*** ie8 ***/
1099
1119
  if (ie678 && node.tagName === "SELECT") {
1100
1120
  node.parentNode.insertBefore(node, node)
@@ -1114,41 +1134,34 @@
1114
1134
  , bind = node[attr] || (node[attr] = setAttr(node, attr, "") || true)
1115
1135
  if (bind !== true) try {
1116
1136
  fn = fnCache[bind] || (fnCache[bind] = makeFn(bind))
1117
- scope.$o = fn.o
1118
- return fn(node, scope, attr)
1137
+ return fn(node, scope, attr, fn.o)
1119
1138
  } catch (e) {
1120
- /*** debug ***/
1121
- console.error(e)
1122
- console.error("BINDING: " + bind, node)
1123
- /**/
1124
- if (window.onerror) {
1125
- window.onerror(e.message, e.fileName, e.lineNumber)
1126
- }
1139
+ logErr(e, attr + ": " + bind, node)
1127
1140
  }
1128
1141
  }
1129
1142
  function makeFn(fn) {
1130
1143
  var i = 0
1131
1144
  , bindOnce = []
1132
- fn = "$s&&(" + fn.replace(renderRe, function(match, name, op, args) {
1145
+ fn = "$s&&(" + replace(fn, renderRe, function(match, name, op, args) {
1133
1146
  return (
1134
1147
  (op === "!" && (bindOnce[i] = match)) ?
1135
1148
  "($el[$a]=$el[$a].replace($o[" + (i++)+ "],''),0)||" :
1136
1149
  ""
1137
- ) + "_b['" + (bindings[name] ? name + "'].call($s,$el" : "attr']($el,'" + name + "'") + (args ? "," + args : "") + ")||"
1150
+ ) + "$b['" + (bindings[name] ? name + "'].call($s,$el" : "set']($el,'" + name + "'") + (args ? "," + replace(args, /^\s*(\w+) in /,"'$1',") : "") + ")||"
1138
1151
  }) + "$r)"
1139
- var vars = fn.replace(fnRe, "").match(wordRe) || []
1152
+ var vars = replace(fn, fnRe, "").match(wordRe) || []
1140
1153
  for (i = vars.length; i--; ) {
1141
1154
  if (vars.indexOf(vars[i]) !== i) vars.splice(i, 1)
1142
1155
  else vars[i] += "=$s." + vars[i]
1143
1156
  }
1144
- fn = Function("$el,$s,$a,$r", "var " + vars + ";return " + fn)
1157
+ fn = Function("$el,$s,$a,$o,$r", "var " + vars + ";return " + fn)
1145
1158
  fn.o = bindOnce
1146
1159
  return fn
1147
1160
  }
1148
1161
 
1149
1162
  /*** kb ***/
1150
1163
  var kbMaps = []
1151
- , kbMod = LiteJS.kbMod = iOS ? "metaKey" : "ctrlKey"
1164
+ , kbMod = LiteJS.kbMod = /^(Mac|iP)/.test(navigator.platform) ? "metaKey" : "ctrlKey"
1152
1165
  , kbCodes = LiteJS.kbCodes = ",,,,,,,,backspace,tab,,,,enter,,,shift,ctrl,alt,pause,caps,,,,,,,esc,,,,,,pgup,pgdown,end,home,left,up,right,down,,,,,ins,del,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,cmd,,,,,,,,,,,,,,,,,,,,,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12".split(splitRe)
1153
1166
 
1154
1167
  El.addKb = addKb
@@ -1168,8 +1181,8 @@
1168
1181
  function runKb(e, code, chr) {
1169
1182
  var fn, map
1170
1183
  , i = 0
1171
- , el = e.target || e.srcElement
1172
- , input = /INPUT|TEXTAREA|SELECT/i.test((el.nodeType == 3 ? el.parentNode : el).tagName)
1184
+ , el = e.target
1185
+ , input = /INPUT|TEXTAREA|SELECT/i.test((el.nodeType < 2 ? el : el.parentNode).tagName)
1173
1186
 
1174
1187
  for (; (map = kbMaps[i++]) && (
1175
1188
  !(fn = !input || map.input ? map[code] || map[chr] || map.num && code > 47 && code < 58 && (chr|=0, map.num) || map.all : fn) &&
@@ -1206,195 +1219,184 @@
1206
1219
  /**/
1207
1220
 
1208
1221
  /*** touch ***/
1209
- var touchEl, touchDist, touchAngle, pinchThreshhold, touchMode
1210
- , TOUCH_FLAG = "-tf"
1211
- , MOVE = "pointermove"
1222
+ var touchEl, touchDist, touchAngle, touchMode, touchTick
1212
1223
  , START = "start"
1213
1224
  , END = "end"
1214
- , MS_WHICH = [0, 1, 4, 2]
1215
1225
  , touches = []
1216
1226
  , touchEv = {}
1217
1227
 
1218
- // tap
1219
- // swipe + left/right/up/down
1220
-
1221
- "pan pinch rotate".split(" ").map(function(name) {
1228
+ // tap, swipe + left/right/up/down
1229
+ each("pan pinch rotate tap", function(name) {
1222
1230
  fixEv[name] = fixEv[name + START] = fixEv[name + END] = ""
1223
1231
  fixFn[name] = touchInit
1224
1232
  })
1225
-
1226
1233
  function touchInit(el) {
1227
- if (!el[TOUCH_FLAG]) {
1234
+ if (!el._ti) {
1228
1235
  addEvent(el, "pointerdown", touchDown)
1229
1236
  addEvent(el, "wheel", touchWheel)
1230
1237
  bindingsOn(el, "pointerup pointercancel", touchUp)
1231
1238
  bindingsCss(el, "touchAction msTouchAction", "none")
1232
- el[TOUCH_FLAG] = 1
1233
- }
1234
- }
1235
-
1236
- function touchDown(e, e2) {
1237
- var len = e ? touches.push(e) : touches.length
1238
- touchEv.cancel = false
1239
-
1240
- if (touchMode) {
1241
- elEmit(touchEl, touchMode + END, e2, touchEv, touchEl)
1242
- touchMode = UNDEF
1243
- }
1244
- if (len === 0) {
1245
- touchEl = null
1246
- }
1247
- if (len === 1) {
1248
- if (e) {
1249
- touchEl = e.currentTarget || e.target
1250
- if (e.button === 2 || matches(e.target, "INPUT,TEXTAREA,SELECT,.no-drag")) return
1251
- } else {
1252
- e = touches[0]
1239
+ el._ti = 1
1240
+ }
1241
+ function touchDown(e, e2) {
1242
+ clearTimeout(touchTick)
1243
+ var len = e ? touches.push(e) : touches.length
1244
+ , MOVE = "pointermove"
1245
+ if (touchMode || len < 1) {
1246
+ elEmit(touchEl, touchMode ? touchMode + END : "tap", e2, touchEv, touchEl)
1247
+ touchMode = UNDEF
1253
1248
  }
1254
- touchEv.X = e.clientX
1255
- touchEv.Y = e.clientY
1256
- touchPos("left", "offsetWidth")
1257
- touchPos("top", "offsetHeight")
1258
- moveOne(e)
1259
- }
1260
- if (len === 2) {
1261
- pinchThreshhold = touchEl.clientWidth / 10
1262
- touchDist = touchAngle = null
1263
- moveTwo(e)
1264
- }
1265
- ;(len === 1 ? addEvent : rmEvent)(document, MOVE, moveOne)
1266
- ;(len === 2 ? addEvent : rmEvent)(document, MOVE, moveTwo)
1267
- return eventStop(e)
1268
- }
1269
-
1270
- function touchUp(e) {
1271
- for (var i = touches.length; i--; ) {
1272
- if (touches[i].pointerId == e.pointerId) {
1273
- touches.splice(i, 1)
1274
- break
1249
+ if (len < 0) {
1250
+ touchEl = UNDEF
1275
1251
  }
1276
- }
1277
- touchDown(null, e)
1278
- }
1279
-
1280
- function touchWheel(e) {
1281
- // IE10 enabled pinch-to-zoom gestures from multi-touch trackpad’s as mousewheel event with ctrlKey.
1282
- // Chrome M35 and Firefox 55 followed up.
1283
- if (!touches[0]) {
1284
- var ev = e.ctrlKey ? "pinch" : e.altKey ? "rotate" : UNDEF
1285
- if (ev && emit.call(e.currentTarget || e.target, ev, e, e.deltaY/20, 0)) {
1286
- return eventStop(e)
1252
+ if (len === 1) {
1253
+ if (e) {
1254
+ touchEl = e.currentTarget || e.target
1255
+ touchEv.X = e.clientX
1256
+ touchEv.Y = e.clientY
1257
+ touchPos("left", "offsetWidth")
1258
+ touchPos("top", "offsetHeight")
1259
+ if (e.button === 2 || matches(touchEl, "INPUT,TEXTAREA,SELECT,.no-drag")) return
1260
+ touchTick = setTimeout(moveOne, 1500, e, 1)
1261
+ }
1262
+ moveOne(e || touches[0])
1263
+ }
1264
+ if (len === 2) {
1265
+ touchDist = touchAngle = UNDEF
1266
+ moveTwo(e)
1267
+ }
1268
+ ;(len === 1 ? addEvent : rmEvent)(document, MOVE, moveOne)
1269
+ ;(len === 2 ? addEvent : rmEvent)(document, MOVE, moveTwo)
1270
+ function touchPos(name, offset) {
1271
+ var val = (
1272
+ touchEl.getBBox ?
1273
+ touchEl.getAttributeNS(null, name == "top" ? "y":"x") :
1274
+ touchEl.style[name]
1275
+ )
1276
+ touchEv[name] = parseInt(val, 10) || 0
1277
+ if (val && val.indexOf("%") > -1) {
1278
+ touchEv[name] *= touchEl.parentNode[offset] / 100
1279
+ }
1287
1280
  }
1288
1281
  }
1289
- }
1290
-
1291
- function moveOne(e) {
1292
- // In IE9 mousedown.buttons is OK but mousemove.buttons == 0
1293
- if (touches[0].buttons && touches[0].buttons !== (e.buttons || MS_WHICH[e.which || 0])) {
1294
- return touchUp(e)
1295
- }
1296
- touchEv.leftPos = e.clientX - touchEv.X + touchEv.left
1297
- touchEv.topPos = e.clientY - touchEv.Y + touchEv.top
1298
- if (!touchMode) {
1299
- touchMode = "pan"
1300
- elEmit(touchEl, touchMode + START, e, touchEv, touchEl)
1301
- }
1302
- elEmit(touchEl, "pan", e, touchEv, touchEl)
1303
- if (!touchEv.cancel) {
1304
- if (touchEl.getBBox) {
1305
- El.attr(touchEl, {
1306
- x: touchEv.leftPos,
1307
- y: touchEv.topPos
1308
- }, 0)
1309
- } else {
1310
- bindingsCss(touchEl, "top,left", [touchEv.topPos + "px", touchEv.leftPos + "px"], 0)
1282
+ function touchUp(e) {
1283
+ for (var i = touches.length; i--; ) {
1284
+ if (touches[i].pointerId == e.pointerId) {
1285
+ touches.splice(i, 1)
1286
+ break
1287
+ }
1288
+ }
1289
+ touchDown(UNDEF, e)
1290
+ }
1291
+ function touchWheel(e) {
1292
+ // IE10 enabled pinch-to-zoom gestures from multi-touch trackpad’s as mousewheel event with ctrlKey.
1293
+ // Chrome M35 and Firefox 55 followed up.
1294
+ 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)) {
1297
+ return eventStop(e)
1298
+ }
1311
1299
  }
1312
1300
  }
1313
- }
1314
-
1315
- function moveTwo(e) {
1316
- touches[ touches[0].pointerId == e.pointerId ? 0 : 1] = e
1317
- var diff
1318
- , x = touchEv.X - touches[1].clientX
1319
- , y = touchEv.Y - touches[1].clientY
1320
- , dist = Math.sqrt(x*x + y*y) | 0
1321
- , angle = Math.atan2(y, x)
1322
-
1323
- if (touchDist !== null) {
1324
- diff = dist - touchDist
1325
- if (diff) elEmit(touchEl, "pinch", e, diff, angle)
1326
- // GestureEvent onGestureChange: function(e) {
1327
- // e.target.style.transform =
1328
- // 'scale(' + e.scale + startScale + ') rotate(' + e.rotation + startRotation + 'deg)'
1329
- diff = angle - touchAngle
1330
- if (diff) elEmit(touchEl, "rotate", e, diff * (180/Math.PI))
1301
+ function moveOne(e, fromTimer) {
1302
+ // In IE9 mousedown.buttons is OK but mousemove.buttons == 0
1303
+ if (touches[0].buttons && touches[0].buttons !== (e.buttons || [0, 1, 4, 2][e.which || 0])) {
1304
+ return touchUp(e)
1305
+ }
1306
+ touchEv.x = e.clientX - touchEv.X
1307
+ touchEv.y = e.clientY - touchEv.Y
1308
+ touchEv.leftPos = touchEv.x + touchEv.left
1309
+ touchEv.topPos = touchEv.y + touchEv.top
1310
+ if (!touchMode) {
1311
+ var evs = touchEl._e
1312
+ touchMode = (
1313
+ haveEv("pan", touchEv.x > 10 || touchEv.x < -10 || touchEv.y > 10 || touchEv.y < -10) ||
1314
+ haveEv("hold", fromTimer)
1315
+ )
1316
+ if (!touchMode) return
1317
+ clearTimeout(touchTick)
1318
+ elEmit(touchEl, touchMode + START, e, touchEv, touchEl)
1319
+ }
1320
+ elEmit(touchEl, touchMode, e, touchEv, touchEl)
1321
+ function haveEv(name, set) {
1322
+ return set && (evs[name] || evs[name + START] || evs[name + END]) && name
1323
+ }
1331
1324
  }
1332
-
1333
- touchDist = dist
1334
- touchAngle = angle
1335
- }
1336
-
1337
- function touchPos(name, offset) {
1338
- var val = (
1339
- touchEl.getBBox ?
1340
- touchEl.getAttributeNS(null, name == "top" ? "y":"x") :
1341
- touchEl.style[name]
1342
- )
1343
- touchEv[name] = parseInt(val, 10) || 0
1344
- if (val && val.indexOf("%") > -1) {
1345
- touchEv[name] *= touchEl.parentNode[offset] / 100
1325
+ function moveTwo(e) {
1326
+ touches[ touches[0].pointerId == e.pointerId ? 0 : 1] = e
1327
+ var diff
1328
+ , x = touchEv.X - touches[1].clientX
1329
+ , y = touchEv.Y - touches[1].clientY
1330
+ , dist = Math.sqrt(x*x + y*y) | 0
1331
+ , angle = Math.atan2(y, x)
1332
+
1333
+ if (touchDist !== UNDEF) {
1334
+ diff = dist - touchDist
1335
+ if (diff) elEmit(touchEl, "pinch", e, diff, angle)
1336
+ // GestureEvent onGestureChange: function(e) {
1337
+ // e.target.style.transform =
1338
+ // 'scale(' + e.scale + startScale + ') rotate(' + e.rotation + startRotation + 'deg)'
1339
+ diff = angle - touchAngle
1340
+ if (diff) elEmit(touchEl, "rotate", e, diff * (180/Math.PI))
1341
+ }
1342
+ touchDist = dist
1343
+ touchAngle = angle
1346
1344
  }
1347
1345
  }
1348
1346
  /**/
1349
1347
 
1350
1348
  function find(root, sel, startNode) {
1351
- return body.querySelector.call(startNode || root, sel)
1349
+ return html.querySelector.call(startNode || root, sel)
1352
1350
  }
1353
1351
  function findAll(root, sel, startNode) {
1354
- return ElWrap(body.querySelectorAll.call(startNode || root, sel))
1352
+ return ElWrap(html.querySelectorAll.call(startNode || root, sel))
1355
1353
  }
1356
1354
  function matches(el, sel) {
1357
- return el && body.matches.call(el, sel)
1355
+ return el && html.matches.call(el, sel)
1358
1356
  }
1359
1357
  function closest(el, sel) {
1360
- return el && body.closest.call(el.closest ? el : el.parentNode, 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
1361
1363
  }
1362
1364
  function acceptMany(fn, prepareVal) {
1363
- return function f(el, names, selector, data, val, delay) {
1364
- if (el && names) {
1365
+ return function f(el, name, val, selector, delay, data) {
1366
+ if (el && name) {
1365
1367
  var i = arguments.length
1366
- if (i == 3 || i == 4 && isNum(data)) {
1367
- delay = data
1368
- val = selector
1369
- selector = data = null
1370
- } else if (i == 4 || i == 5 && isNum(val)) {
1371
- delay = val
1372
- val = data
1373
- if (isStr(selector)) {
1374
- data = null
1375
- } else {
1376
- data = selector
1377
- selector = null
1378
- }
1368
+ if (i > 3 && i < 6 && isNum(selector)) {
1369
+ data = delay
1370
+ delay = selector
1371
+ selector = UNDEF
1379
1372
  }
1380
- if (delay >= 0) {
1381
- setTimeout(f, delay, el, names, selector, data, val)
1373
+ if (delay > 0) {
1374
+ setTimeout(f, delay, el, name, val, selector, 0, data)
1382
1375
  return
1383
1376
  }
1384
- if (isObj(names)) {
1385
- for (delay in names) {
1386
- if (hasOwn.call(names, delay)) f(el, delay, selector, data, names[delay])
1377
+ if (isObj(name)) {
1378
+ for (delay in name) if (hasOwn.call(name, delay)) {
1379
+ f(el, delay, name[delay], selector, 0, data)
1387
1380
  }
1388
1381
  return
1389
1382
  }
1390
- if (prepareVal) val = prepareVal(el, selector, data, val)
1391
- selector = !prepareVal && selector ? findAll(el, selector) : [ el ]
1392
- var arr = ("" + names).split(splitRe), len = arr.length
1393
- for (delay = 0; (el = selector[delay++]); )
1394
- for (i = 0; i < len; ) if (arr[i]) fn(el, arr[i++], isArr(val) ? val[i - 1] : val)
1383
+ if (prepareVal) val = prepareVal(el, val, selector, data)
1384
+ selector = !prepareVal && selector ? findAll(el, selector) : isArr(el) ? el : [ el ]
1385
+ var arr = ("" + name).split(splitRe), len = arr.length
1386
+ for (delay = 0; (el = selector[delay++]); ) {
1387
+ for (i = 0; i < len; ) {
1388
+ if (arr[i]) fn(el, arr[i++], isArr(val) ? val[i - 1] : val)
1389
+ }
1390
+ }
1395
1391
  }
1396
1392
  }
1397
1393
  }
1394
+ 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]
1397
+ }
1398
+ return target
1399
+ }
1398
1400
  function blur() {
1399
1401
  // IE8 can throw on accessing document.activeElement.
1400
1402
  try {
@@ -1407,9 +1409,11 @@
1407
1409
  return a.toUpperCase()
1408
1410
  }
1409
1411
  function each(arr, fn, scope, key) {
1410
- if (isStr(arr)) arr = arr.split(splitRe)
1411
- if (isArr(arr)) arr.forEach(fn, scope)
1412
- else if (arr) for (key in arr) if (hasOwn.call(arr, key)) fn.call(scope, arr[key], key, arr)
1412
+ if (arr) {
1413
+ if (isStr(arr)) arr = arr.split(splitRe)
1414
+ 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)
1416
+ }
1413
1417
  }
1414
1418
  function expand(str) {
1415
1419
  var first = str.charAt(0)
@@ -1420,6 +1424,28 @@
1420
1424
  (lastExp = str)
1421
1425
  )
1422
1426
  }
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
+ }
1423
1449
  function injectCss(cssText) {
1424
1450
  if (!styleNode) {
1425
1451
  // Safari and IE6-8 requires dynamically created
@@ -1442,19 +1468,23 @@
1442
1468
  function isStr(str) {
1443
1469
  return typeof str === "string"
1444
1470
  }
1445
- // Maximum call rate for Function
1446
- // leading edge, trailing edge
1447
- function rate(fn, ms) {
1471
+ // Maximum call rate for Function with optional leading edge and trailing edge
1472
+ function rate(fn, ms, onStart, onEnd) {
1448
1473
  var tick
1449
1474
  , next = 0
1475
+ onStart = isFn(onStart) ? onStart : (onStart === tick || onStart) && fn
1476
+ onEnd = isFn(onEnd) ? onEnd : (onEnd === tick || onEnd) && fn
1450
1477
  return function() {
1451
1478
  var now = Date.now()
1452
1479
  clearTimeout(tick)
1453
1480
  if (now >= next) {
1481
+ if (next < 1) {
1482
+ if (onStart) onStart()
1483
+ } else fn()
1454
1484
  next = now + ms
1455
- fn()
1456
- } else {
1457
- tick = setTimeout(fn, next - now)
1485
+ }
1486
+ if (onEnd) {
1487
+ tick = setTimeout(onEnd, next - now)
1458
1488
  }
1459
1489
  }
1460
1490
  }
@@ -1472,20 +1502,20 @@
1472
1502
  }
1473
1503
 
1474
1504
  function readTemplates(next) {
1475
- xhr.load(findAll(body, "script[type=ui]").map(function(el) {
1505
+ xhr.load(findAll(html, "script[type=ui]").map(function(el) {
1476
1506
  // IE6 script.innerText is empty
1477
- sources.push(el[txtAttr] || el.innerHTML)
1507
+ sources.push(el.innerHTML)
1478
1508
  elKill(el)
1479
1509
  return el.src
1480
1510
  }), function(res) {
1481
- res = res.concat(sources).filter(Boolean)
1511
+ res = res.concat(sources, (next || res).innerHTML).filter(Boolean)
1482
1512
  if (res[sources.length = 0]) {
1483
1513
  if (!parser) LiteJS.ui = LiteJS()
1484
1514
  each(res, parser)
1485
1515
  }
1486
- if (next) next()
1516
+ if (isFn(next)) next()
1487
1517
  }, 1)
1488
1518
  }
1489
- readTemplates()
1490
- }(this, document, history, location, Function, Object) // jshint ignore:line
1519
+ readTemplates(findAll(html, "script").pop())
1520
+ }(this, document, history, localStorage, location, navigator, Function, Object) // jshint ignore:line
1491
1521