@litejs/ui 26.2.1 → 26.3.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/css/base.css CHANGED
@@ -91,4 +91,8 @@ button,
91
91
  user-select: none;
92
92
  }
93
93
 
94
+ .no-scroll {
95
+ overflow: hidden;
96
+ }
97
+
94
98
 
package/el/dialog.ui CHANGED
@@ -1,15 +1,15 @@
1
1
 
2
2
  %css
3
3
  .Modal {
4
+ overflow: auto;
5
+ overscroll-behavior: contain;
4
6
  z-index: 9;
5
7
  }
6
8
  .Modal-bg {
7
- position: absolute;
8
- position: static;
9
9
  background-color: rgba(0, 0, 0, .6);
10
+ pointer-events: none;
10
11
  }
11
12
  .Modal-content {
12
- position: absolute;
13
13
  left: 0;
14
14
  right: 0;
15
15
  margin: 0 auto;
@@ -59,110 +59,100 @@
59
59
  // { "action": "no", "title": "No", "icon": "images/no.png" }
60
60
  // ]
61
61
  %js
62
- $ui.on("modal", function(title, opts, next) {
63
- El.blur()
64
- if (!next && typeof opts === "function") {
65
- next = opts
66
- opts = null
62
+ $ui.on("modal", function(title, opts) {
63
+ var oldFocus = document.activeElement
64
+ , modal = El("Modal")
65
+ , el = El("Dialog.Modal-content.Modal--blur.abs")
66
+ , scope = El.scope(el, el, typeof opts === "string" ? { body: opts } : opts)
67
+ , kbMap = {
68
+ bubble: 1, input: 1,
69
+ tab: function(e) {
70
+ var items = $$("a[href],:is(button,input,select,textarea):not(:disabled),[tabindex]:not([tabindex='-1'])", modal)
71
+ , last = items.length - 1
72
+ if (document.activeElement === items[e.shiftKey ? 0 : last]) {
73
+ items[e.shiftKey ? last : 0].focus()
74
+ El.stop(e)
75
+ }
76
+ }
67
77
  }
68
- var sound, vibrate
69
- , code = ""
70
- , el = El("Dialog")
71
- , scope = Object.assign(El.scope(el, el), opts)
72
- , kbMap = {}
73
78
  , body = document.body
74
- , blurEl = $(scope.blur) || $ui.root.lastChild
75
- scope.title = title || "Confirm?"
79
+ , scrollEl = document.scrollingElement || document.documentElement
80
+ , vibrate = scope.vibrate && navigator.vibrate
81
+ , sound = scope.sound && window.Audio && new Audio(scope.sound)
82
+ scope.title = title
76
83
  if (!scope.actions) scope.actions = [
77
- { action: "close", title: "Close", key: "esc" }
84
+ { title: "Close", key: "esc" }
78
85
  ]
79
- for (var a, i = 0; a = scope.actions[i++]; ) {
80
- if (typeof a == "string") a = scope.actions[i-1] = {title:a,action:a}
81
- if (a.key) kbMap[a.key] = resolve.bind(el, el, a.action)
82
- }
83
- El.cls(blurEl, "Modal--blur")
84
- var mm = El("Modal")
85
- El.append(mm, el)
86
- El.append(body, mm)
87
- El.render(mm)
88
- if (scope.code) {
89
- $$(".js-numpad", el).on("click", numpad)
90
- kbMap.backspace = kbMap.del = kbMap.num = numpad
91
- }
92
- El.addKb(kbMap, el)
93
- $$(".js-btn", el).on("click", resolve)
94
- $ui.one("show", resolve)
95
- if (scope.bgClose) El.on(el, "click", resolve)
96
- El.on(el, "wheel", El.stop)
97
- El.on(el.lastChild, "click", El.stop)
98
- if (scope.vibrate && navigator.vibrate) {
99
- vibrate = navigator.vibrate(scope.vibrate)
100
- }
101
- if (scope.sound && window.Audio) {
102
- sound = new Audio(scope.sound)
103
- sound.play()
86
+ for (var a, i = 0; (a = scope.actions[i++]); ) {
87
+ if (typeof a === "string") a = scope.actions[i - 1] = { title: a, action: a }
88
+ if (a.key) kbMap[a.key] = resolve.bind(el, a.action)
104
89
  }
105
- function numpad(e, _num) {
106
- // Enter pressed on focused element
107
- if (_num == void 0 && e.clientX == 0) return
108
- var num = _num == void 0 ? e.target[El.T] : _num
109
- code += num
110
- if (num == "CLEAR" || num == "del" || num == "backspace") code = ""
111
- El.md($(".js-body", el), code.replace(/./g, "•") || opts.body)
112
- if (typeof scope.code == "number" && code.length == scope.code && id && !sent) next(sent = code, id, resolve, reject)
90
+ El.append(modal, el)
91
+ if (scope.el) El.append(el, El(scope.el))
92
+ El.append(body, modal)
93
+ El.render(modal)
94
+ El.addKb(kbMap, modal)
95
+ if (El.closest(document.activeElement, 'form') !== el) {
96
+ el.focus()
113
97
  }
114
- function resolve(e, key) {
115
- if (el) {
116
- var action = key || El.get(this, "data-action")
117
- , result = {
118
- code: code,
119
- input: El.val($(".js-input", el)),
120
- inputMd: El.val($(".js-inputMd", el)),
121
- select: El.val($(".js-select", el))
122
- }
123
- El.kill(mm, "transparent")
124
- El.cls(blurEl, "Modal--blur", el = false)
125
- if (action && next) {
126
- if (typeof next === "function") next(action, result)
127
- else if (typeof next[action] === "function") next[action](result)
128
- else if (next[action]) $ui.emit(next[action], result)
129
- }
130
- if (vibrate) navigator.vibrate(0)
98
+ El.on(el, "click", function(e) {
99
+ console.log("click1 resolve")
100
+ resolve(El.get(e.target, "data-action"), e)
101
+ }, "[data-action]")
102
+ El.cls(el, "Modal--blur",!1,1)
103
+ $ui.emit("modalOpen", scope)
104
+ $ui.one("show", function() { resolve() })
105
+ El.on(modal.firstChild, "click", function() {
106
+ console.log("click resolve")
107
+ resolve()
108
+ })
109
+ if (vibrate) vibrate(scope.vibrate)
110
+ if (sound) sound.play()
111
+ El.cls(scrollEl, "no-scroll")
112
+ function resolve(action, e) {
113
+ if (modal) {
114
+ El.stop(e)
115
+ El.cls(scrollEl, "no-scroll", 0)
116
+ El.kill(modal, "transparent", 1)
117
+ if (vibrate) vibrate(0)
131
118
  if (sound) sound.pause()
119
+ modal = null
120
+ if (action) {
121
+ $ui.emit(scope.event || "modal:" + action, El.val(el), scope)
122
+ }
123
+ $ui.emit("modalClose", scope)
124
+ if (oldFocus) oldFocus.focus()
132
125
  }
133
126
  }
134
- scope.resolve = resolve
135
- $ui.emit("modal:open", scope)
136
127
  })
137
128
 
138
- %el Dialog-numpad
139
- .row.js-numpad
140
- @click ".btn", function(){}
141
- ;each! "num",[1,2,3,4,5,6,7,8,9,"CLEAR",0]
142
- .col.w4>.btn {num}
143
-
144
- %js
145
- $ui.on("showDialog", function(e, el, dialog) {
146
- console.log("showDialog", El.scope(el), arguments)
147
- El.kill(dialog)
148
- })
129
+ %el Modal
130
+ .Modal.fix.max.anim>.Modal-bg.abs.max
149
131
 
150
132
  %el Dialog
151
- .Dialog.grid.p2.anim
152
- .col.ts3 ;txt! _(title, map)
153
- .col.js-body ;md! _(body, map)
154
- .col
155
- .group ;each! 'action',actions
156
- .btn.js-btn
157
- ;txt! _(action.title)
158
- ;class! "w" + (12/actions.length)
159
- ;nop! this.focus()
160
- ;set! "data-action", action.action
161
- ;class! "is-" + action.action, action.action
133
+ form.Dialog.grid.p2.anim[action="javascript:"][method=POST][tabindex='-1']
134
+ .col>h1 ;txt title
135
+ .col ;if body; d body
136
+ %slot
137
+ .col.group
138
+ button.btn.js-action
139
+ ;each! 'action',actions
140
+ ;set! "data-action", action.action||"-"
141
+ ;txt _(action.title)
142
+ ;cls! "w" + (12/actions.length)
143
+ ;cls! action.action && "is-" + action.action, action.action
144
+
145
+ %el Dialog-input
146
+ .col
147
+ input.field[name=value][type=text][placeholder="Enter value"] ;focus
148
+
149
+ %el Dialog-numpad
150
+ .grid.p1.js-numpad
151
+ @click function(e,b,n,v){$s.in.value=(v=$s.in.value,n=b[El.T])>-1?v+n:n=='DEL'?v.slice(0,-1):'',$s.in.focus()},'.btn'
152
+ ;kb!{num:function(e,b,n){$s.in.value=(n)>-1?$s.in.value+n:''},del:function(e,b,n){$s.in.value=$s.in.value.slice(-1)}}
153
+ .col>input[name=code] ;ref!'in';focus
154
+ .col.w4
155
+ ;each! "num",[1,2,3,4,5,6,7,8,9,"CLEAR",0,"DEL"]
156
+ button.btn.w12[type=button]
157
+ ;txt!num
162
158
 
163
- %el Modal
164
- .Modal.max.fix.anim
165
- .Modal-bg.max.abs
166
- .Modal-content.Modal--blur.grid.p2.anim
167
- ;cls! "Modal--blur",!1,1
168
- %slot
package/el/material.ui CHANGED
@@ -1,111 +1,5 @@
1
1
 
2
2
  %css
3
- .mat-Menu,
4
- .tooltip {
5
- border-radius: 2px;
6
- position: absolute;
7
- margin: 0;
8
- opacity: 0;
9
- transform: scale(0);
10
- transition: opacity .4s cubic-bezier(0, 0, .2, 1) 0s, transform .2s cubic-bezier(0, 0, .2, 1) 0s;
11
- }
12
- .tooltip {
13
- padding: 8px;
14
- color: #fff;
15
- background: #666;
16
- font-weight: 500;
17
- max-width: 90%;
18
- text-align: center;
19
- pointer-events: none;
20
- z-index: 8;
21
- }
22
- .tooltip[data-pos]:before {
23
- content: "";
24
- position: absolute;
25
- display: block;
26
- width: .4em;
27
- height: .4em;
28
- border: .4em solid transparent;
29
- border-left-color: #666;
30
- border-top-color: #666;
31
- }
32
- .tooltip[data-pos=top]:before {
33
- bottom: -.3em;
34
- left: -.3em;
35
- margin-left: 50%;
36
- transform: rotate(225deg);
37
- }
38
- .tooltip[data-pos=bottom]:before {
39
- top: -.3em;
40
- left: -.3em;
41
- margin-left: 50%;
42
- transform: rotate(45deg);
43
- }
44
- .tooltip[data-pos=right]:before {
45
- top: 50%;
46
- left: -.3em;
47
- margin-top: -.4em;
48
- transform: rotate(315deg);
49
- }
50
- .tooltip[data-pos=left]:before {
51
- top: -.3em;
52
- right: -.3em;
53
- margin-top: 50%;
54
- transform: rotate(135deg);
55
- }
56
- .mat-Menu {
57
- padding: 8px 0;
58
- color: #000;
59
- background: #fff;
60
- min-width: 124px;
61
- max-width: 100%;
62
- z-index: 7;
63
- }
64
- .mat-Menu-item {
65
- display: block;
66
- padding: 12px 16px;
67
- text-decoration: none;
68
- line-height: 24px;
69
- cursor: pointer;
70
- overflow: hidden;
71
- text-overflow: ellipsis;
72
- white-space: nowrap;
73
- }
74
- .mat-Menu-item:hover {
75
- background-color: #eee;
76
- }
77
- .mat-Menu-item[disabled] {
78
- color: #bdbdbd;
79
- background-color: transparent;
80
- cursor: auto;
81
- }
82
- .mat-Menu-item.is-divider {
83
- border-bottom: 1px solid #ddd;
84
- }
85
- .mat-Menu.is-visible,
86
- .tooltip.is-visible {
87
- transform: scale(1);
88
- opacity: 1;
89
- }
90
- .waves {
91
- position: relative;
92
- overflow: hidden;
93
- }
94
- .waves-ripple {
95
- position: absolute;
96
- border-radius: 50%;
97
- background-color: #000;
98
- opacity: 0;
99
- transform: scale(0);
100
- transition: opacity .6s cubic-bezier(0, 0, .2, 1) 0s, transform 0s cubic-bezier(0, 0, .2, 1) .6s;
101
- pointer-events: none;
102
- }
103
- .waves.is-active > .waves-ripple,
104
- .waves-ripple--play {
105
- opacity: .4;
106
- transform: scale(1);
107
- transition: opacity 1s cubic-bezier(0, 0, .2, 1) 0ms, transform .4s cubic-bezier(0, 0, .2, 1) 0ms;
108
- }
109
3
  .shadow-1 {
110
4
  box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
111
5
  }
@@ -193,172 +87,6 @@
193
87
  left: 25%;
194
88
  }
195
89
 
196
- %js
197
- !function($ui) {
198
- var menuTarget, menuEl, tipTarget, tipEl, tick, wait
199
- , ripple = El(".waves-ripple")
200
- , html = document.documentElement
201
- El.near = near
202
- function near(source, target, x, y, margin) {
203
- var rect = target.getBoundingClientRect()
204
- , top = rect.top + El.scrollTop()
205
- , left = rect.left + El.scrollLeft()
206
- // svg elements dont have offsetWidth, IE8 does not have rect.width
207
- , width = rect.width || target.offsetWidth || 0
208
- , height = rect.height || target.offsetHeight || 0
209
- if (x == "left") {
210
- left -= source.offsetWidth + margin
211
- x = "150%"
212
- } else if (x == "left-start") {
213
- left -= margin
214
- x = "0%"
215
- } else if (x == "right") {
216
- left += width + margin
217
- x = "-50%"
218
- } else if (x == "right-end") {
219
- left += width + margin - source.offsetWidth
220
- x = "100%"
221
- } else {
222
- left += (width / 2) - (source.offsetWidth/2)
223
- x = "50%"
224
- }
225
- if (y == "top") {
226
- top -= margin + source.offsetHeight
227
- y = " 150%"
228
- } else if (y == "bottom") {
229
- top += height + margin
230
- y = " -50%"
231
- setTimeout(function(){
232
- //document.scrollingElement.scrollHeight
233
- var overflow = top + source.offsetHeight - El.scrollTop() - document.documentElement.offsetHeight
234
- if (overflow > 0) scrollBy({ top: overflow, left: 0, behavior: "smooth" })
235
- }, 400)
236
- } else {
237
- top += (height / 2) - (source.offsetHeight/2)
238
- y = " 50%"
239
- }
240
- El.css(source, {
241
- "transform-origin": x + y,
242
- top: (top < 0 ? 0 : top) + "px",
243
- left: (left < 0 ? 0 : left) + "px"
244
- })
245
- }
246
- El.on(html, "mouseover", onOver)
247
- El.on(window, "focusin", onOver)
248
- $ui.on("show", closeTooltip)
249
- function onOver(e) {
250
- var x, y, pos
251
- , target = e.target
252
- , text = El.get(target, "data-tooltip")
253
- , relTarg = e.relatedTarget || e.fromElement
254
- // without relTarg is event on click
255
- if (!relTarg && e.type !== "focusin" || target === tipTarget) return
256
- if (!text && tipTarget) {
257
- for (; target = target.parentNode; ) {
258
- if (target === tipTarget) return
259
- }
260
- }
261
- closeTooltip()
262
- if (!text) return
263
- tipEl = openVisible("pre.tooltip", tipTarget = target)
264
- pos = El.get(target, "data-tooltip-pos") || "top"
265
- El.txt(tipEl, text)
266
- if (pos === "left" || pos === "right") {
267
- x = pos
268
- } else {
269
- y = pos
270
- }
271
- El.set(tipEl, "data-pos", pos)
272
- near(tipEl, target, x, y, 6)
273
- }
274
- function openVisible(tag, target) {
275
- var el = typeof tag == "string" ? El(tag) : tag
276
- , scope = El.scope(el, target)
277
- scope.openTarget = target
278
- El.render(el)
279
- El.append(document.body, el)
280
- El.cls(el, "is-visible", 1, 5)
281
- return el
282
- }
283
- function closeVisible(el, delay) {
284
- if (el) {
285
- setTimeout(el.closeFn || El.kill.bind(El, el), 999)
286
- El.cls(el, "is-visible", 0, delay)
287
- }
288
- }
289
- function closeTooltip() {
290
- if (tipEl) {
291
- closeVisible(tipEl)
292
- tipTarget = tipEl = null
293
- }
294
- }
295
- function closeMenu(e) {
296
- if (e && e.target == menuTarget) return
297
- if (menuEl) {
298
- closeVisible(menuEl, 200)
299
- El.cls(menuTarget, "is-active", menuEl = menuTarget = null)
300
- }
301
- }
302
- $ui.on("ping", closeMenu)
303
- $ui.on("closeMenu", closeMenu)
304
- $ui.on("showMenu", function(e, target, menu, x, y, margin) {
305
- El.stop(e)
306
- var close = menuEl && menuTarget == target
307
- closeMenu()
308
- if (close) return
309
- menuEl = openVisible(menu, target)
310
- if (x == "mouse") {
311
- El.css(menuEl, {
312
- top: e.pageY + "px",
313
- left: e.pageX + "px"
314
- })
315
- } else {
316
- El.cls(menuTarget = target, "is-active")
317
- near(menuEl, target, x, y, margin)
318
- }
319
- if (menuEl.style.transform !== void 0) {
320
- El.cls(menuEl, "no-events")
321
- El.on(menuEl, "transitionend", function(e) {
322
- if (e.propertyName === "transform") El.cls(menuEl, "no-events", 0)
323
- })
324
- }
325
- })
326
- El.on(html, "click", closeMenu)
327
- El.on(html, "pointerdown", pointerdown)
328
- function pointerdown(e) {
329
- var target = e.target
330
- if (!El.hasClass(target, "waves") || target.disabled) return
331
- var rect = target.getBoundingClientRect()
332
- , fromMouse = !El.hasClass(target, "Checkbox-icon")
333
- , top = fromMouse ? e.clientY - rect.top : rect.height
334
- , left = fromMouse ? e.clientX - rect.left : rect.width
335
- , maxH = Math.max(top, target.offsetHeight - top)
336
- , maxW = Math.max(left, target.offsetWidth - left)
337
- , max = Math.sqrt(maxH * maxH + maxW * maxW)
338
- , size = (fromMouse ? 2 * max : max) + "px"
339
- El.css(ripple, {
340
- top: (top - max) + "px",
341
- left: (left - max) + "px",
342
- width: size,
343
- height: size
344
- })
345
- El.append(target, ripple)
346
- clearTimeout(tick)
347
- end()
348
- wait = 1
349
- tick = setTimeout(end, 800)
350
- El.one(html, "pointerup", end)
351
- ripple.offsetTop // force repaint
352
- El.cls(ripple, "waves-ripple--play")
353
- }
354
- function end() {
355
- if (!(wait--)) {
356
- El.cls(ripple, "waves-ripple--play", 0)
357
- }
358
- }
359
- }($ui)
360
-
361
-
362
90
  %el Checkbox
363
91
  label.Checkbox
364
92
  input[type=checkbox].hide
@@ -424,5 +152,3 @@
424
152
  %el MenuBtn
425
153
  button[type=button].MenuBtn.reset.noselect
426
154
  .MenuBtn-x.anim
427
-
428
-
package/el/menu.ui ADDED
@@ -0,0 +1,213 @@
1
+
2
+ %css
3
+ .Menu,
4
+ .Tooltip {
5
+ border-radius: 2px;
6
+ position: absolute;
7
+ margin: 0;
8
+ opacity: 0;
9
+ transform: scale(0);
10
+ transition: opacity .4s cubic-bezier(0, 0, .2, 1) 0s, transform .2s cubic-bezier(0, 0, .2, 1) 0s;
11
+ }
12
+ .Tooltip {
13
+ padding: 8px;
14
+ color: #fff;
15
+ background: #666;
16
+ font-weight: 500;
17
+ max-width: 90%;
18
+ text-align: center;
19
+ z-index: 8;
20
+ }
21
+ .Tooltip[data-pos]:before {
22
+ content: "";
23
+ position: absolute;
24
+ display: block;
25
+ width: .4em;
26
+ height: .4em;
27
+ border: .4em solid transparent;
28
+ border-left-color: #666;
29
+ border-top-color: #666;
30
+ }
31
+ .Tooltip[data-pos=top]:before {
32
+ bottom: -.3em;
33
+ left: -.3em;
34
+ margin-left: 50%;
35
+ transform: rotate(225deg);
36
+ }
37
+ .Tooltip[data-pos=bottom]:before {
38
+ top: -.3em;
39
+ left: -.3em;
40
+ margin-left: 50%;
41
+ transform: rotate(45deg);
42
+ }
43
+ .Tooltip[data-pos=right]:before {
44
+ top: 50%;
45
+ left: -.3em;
46
+ margin-top: -.4em;
47
+ transform: rotate(315deg);
48
+ }
49
+ .Tooltip[data-pos=left]:before {
50
+ top: -.3em;
51
+ right: -.3em;
52
+ margin-top: 50%;
53
+ transform: rotate(135deg);
54
+ }
55
+ .Menu {
56
+ padding: 8px 0;
57
+ color: #000;
58
+ background: #fff;
59
+ min-width: 124px;
60
+ max-width: 100%;
61
+ z-index: 7;
62
+ }
63
+ .Menu-item {
64
+ display: block;
65
+ padding: 12px 16px;
66
+ text-decoration: none;
67
+ line-height: 24px;
68
+ cursor: pointer;
69
+ overflow: hidden;
70
+ text-overflow: ellipsis;
71
+ white-space: nowrap;
72
+ }
73
+ .Menu-item:hover {
74
+ background-color: #eee;
75
+ }
76
+ .Menu-item[disabled] {
77
+ color: #bdbdbd;
78
+ background-color: transparent;
79
+ cursor: auto;
80
+ }
81
+ .Menu-item.is-divider {
82
+ border-bottom: 1px solid #ddd;
83
+ }
84
+ .Menu.is-visible,
85
+ .Tooltip.is-visible {
86
+ transform: scale(1);
87
+ opacity: 1;
88
+ }
89
+
90
+ %js
91
+ !function($ui) {
92
+ var menuTarget, menuEl, tipTarget, tipEl
93
+ , html = document.documentElement
94
+ El.near = near
95
+ function near(source, target, x, y, margin) {
96
+ var rect = target.getBoundingClientRect()
97
+ , top = rect.top + El.scrollTop()
98
+ , left = rect.left + El.scrollLeft()
99
+ // svg elements dont have offsetWidth, IE8 does not have rect.width
100
+ , width = rect.width || target.offsetWidth || 0
101
+ , height = rect.height || target.offsetHeight || 0
102
+ if (x == "left") {
103
+ left -= source.offsetWidth + margin
104
+ x = "150%"
105
+ } else if (x == "left-start") {
106
+ left -= margin
107
+ x = "0%"
108
+ } else if (x == "right") {
109
+ left += width + margin
110
+ x = "-50%"
111
+ } else if (x == "right-end") {
112
+ left += width + margin - source.offsetWidth
113
+ x = "100%"
114
+ } else {
115
+ left += (width / 2) - (source.offsetWidth/2)
116
+ x = "50%"
117
+ }
118
+ if (y == "top") {
119
+ top -= margin + source.offsetHeight
120
+ y = " 150%"
121
+ } else if (y == "bottom") {
122
+ top += height + margin
123
+ y = " -50%"
124
+ setTimeout(function() {
125
+ var overflow = top + source.offsetHeight - El.scrollTop() - html.offsetHeight
126
+ if (overflow > 0) scrollBy({ top: overflow, left: 0, behavior: "smooth" })
127
+ }, 400)
128
+ } else {
129
+ top += (height / 2) - (source.offsetHeight/2)
130
+ y = " 50%"
131
+ }
132
+ El.css(source, {
133
+ "transform-origin": x + y,
134
+ top: (top < 0 ? 0 : top) + "px",
135
+ left: (left < 0 ? 0 : left) + "px"
136
+ })
137
+ }
138
+ El.on(html, "mouseover focusin", function(e) {
139
+ var x, y, pos
140
+ , target = e.target
141
+ , text = El.get(target, "data-tooltip")
142
+ if (!e.relatedTarget && e.type !== "focusin" || target === tipTarget) return
143
+ if (!text && tipTarget) {
144
+ if (tipEl && tipEl.contains(target)) return
145
+ for (; target = target.parentNode; ) {
146
+ if (target === tipTarget) return
147
+ }
148
+ }
149
+ closeTooltip()
150
+ if (!text) return
151
+ tipEl = openVisible("pre.Tooltip", tipTarget = target)
152
+ pos = El.get(target, "data-tooltip-pos") || "top"
153
+ El.txt(tipEl, text)
154
+ if (pos === "left" || pos === "right") {
155
+ x = pos
156
+ } else {
157
+ y = pos
158
+ }
159
+ El.set(tipEl, "data-pos", pos)
160
+ near(tipEl, target, x, y, 6)
161
+ })
162
+ $ui.on("show", closeTooltip)
163
+ function openVisible(tag, target) {
164
+ var el = typeof tag == "string" ? El(tag) : tag
165
+ , scope = El.scope(el, target)
166
+ scope.openTarget = target
167
+ El.render(el)
168
+ El.append(document.body, el)
169
+ El.cls(el, "is-visible", 1, 5)
170
+ return el
171
+ }
172
+ function closeVisible(el, delay) {
173
+ if (el) {
174
+ El.kill(el, null, 999)
175
+ El.cls(el, "is-visible", 0, delay)
176
+ }
177
+ }
178
+ function closeTooltip() {
179
+ if (tipEl) {
180
+ closeVisible(tipEl)
181
+ tipTarget = tipEl = null
182
+ }
183
+ }
184
+ function menuClose(e) {
185
+ if (e && e.target == menuTarget) return
186
+ if (menuEl) {
187
+ closeVisible(menuEl, 200)
188
+ El.cls(menuTarget, "is-active", menuEl = menuTarget = null)
189
+ }
190
+ }
191
+ $ui.on("ping,menuClose", menuClose)
192
+ $ui.on("menu", function(e, target, menu, x, y, margin) {
193
+ El.stop(e)
194
+ var close = menuEl && menuTarget == target
195
+ menuClose()
196
+ if (close) return
197
+ menuEl = openVisible(menu, target)
198
+ if (x == "mouse") {
199
+ El.css(menuEl, {
200
+ top: e.pageY + "px",
201
+ left: e.pageX + "px"
202
+ })
203
+ } else {
204
+ El.cls(menuTarget = target, "is-active")
205
+ near(menuEl, target, x, y, margin)
206
+ }
207
+ El.cls(menuEl, "no-events")
208
+ El.on(menuEl, "transitionend", function(e) {
209
+ if (e.propertyName === "transform") El.cls(menuEl, "no-events", 0)
210
+ })
211
+ })
212
+ El.on(html, "click", menuClose)
213
+ }($ui)
package/el/ripple.ui ADDED
@@ -0,0 +1,56 @@
1
+
2
+ %css
3
+ .waves {
4
+ position: relative;
5
+ overflow: hidden;
6
+ }
7
+ .waves-ripple {
8
+ position: absolute;
9
+ border-radius: 50%;
10
+ background-color: #000;
11
+ opacity: 0;
12
+ transform: scale(0);
13
+ transition: opacity .6s cubic-bezier(0, 0, .2, 1) 0s, transform 0s cubic-bezier(0, 0, .2, 1) .6s;
14
+ pointer-events: none;
15
+ }
16
+ .waves-ripple--play {
17
+ opacity: .4;
18
+ transform: scale(1);
19
+ transition: opacity 1s cubic-bezier(0, 0, .2, 1) 0ms, transform .4s cubic-bezier(0, 0, .2, 1) 0ms;
20
+ }
21
+
22
+ %js
23
+ !function() {
24
+ var tick, wait
25
+ , ripple = El(".waves-ripple")
26
+ , html = document.documentElement
27
+ El.on(html, "pointerdown", function(e) {
28
+ var target = e.target
29
+ if (!El.hasClass(target, "waves") || target.disabled) return
30
+ var rect = target.getBoundingClientRect()
31
+ , top = e.clientY - rect.top
32
+ , left = e.clientX - rect.left
33
+ , maxH = Math.max(top, target.offsetHeight - top)
34
+ , maxW = Math.max(left, target.offsetWidth - left)
35
+ , max = 2 * Math.sqrt(maxH * maxH + maxW * maxW)
36
+ El.css(ripple, {
37
+ top: (top - max / 2) + "px",
38
+ left: (left - max / 2) + "px",
39
+ width: max + "px",
40
+ height: max + "px"
41
+ })
42
+ El.append(target, ripple)
43
+ clearTimeout(tick)
44
+ end()
45
+ wait = 1
46
+ tick = setTimeout(end, 600)
47
+ El.one(html, "pointerup", end)
48
+ ripple.offsetTop // force repaint
49
+ El.cls(ripple, "waves-ripple--play")
50
+ })
51
+ function end() {
52
+ if (!(wait--)) {
53
+ El.cls(ripple, "waves-ripple--play", 0)
54
+ }
55
+ }
56
+ }()
@@ -47,7 +47,7 @@
47
47
  :focus > * > .Slider-knob {
48
48
  box-shadow: 0 0 0 8px rgb(0, 0, 0, .2), 0 1px 4px rgba(0, 0, 0, .3);
49
49
  }
50
- .Slider-knob.is-active {
50
+ .is-active > .Slider-knob {
51
51
  box-shadow: 0 0 0 12px rgb(0, 0, 0, .2), 0 1px 5px 5px rgba(0, 0, 0, .3);
52
52
  }
53
53
  /* value bubble */
@@ -68,13 +68,14 @@
68
68
  }
69
69
  .Slider-knob:after {
70
70
  content: attr(data-val);
71
+ text-align: center;
71
72
  left: 10px;
72
73
  padding: 0 4px;
73
74
  transform: translate(-50%, 0);
74
75
  }
75
76
  :focus > * > .Slider-knob:before,
76
77
  :hover > * > .Slider-knob:before,
77
- .Slider-knob.is-active:before {
78
+ .is-active > .Slider-knob:before {
78
79
  border-radius: 50% 50% 0 50%;
79
80
  box-shadow: 3px 3px 3px 1px rgba(0, 0, 0, .3);
80
81
  opacity: 1;
@@ -82,7 +83,7 @@
82
83
  }
83
84
  :focus > * > .Slider-knob:after,
84
85
  :hover > * > .Slider-knob:after,
85
- .Slider-knob.is-active:after {
86
+ .is-active > .Slider-knob:after {
86
87
  box-shadow: -4px -3px 3px -2px rgba(0, 0, 0, .15), 4px -3px 3px -2px rgba(0, 0, 0, .15);
87
88
  opacity: 1;
88
89
  top: -43px;
@@ -109,113 +110,124 @@
109
110
  , elOff = El.off
110
111
  , elCls = El.cls
111
112
  , elGet = El.get
112
- El.$b.SliderInit = function(el, range) {
113
+ , sliderTypes = {
114
+ time: ["0:1440:60", function(s) {
115
+ s = s.split(":")
116
+ return s[0] * 60 + +s[1]
117
+ }, function(m) {
118
+ return (m / 60 | 0) + ":" + ("0" + m % 60).slice(-2)
119
+ }]
120
+ }
121
+ El.$b.sliderInit = function(el, range) {
113
122
  range = (range || elGet(el, "range") || "").split(/[^+\-\d.]/) // min:max:step:margin
114
- var knobLen, offset, px, drag, min, max, minPx, maxPx, value
115
- //, vert = El.hasClass(el, "is-vertical")
116
- , vert = false
117
- , attr = vert ? "offsetHeight" : "offsetWidth"
118
- , fill = el.firstChild
119
- , knob = fill.lastChild
123
+ var knobLen, offset, px, drag, min, max, minPx, maxPx
124
+ , step = +(range[2] || 1)
125
+ , margin = +(range[3] || 0)
126
+ , fmt = elGet(el, "format")
127
+ , idx
128
+ , fill
129
+ , values = []
130
+ , inputs = $$("input", el)
131
+ , type = sliderTypes[elGet(el, "type")]
132
+ , parse = type ? type[1] : Number
133
+ , format = type ? type[2] : String
134
+ , fills = []
120
135
  , emit = El.rate(El.emit.bind(el, el, "change"), 500)
136
+ if (type && !range[1]) range = type[0].split(/[^+\-\d.]/)
137
+ for (idx = 0; idx < inputs.length; ) {
138
+ fills[idx] = El("Slider-fill")
139
+ values[idx] = parse(inputs[idx].value)
140
+ idx++
141
+ }
142
+ if (fills.length) {
143
+ El.append(el, fills.slice(0).reverse())
144
+ load()
145
+ for (idx = 0; idx < fills.length; idx++) setVal(idx, values[idx])
146
+ idx = 0
147
+ fill = fills[0]
148
+ } else {
149
+ fill = el.firstChild
150
+ }
121
151
  elOn(window, "blur", stop)
122
- setTimeout(el.val = set, 10, value||0)
123
152
  function load() {
124
153
  min = +(range[0] || 0)
125
154
  max = +(range[1] || 100)
126
- knobLen = knob[attr]>>1
155
+ knobLen = (fills[0] || fill).firstChild.offsetWidth>>1
127
156
  minPx = 0
128
- maxPx = el[attr] - knobLen - knobLen
157
+ maxPx = el.offsetWidth - knobLen - knobLen
129
158
  px = maxPx / (max - min)
130
159
  }
131
160
  elOn(el, "pointerdown", function(e) {
132
161
  drag = true
133
162
  load()
134
163
  var tmp = el.getBoundingClientRect()
135
- offset = (vert ? tmp.top + maxPx + El.scrollTop() + knobLen : tmp.left + El.scrollLeft()) + knobLen
136
- tmp = offset - e.clientX + (value-min||0)*px
137
- if (tmp < knobLen && tmp > -knobLen) offset -= tmp
138
- if (el.childNodes.length > 1) {
139
- var next
140
- , x = maxPx
141
- , diff = vert ? offset - e.pageY : e.pageX - offset
142
- for (tmp = fill = el.firstChild; tmp; tmp = tmp.nextSibling) {
143
- next = diff - tmp[attr] + knobLen
144
- if (next < 0 ? -next <= x : next < x) {
145
- fill = tmp
146
- knob = tmp.firstChild
147
- x = next < 0 ? -next : next
148
- }
149
- }
150
- if (fill.previousSibling) {
151
- maxPx = fill.previousSibling[attr] - knobLen
152
- if (range[3]) maxPx -= px * range[3]
153
- }
154
- if (fill.nextSibling) {
155
- minPx = fill.nextSibling[attr] - knobLen
156
- if (range[3]) minPx += px * range[3]
164
+ offset = tmp.left + El.scrollLeft() + knobLen
165
+ var next, i
166
+ , x = maxPx
167
+ , diff = e.pageX - offset
168
+ for (i = 0; i < fills.length; i++) {
169
+ next = diff - fills[i].offsetWidth + knobLen
170
+ if (next < 0 ? -next <= x : next < x) {
171
+ idx = i
172
+ fill = fills[i]
173
+ x = next < 0 ? -next : next
157
174
  }
158
175
  }
159
- move(e)
176
+ tmp = offset - e.pageX + (values[idx]-min||0)*px
177
+ if (tmp < knobLen && tmp > -knobLen) offset -= tmp
160
178
  listen(elOn)
179
+ move(e)
161
180
  })
162
181
  function move(e) {
163
- var diff = vert ? offset - e.pageY : e.pageX - offset
182
+ var diff = e.pageX - offset
164
183
  diff = (diff > maxPx ? maxPx : (diff < minPx ? minPx : diff))
165
- set((diff / px) + min, e, diff)
184
+ setVal(idx, (diff / px) + min, diff)
185
+ emit(e)
166
186
  return El.stop(e)
167
187
  }
168
188
  function stop(e) {
169
189
  if (drag) {
170
190
  drag = false
171
191
  listen(elOff)
172
- set(value)
192
+ setVal(idx, values[idx])
173
193
  }
174
194
  }
175
195
  function listen(onOff) {
176
196
  elCls(el, "anim", !drag)
177
- elCls(knob, "is-active", drag)
197
+ elCls(fill, "is-active", drag)
178
198
  onOff(document, "pointerup", stop)
179
199
  onOff(document, "pointermove", move)
180
200
  }
181
- function set(val, e, pos) {
182
- load()
183
- val = El.step(val < min ? min : val > max ? max : val || 0, +(range[2] || 1))
184
- if (value !== void 0 && (!drag || pos !== void 0)) {
185
- El.css(fill, vert ? "height" : "width", ((pos || (val-min)*px)+knobLen) + "px", 0)
201
+ function setVal(i, val, pos) {
202
+ val = El.step(val < min ? min : val > max ? max : val || 0, step)
203
+ if (values[i] !== void 0 && (!drag || pos !== void 0 || i !== idx)) {
204
+ El.css(fills[i], "width", ((pos || (val-min)*px)+knobLen) + "px", 0)
186
205
  }
187
- if (value !== val) {
188
- el.value = value = val
189
- if (drag && e) emit(e)
190
- var format = elGet(el, "format")
191
- El.set(knob, "data-val", format ? $d._(format, {val:val}) : val)
206
+ if (values[i] !== val) {
207
+ values[i] = val
208
+ inputs[i].value = format(val)
209
+ El.set(fills[i].firstChild, "data-val", fmt ? $d._(fmt, {val:val}) : format(val))
210
+ if (drag && margin) {
211
+ if (i > 0 && val - values[i - 1] < margin) setVal(i - 1, +val - margin)
212
+ if (i < fills.length - 1 && values[i + 1] - val < margin) setVal(i + 1, +val + margin)
213
+ }
192
214
  }
193
215
  }
194
216
  }
217
+ El.$b.sliderInit.types = sliderTypes
195
218
 
196
- %el Slider
197
- button.Slider.anim.reset ;SliderInit!
198
- .Slider-fill
199
- .Slider-knob[tabindex=0]
219
+ %el Knob
220
+ input[type=hidden]
200
221
 
201
- %el Slider2
202
- button.Slider.anim.reset ;SliderInit!
203
- .Slider-fill
204
- .Slider-knob[tabindex=0]
205
- .Slider-fill
206
- .Slider-knob[tabindex=0]
222
+ %el Slider-fill
223
+ .Slider-fill
224
+ .Slider-knob[tabindex=0]
207
225
 
208
- %el Slider3
209
- button.Slider.reset ;SliderInit!
210
- .Slider-fill
211
- .Slider-knob[tabindex=0]
212
- .Slider-fill
213
- .Slider-knob[tabindex=0]
214
- .Slider-fill
215
- .Slider-knob[tabindex=0]
226
+ %el Slider
227
+ .Slider.hand.anim[data-out="sliderInit!"]
216
228
 
217
229
  %el Toggle
218
- button.Toggle.reset ;SliderInit! "0:1"
230
+ button.Toggle.reset ;sliderInit! "0:1"
219
231
  ;css: "width","36px"
220
232
  .Slider-fill.anim
221
233
  .Slider-knob.anim[tabindex=0]
package/load.js CHANGED
@@ -63,10 +63,10 @@
63
63
  if (lastError !== (lastError =
64
64
  [ file
65
65
  , line
66
- , col || (window.event || unsentLog).errorCharacter || "?"
66
+ , col || (window.event || unsentLog).errorCharacter || ""
67
67
  , message
68
68
  ].join(":")
69
- )) log("e", lastError, [error && (error.stack || error.stacktrace) || "-", "" + location])
69
+ )) log("e", lastError, [error && (error.stack || error.stacktrace) || "", "" + location])
70
70
  }
71
71
  , log = xhr.log = function(type, msg, extra) {
72
72
  if (unsentLog.push([new Date() - initTime, type].concat(msg, extra || [])) < 2) sendLog()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litejs/ui",
3
- "version": "26.2.1",
3
+ "version": "26.3.0",
4
4
  "description": "UI engine for LiteJS full-stack framework",
5
5
  "license": "MIT",
6
6
  "author": "Lauri Rooden <lauri@rooden.ee>",
package/ui.js CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /* litejs.com/MIT-LICENSE.txt */
3
3
 
4
- /* global escape, navigator, xhr */
4
+ /* global escape, getComputedStyle, navigator, pageXOffset, pageYOffset, scrollTo, xhr */
5
5
 
6
6
  // Conditional compilation via toggle comments (processed by build tool):
7
7
  // /*** name ***/ code /**/ - `code` active in source; build can strip it
@@ -318,10 +318,11 @@ console.log("LiteJS is in debug mode and that's fine for production")
318
318
  var fn2 = fixFn[ev] && fixFn[ev](el, fn, ev) || fn
319
319
  , ev2 = fixEv[ev] || ev
320
320
 
321
- if (ev2 !== "" && "on" + ev2 in el) {
321
+ if (ev2 !== "") {
322
322
  // polyfilled addEventListener returns patched function
323
323
  // useCapture defaults to false
324
324
  // Chrome56 touchstart/move sets {passive:true} by default; use {passive:false} to enable preventDefault()
325
+ // FF/Chrome el.onfocusin property is not supported, only addEventListener("focusin", fn)
325
326
  fn2 = html.addEventListener.call(el, ev2, fn2, opts) || fn2
326
327
  }
327
328
 
@@ -336,7 +337,7 @@ console.log("LiteJS is in debug mode and that's fine for production")
336
337
  if (fn !== evs[id + 1] && evs[id + 1]._rm) {
337
338
  evs[id + 1]._rm()
338
339
  }
339
- if (ev2 !== "" && "on" + ev2 in el) {
340
+ if (ev2 !== "") {
340
341
  html.removeEventListener.call(el, ev2, evs[id + 1], opts)
341
342
  }
342
343
  evs.splice(id - 1, 3)
@@ -507,6 +508,8 @@ console.log("LiteJS is in debug mode and that's fine for production")
507
508
  viewEmit(view, "pong", params, View)
508
509
  }
509
510
  viewEmit(lastView, "show", params)
511
+ close = history.state || 0
512
+ scrollTo(close.x|0, close.y|0)
510
513
  blur()
511
514
  }
512
515
  function viewClose(view, open) {
@@ -709,6 +712,9 @@ console.log("LiteJS is in debug mode and that's fine for production")
709
712
  addPlugin("el", {
710
713
  d: function(plugin, el) {
711
714
  el = usePluginContent(plugin)
715
+ /*** debug ***/
716
+ if (elCache[plugin.n]) console.error("El '%s' exist! Loading .ui file twice will execute %js also twice", plugin.n)
717
+ /**/
712
718
  elCache[plugin.n] = el
713
719
  }
714
720
  }, 1)
@@ -735,7 +741,7 @@ console.log("LiteJS is in debug mode and that's fine for production")
735
741
  // document.documentElement.clientWidth is 0 in IE5
736
742
  bindingsIs(html, (width = html.offsetWidth), breakpoints, "")
737
743
  bindingsIs(html, +(width > html.offsetHeight), "port,1=land", "")
738
- emit(View, "resize")
744
+ emit(LiteJS, "resize")
739
745
  }, 99)
740
746
 
741
747
  if (breakpoints) {
@@ -1006,7 +1012,7 @@ console.log("LiteJS is in debug mode and that's fine for production")
1006
1012
  function setUrl(url, rep) {
1007
1013
  /*** pushState ***/
1008
1014
  if (pushBase) {
1009
- history[rep ? "replaceState" : "pushState"](NUL, NUL, pushBase + url)
1015
+ history[rep ? "replaceState" : (history.replaceState({x: pageXOffset, y: pageYOffset}, NUL, location.href), "pushState")](NUL, NUL, pushBase + url)
1010
1016
  } else
1011
1017
  /**/
1012
1018
  location[rep ? "replace" : "assign"]("#" + url)
@@ -1169,7 +1175,7 @@ console.log("LiteJS is in debug mode and that's fine for production")
1169
1175
  },
1170
1176
  is: bindingsIs,
1171
1177
  name: function(el, name) {
1172
- setAttr(el, "name", expand(name, 1))
1178
+ if (name) setAttr(el, "name", expand(name, 1))
1173
1179
  },
1174
1180
  ref: function(el, name) {
1175
1181
  this[name] = el
@@ -1336,7 +1342,7 @@ console.log("LiteJS is in debug mode and that's fine for production")
1336
1342
  if (isObj(tr)) bindingsCss(el, tr)
1337
1343
  tr = "transitionend"
1338
1344
  // transitionend fires for each property transitioned
1339
- if ("on" + tr in el) return addEvent(el, tr, bind(elKill, el, el))
1345
+ if ("on" + tr in el) return addEvent(el, tr, bind(elKill, el, el, el = UNDEF))
1340
1346
  }
1341
1347
  if (el._e) {
1342
1348
  emit(el, "kill")
@@ -1355,9 +1361,9 @@ console.log("LiteJS is in debug mode and that's fine for production")
1355
1361
  }
1356
1362
  }
1357
1363
  }
1358
- function elScope(el, parent) {
1364
+ function elScope(el, parent, opts) {
1359
1365
  return el.$s || (
1360
- parent ? ((parent = elScope(parent)), el.$s = assign(create(parent), { $up: parent })) :
1366
+ parent ? (el.$s = assign(create(parent = elScope(parent)), opts, { $up: parent })) :
1361
1367
  closestScope(el)
1362
1368
  )
1363
1369
  }
@@ -1426,7 +1432,7 @@ console.log("LiteJS is in debug mode and that's fine for production")
1426
1432
  /*** kb ***/
1427
1433
  var kbMaps = []
1428
1434
  , kbMod = LiteJS.kbMod = /\bMac|\biP/.test(navigator.userAgent) ? "metaKey" : "ctrlKey"
1429
- , 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)
1435
+ , 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(",")
1430
1436
 
1431
1437
  El.addKb = addKb
1432
1438
  El.rmKb = rmKb
@@ -1476,22 +1482,23 @@ console.log("LiteJS is in debug mode and that's fine for production")
1476
1482
  fn = !input || map.input ? map[code] || map[chr] || map.num && code > 47 && code < 58 && (chr|=0, map.num) || map.all : fn
1477
1483
  ) && map.bubble; );
1478
1484
  if (isStr(fn)) setUrl(fn)
1479
- if (isFn(fn)) fn(e, chr, el)
1485
+ else if (isFn(fn)) fn(e, chr, el)
1480
1486
  }
1481
1487
  })
1482
1488
  /**/
1483
1489
 
1484
1490
  /*** touch ***/
1485
- var touchEl, touchDist, touchAngle, touchMode, touchTick
1491
+ var e0, touchDist, touchAngle, touchMode, touchTick
1486
1492
  , START = "start"
1487
1493
  , END = "end"
1488
1494
  , touches = []
1489
- , touchEv = {}
1495
+ , tapCount = 0
1496
+ , tapTime = 0
1490
1497
 
1491
1498
  // swipe + left/right/up/down
1492
- each("hold pan pinch rotate tap", function(name) {
1499
+ each("hold pan pinch rotate tap tap2", function(name) {
1493
1500
  fixEv[name] = fixEv[name + START] = fixEv[name + END] = ""
1494
- fixFn[name] = touchInit
1501
+ fixFn[name] = fixFn[name + START] = fixFn[name + END] = touchInit
1495
1502
  })
1496
1503
  function touchInit(el) {
1497
1504
  if (!el._ti) {
@@ -1505,21 +1512,21 @@ console.log("LiteJS is in debug mode and that's fine for production")
1505
1512
  clearTimeout(touchTick)
1506
1513
  var len = e ? touches.push(e) : touches.length
1507
1514
  , MOVE = "pointermove"
1508
- if (touchMode || len < 1) {
1509
- emit(touchEl, touchMode ? touchMode + END : "tap", e2, touchEv, touchEl)
1515
+ if (touchMode) {
1516
+ emit(el, touchMode + END, e2, el)
1510
1517
  touchMode = UNDEF
1511
- }
1512
- if (len < 1) {
1513
- touchEl = UNDEF
1518
+ } else if (len < 1) {
1519
+ var now = Date.now()
1520
+ tapCount = e2.count = now - tapTime < (LiteJS.tapDelay || 400) ? tapCount + 1 : 1
1521
+ tapTime = now
1522
+ if (!emit(el, "tap" + tapCount, e2, el)) emit(el, "tap", e2, el)
1514
1523
  }
1515
1524
  if (len === 1) {
1516
1525
  if (e) {
1517
- touchEl = e.currentTarget || e.target
1518
- touchEv.X = e.clientX
1519
- touchEv.Y = e.clientY
1526
+ e0 = e
1520
1527
  touchPos("left", "offsetWidth")
1521
1528
  touchPos("top", "offsetHeight")
1522
- if (e.button === 2 || matches(touchEl, "INPUT,TEXTAREA,SELECT,.no-drag")) return
1529
+ if (e.button === 2 || matches(el, "INPUT,TEXTAREA,SELECT,.no-drag")) return
1523
1530
  touchTick = setTimeout(moveOne, LiteJS.holdDelay || 800, e, 1)
1524
1531
  }
1525
1532
  moveOne(e || touches[0])
@@ -1531,15 +1538,9 @@ console.log("LiteJS is in debug mode and that's fine for production")
1531
1538
  ;(len === 1 ? addEvent : rmEvent)(document, MOVE, moveOne)
1532
1539
  ;(len === 2 ? addEvent : rmEvent)(document, MOVE, moveTwo)
1533
1540
  function touchPos(name, offset) {
1534
- var val = (
1535
- touchEl.getBBox ?
1536
- touchEl.getAttributeNS(NUL, name == "top" ? "y":"x") :
1537
- touchEl.style[name]
1538
- )
1539
- touchEv[name] = parseInt(val, 10) || 0
1540
- if (val && val.indexOf("%") > -1) {
1541
- touchEv[name] *= touchEl.parentNode[offset] / 100
1542
- }
1541
+ var val = getAttr(el, name == "top" ? "y" : "x") || getComputedStyle(el)[name]
1542
+ , num = parseInt(val, 10) || 0
1543
+ e0[name] = val && val.indexOf("%") > -1 ? num * el.parentNode[offset] / 100 : num
1543
1544
  }
1544
1545
  }
1545
1546
  function touchUp(e) {
@@ -1557,7 +1558,7 @@ console.log("LiteJS is in debug mode and that's fine for production")
1557
1558
  // alt+wheel may be OS level zoom, use shiftKey as alternative
1558
1559
  if (!touches[0]) {
1559
1560
  var ev = e.ctrlKey ? "pinch" : e.altKey || e.shiftKey ? "rotate" : UNDEF
1560
- if (ev && emit(e.currentTarget || e.target, ev, e, e.deltaY/20, 0)) {
1561
+ if (ev && (e.diff = e.deltaY / 20, e.angle = 0, emit(el, ev, e, el))) {
1561
1562
  return eventStop(e)
1562
1563
  }
1563
1564
  }
@@ -1567,21 +1568,23 @@ console.log("LiteJS is in debug mode and that's fine for production")
1567
1568
  if (touches[0].buttons && touches[0].buttons !== (e.buttons || [0, 1, 4, 2][e.which || 0])) {
1568
1569
  return touchUp(e)
1569
1570
  }
1570
- touchEv.x = e.clientX - touchEv.X
1571
- touchEv.y = e.clientY - touchEv.Y
1572
- touchEv.leftPos = touchEv.x + touchEv.left
1573
- touchEv.topPos = touchEv.y + touchEv.top
1571
+ e.x0 = e0.clientX
1572
+ e.y0 = e0.clientY
1573
+ e.dx = e.clientX - e.x0
1574
+ e.dy = e.clientY - e.y0
1575
+ e.ex = e.dx + e0.left
1576
+ e.ey = e.dy + e0.top
1574
1577
  if (!touchMode) {
1575
- var evs = touchEl._e
1578
+ var evs = el._e
1576
1579
  touchMode = (
1577
- haveEv("pan", touchEv.x > 10 || touchEv.x < -10 || touchEv.y > 10 || touchEv.y < -10) ||
1580
+ haveEv("pan", e.dx > 10 || e.dx < -10 || e.dy > 10 || e.dy < -10) ||
1578
1581
  haveEv("hold", fromTimer)
1579
1582
  )
1580
1583
  if (!touchMode) return
1581
1584
  clearTimeout(touchTick)
1582
- emit(touchEl, touchMode + START, e, touchEv, touchEl)
1585
+ emit(el, touchMode + START, e, el)
1583
1586
  }
1584
- emit(touchEl, touchMode, e, touchEv, touchEl)
1587
+ emit(el, touchMode, e, el)
1585
1588
  function haveEv(name, set) {
1586
1589
  return set && (evs[name] || evs[name + START] || evs[name + END]) && name
1587
1590
  }
@@ -1589,19 +1592,26 @@ console.log("LiteJS is in debug mode and that's fine for production")
1589
1592
  function moveTwo(e) {
1590
1593
  touches[ touches[0].pointerId == e.pointerId ? 0 : 1] = e
1591
1594
  var diff
1592
- , x = touchEv.X - touches[1].clientX
1593
- , y = touchEv.Y - touches[1].clientY
1595
+ , x = e0.clientX - touches[1].clientX
1596
+ , y = e0.clientY - touches[1].clientY
1594
1597
  , dist = Math.sqrt(x*x + y*y) | 0
1595
1598
  , angle = Math.atan2(y, x)
1596
1599
 
1597
1600
  if (touchDist !== UNDEF) {
1598
1601
  diff = dist - touchDist
1599
- if (diff) emit(touchEl, "pinch", e, diff, angle)
1602
+ if (diff) {
1603
+ e.diff = diff
1604
+ e.angle = angle
1605
+ emit(el, "pinch", e, el)
1606
+ }
1600
1607
  // GestureEvent onGestureChange: function(e) {
1601
1608
  // e.target.style.transform =
1602
1609
  // 'scale(' + e.scale + startScale + ') rotate(' + e.rotation + startRotation + 'deg)'
1603
1610
  diff = angle - touchAngle
1604
- if (diff) emit(touchEl, "rotate", e, diff * (180/Math.PI))
1611
+ if (diff) {
1612
+ e.diff = diff * (180/Math.PI)
1613
+ emit(el, "rotate", e, el)
1614
+ }
1605
1615
  }
1606
1616
  touchDist = dist
1607
1617
  touchAngle = angle
@@ -1661,28 +1671,27 @@ console.log("LiteJS is in debug mode and that's fine for production")
1661
1671
  }
1662
1672
  return
1663
1673
  }
1664
- if (prepareVal) val = delegate(el, val, selector, data)
1665
- selector = !prepareVal && selector ? findAll(el, selector) : isArr(el) ? el : [ el ]
1666
- for (delay = 0; (el = selector[delay++]); ) {
1667
- for (var result, arr = ("" + name).split(splitRe), i = 0, len = arr.length; i < len; i++) {
1668
- if (arr[i]) {
1669
- result = fn(el, arr[i], isArr(val) ? val[i] : val, data)
1670
- if (!prepareVal && data > 0) f(el, name, result, "", data)
1674
+ var result, node
1675
+ , arr = ("" + name).split(splitRe)
1676
+ , i = 0, len = arr.length
1677
+ , value = prepareVal && (selector || isStr(val)) ? function(e, mem, target) {
1678
+ target = selector ? closest(e.target, selector) : el
1679
+ if (target) {
1680
+ if (isStr(val)) emit.apply(NUL, [elScope(target).$ui, val, e, target].concat(data))
1681
+ else if (isFn(val)) val.apply(target, [e, target].concat(data))
1682
+ }
1683
+ } : val
1684
+ , els = !prepareVal && selector ? findAll(el, selector) : isArr(el) ? el : [ el ]
1685
+ for (; (node = els[i++]); ) {
1686
+ for (delay = 0; delay < len; delay++) {
1687
+ if (arr[delay]) {
1688
+ result = fn(node, arr[delay], isArr(value) ? value[delay] : value, data)
1689
+ if (!prepareVal && data > 0) f(node, name, result, "", data)
1671
1690
  }
1672
1691
  }
1673
1692
  }
1674
1693
  }
1675
1694
  }
1676
- function delegate(el, val, selector, data) {
1677
- return isStr(val) ? function(e) {
1678
- var target = selector ? closest(e.target, selector) : el
1679
- if (target) emit.apply(target, [elScope(el).$ui, val, e, target].concat(data))
1680
- } :
1681
- selector ? function(e, touchEv, touchEl) {
1682
- if (matches(touchEl = e.target, selector)) val(e, touchEv, touchEl, data)
1683
- } :
1684
- val
1685
- }
1686
1695
  }
1687
1696
  function assignDeep(target, map) {
1688
1697
  if (map) for (var k in map) if (hasOwn(map, k)) {
@@ -1779,7 +1788,7 @@ console.log("LiteJS is in debug mode and that's fine for production")
1779
1788
  res = res.concat(sources, next && next.src && next.innerHTML)
1780
1789
  if (res[sources.length = 0]) {
1781
1790
  if (!parser) {
1782
- var m = res.join("\n").match(/\n%ui\s+(\{[\s\S]*\})\s*$/)
1791
+ var m = res.join("\n").match(/\n%ui\s+(\{[\s\S]*)/)
1783
1792
  LiteJS.ui = LiteJS(m && Function("return " + m[1])())
1784
1793
  }
1785
1794
  each(res, parser)