@nebularstreams/libmui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,444 @@
1
+ import m from "mithril";
2
+
3
+
4
+ ///////////////////////////////////////////////
5
+ // Mithril Range
6
+ // (c) 2017-2018 by Mike Linkovich
7
+ // https://github.com/spacejack/mithril-range
8
+ // License: MIT
9
+ ///////////////////////////////////////////////
10
+ const
11
+
12
+ NONE = 0,
13
+ MOUSE = 1,
14
+ TOUCH = 2,
15
+
16
+ // So we aren't triggered by echoed mouse events (some mobile browsers)
17
+ DEVICE_DELAY = 350,
18
+
19
+ isIOS = !!navigator.userAgent.match(/iPhone|iPad|iPod/i);
20
+
21
+ // This is some unbelievable stone-age nonsense.
22
+ // Workaround for webkit bug where event.preventDefault
23
+ // within touchmove handler fails to prevent scrolling.
24
+
25
+ let
26
+ iOSHackApplied = false;
27
+
28
+ /** Clamp number to range */
29
+ function clamp(n, min, max) {
30
+ return Math.min(Math.max(n, min), max);
31
+ }
32
+
33
+ function applyIOSHack() {
34
+ // Only apply this hack if iOS, haven't yet applied it,
35
+ // and only if a component is actually created
36
+ if (!isIOS || iOSHackApplied) {
37
+ return;
38
+ }
39
+ window.addEventListener("touchmove", function () {
40
+ });
41
+ iOSHackApplied = true;
42
+ }
43
+
44
+ function quantize(val, min, max, step) {
45
+ if (max - min <= 0) {
46
+ return min;
47
+ }
48
+ if (step <= 0) {
49
+ return clamp(val, min, max);
50
+ }
51
+ const
52
+ steps = Math.ceil((max - min) / step),
53
+ v = min + Math.round(steps * (val - min) / (max - min)) * step;
54
+ return clamp(v, min, max);
55
+ }
56
+
57
+ exports.quantize = quantize;
58
+
59
+ /** Range Component */
60
+ function MithrilRange() {
61
+ let
62
+ elHit,
63
+ elBar,
64
+ elBar0,
65
+ elBar1,
66
+ elHandle,
67
+ rcBar,
68
+ device = NONE,
69
+ pressed = false,
70
+
71
+ useAbsolute = false,
72
+ factorCoarse = 4,
73
+ factorFine = 1,
74
+ absolute,
75
+ lastRead,
76
+
77
+ // Attrs we need to cache
78
+ min = 0,
79
+ max = 10,
80
+ value = 0,
81
+ startValue = 0,
82
+ step = 1,
83
+ orientation = "horizontal",
84
+ onchange,
85
+ ondrag;
86
+
87
+ // App handlers
88
+ function onPressAbsolute(x, y) {
89
+ startValue = value;
90
+ pressed = absolute = true;
91
+ rcBar = elBar.getBoundingClientRect();
92
+ const val = moveHandleAbsolute(x, y);
93
+ if (val !== value) {
94
+ value = val;
95
+ if (ondrag && ondrag(value) !== false) {
96
+ m.redraw();
97
+ }
98
+ }
99
+ }
100
+
101
+ function onPressDelta(x, y, e, isTouch) {
102
+ startValue = value;
103
+ pressed = true;
104
+ absolute = false;
105
+ rcBar = elBar.getBoundingClientRect();
106
+ lastRead = (orientation === "vertical") ? y : x;
107
+ if (!isTouch) e.target.requestPointerLock();
108
+ }
109
+
110
+ function moveHandleAbsolute(x, y) {
111
+
112
+ let
113
+ barLength,
114
+ delta;
115
+
116
+ if (orientation === "vertical") {
117
+
118
+ barLength = rcBar.bottom - rcBar.top;
119
+ delta = rcBar.bottom - y;
120
+
121
+ } else {
122
+ barLength = rcBar.right - rcBar.left;
123
+ delta = x - rcBar.left;
124
+ }
125
+
126
+ delta = clamp(delta, 0, barLength);
127
+ const val = quantize((delta / barLength) * (max - min) + min, min, max, step);
128
+ setStyles(val);
129
+ return val;
130
+ }
131
+
132
+ function moveHandleDelta(x, y, event) {
133
+
134
+ const
135
+ factor = event.shiftKey
136
+ ? factorCoarse
137
+ : (event.metaKey || event.ctrlKey)
138
+ ? 4 * factorCoarse
139
+ : factorFine;
140
+
141
+ let
142
+ delta;
143
+
144
+ if (orientation === "vertical") {
145
+ delta = event.movementY || 0;
146
+ } else {
147
+ delta = event.movementX || 0;
148
+ }
149
+
150
+ let newVal;
151
+ let s = step > 0 ? step : (max - min) / 100;
152
+
153
+ if (delta < 0) {
154
+ newVal = Math.max(value + s * factor * delta, min);
155
+ } else if (delta > 0) { // right/up
156
+ newVal = Math.min(value + s * factor * delta, max);
157
+ } else {
158
+ return value;
159
+ }
160
+
161
+ newVal = quantize(newVal, min, max, step);
162
+
163
+ setStyles(newVal);
164
+ return newVal;
165
+
166
+ }
167
+
168
+ function onPress(x, y, event) {
169
+ return useAbsolute
170
+ ? onPressAbsolute(x, y, event)
171
+ : onPressDelta(x, y, event);
172
+ }
173
+
174
+ function moveHandle(x, y, event) {
175
+ return absolute
176
+ ? moveHandleAbsolute(x, y, event)
177
+ : moveHandleDelta(x, y, event);
178
+ }
179
+
180
+ // Event handlers
181
+ function onMouseDown(e) {
182
+ if (device === TOUCH) {
183
+ return;
184
+ }
185
+ device = MOUSE;
186
+ if (e.button !== 0) {
187
+ return;
188
+ }
189
+ window.addEventListener("mousemove", onMouseMove);
190
+ window.addEventListener("mouseup", onMouseUp);
191
+ onPress(e.clientX, e.clientY, e);
192
+ }
193
+
194
+ function onMouseMove(e) {
195
+ e.preventDefault();
196
+ onMove(e.clientX, e.clientY, e);
197
+ }
198
+
199
+ function onMouseUp(e) {
200
+ window.removeEventListener("mousemove", onMouseMove);
201
+ window.removeEventListener("mouseup", onMouseUp);
202
+ onRelease(e.clientX, e.clientY, e);
203
+ }
204
+
205
+ function onTouchStart(e) {
206
+ if (device === MOUSE) {
207
+ return;
208
+ }
209
+ elHit.addEventListener("touchmove", onTouchMove);
210
+ elHit.addEventListener("touchend", onTouchEnd);
211
+ const t = e.changedTouches[0];
212
+ onPress(t.clientX, t.clientY, e, true);
213
+ }
214
+
215
+ function onTouchMove(e) {
216
+ e.preventDefault();
217
+ const t = e.changedTouches[0];
218
+ onMove(t.clientX, t.clientY, e);
219
+ }
220
+
221
+ function onTouchEnd(e) {
222
+ elHit.removeEventListener("touchmove", onTouchMove);
223
+ elHit.removeEventListener("touchend", onTouchEnd);
224
+ const t = e.changedTouches[0];
225
+ onRelease(t.clientX, t.clientY, t);
226
+ }
227
+
228
+ function onKeyDown(e) {
229
+
230
+ const k = e.keyCode;
231
+ let newVal;
232
+
233
+ if (k === 33) { // pgup
234
+ e.preventDefault();
235
+ let s = Math.max((max - min) / 10, step);
236
+ if (s <= 0) {
237
+ s = 1;
238
+ }
239
+ newVal = quantize(value + s, min, max, step);
240
+ } else if (k === 34) { // pgdown
241
+ e.preventDefault();
242
+ let s = Math.max((max - min) / 10, step);
243
+ if (s <= 0) {
244
+ s = 1;
245
+ }
246
+ newVal = quantize(value - s, min, max, step);
247
+ } else if (k === 35) { // end
248
+ e.preventDefault();
249
+ newVal = max;
250
+ } else if (k === 36) { // home
251
+ e.preventDefault();
252
+ newVal = min;
253
+ } else if (k === 37 || k === 40) { // left/down
254
+ e.preventDefault();
255
+ let s = (step > 0 ? step : (max - min) / 10) * (e.shiftKey ? 0.25 : (e.ctrlKey || e.metaKey) ? 4 : 1);
256
+ newVal = Math.max(value - s, min);
257
+ } else if (k === 38 || k === 39) { // right/up
258
+ e.preventDefault();
259
+ let s = (step > 0 ? step : (max - min) / 10) * (e.shiftKey ? 0.25 : (e.ctrlKey || e.metaKey) ? 4 : 1);
260
+ newVal = Math.min(value + s, max);
261
+ } else if (k === 48) { // 0
262
+ newVal = (max + min) / 2;
263
+ }
264
+ if (typeof newVal !== "number" || newVal === value) {
265
+ return; // no change to make
266
+ }
267
+ value = newVal;
268
+ setStyles(value);
269
+ if (onchange && onchange(value) !== false) {
270
+ m.redraw();
271
+ }
272
+ }
273
+
274
+ function onMove(x, y, event) {
275
+ if (!pressed) {
276
+ return;
277
+ }
278
+
279
+ const val = moveHandle(x, y, event);
280
+ if (val !== value) {
281
+ value = val;
282
+ if (ondrag && ondrag(value) !== false) {
283
+ m.redraw();
284
+ }
285
+ }
286
+ }
287
+
288
+ function onRelease(x, y, e) {
289
+
290
+ if (!pressed) {
291
+ return;
292
+ }
293
+
294
+ pressed = false;
295
+ value = moveHandle(x, y, e);
296
+
297
+ if (onchange && onchange(value) !== false) {
298
+ m.redraw();
299
+ }
300
+
301
+ /*
302
+ if (value !== startValue) {
303
+ if (onchange && onchange(value) !== false) {
304
+ m.redraw();
305
+ }
306
+ }
307
+ */
308
+
309
+ if (!absolute) document.exitPointerLock();
310
+
311
+ setTimeout(function () {
312
+ if (!pressed) {
313
+ device = NONE;
314
+ }
315
+ }, DEVICE_DELAY);
316
+ }
317
+
318
+ /** Compute handle position style */
319
+ function positionStyle(val) {
320
+ let s = (val - min) / (max - min);
321
+ if (orientation === "vertical") {
322
+ s = 1.0 - s;
323
+ }
324
+ return String(100 * s) + "%";
325
+ }
326
+
327
+ /** Set styles for movable parts */
328
+ function setStyles(value) {
329
+ const
330
+ ps = positionStyle(value),
331
+ rps = positionStyle(max - value + min);
332
+
333
+ if (orientation === "vertical") {
334
+ elHandle.style.top = ps;
335
+ elBar0.style.height = ps;
336
+ elBar1.style.height = rps;
337
+ } else {
338
+ elHandle.style.left = ps;
339
+ elBar0.style.width = rps;
340
+ elBar1.style.width = ps;
341
+ }
342
+ }
343
+
344
+ /** Some attrs need to be cached (and updated) so that they are current in event handlers */
345
+ function updateAttrs(attrs) {
346
+ min = attrs.min;
347
+ max = attrs.max;
348
+ step = (typeof attrs.step === "number" && !Number.isNaN(attrs.step))
349
+ ? clamp(attrs.step, 0, max - min) : 1;
350
+ orientation = attrs.orientation === "vertical" ? "vertical" : "horizontal";
351
+ onchange = attrs.onchange;
352
+ ondrag = attrs.ondrag;
353
+ if (typeof attrs.value === "number") {
354
+ value = clamp(attrs.value, min, max);
355
+ }
356
+ useAbsolute = !attrs.relative;
357
+ factorCoarse = (attrs.factor && attrs.factor[0]) || 4;
358
+ factorFine = (attrs.factor && attrs.factor[1]) || 1;
359
+
360
+ }
361
+
362
+ /** Return mithril component hooks object */
363
+ return {
364
+ oncreate: ({attrs, dom}) => {
365
+ applyIOSHack();
366
+ updateAttrs(attrs);
367
+ elHit = dom;
368
+ elBar = elHit.querySelector(".mithril-range-bar");
369
+ elBar0 = elBar.querySelector(".mithril-range-bar-0");
370
+ elBar1 = elBar.querySelector(".mithril-range-bar-1");
371
+ elHandle = elBar.querySelector(".mithril-range-handle");
372
+ elHit.addEventListener("mousedown", onMouseDown);
373
+ elHit.addEventListener("touchstart", onTouchStart);
374
+ elHit.addEventListener("keydown", onKeyDown);
375
+ },
376
+ onremove: function () {
377
+ window.removeEventListener("mousemove", onMouseMove);
378
+ window.removeEventListener("mouseup", onMouseUp);
379
+ elHit.removeEventListener("mousedown", onMouseDown);
380
+ elHit.removeEventListener("touchstart", onTouchStart);
381
+ elHit.removeEventListener("touchmove", onTouchMove);
382
+ elHit.removeEventListener("touchend", onTouchEnd);
383
+ elHit.removeEventListener("keydown", onKeyDown);
384
+ },
385
+ view: function ({attrs, children}) {
386
+
387
+ updateAttrs(attrs);
388
+ value = quantize(value, min, max, step);
389
+ const a = {
390
+ class: "mithril-range nokey" + (attrs.relative ? " isrelative " : " ") + (value === 0 ? "atzero" : value < 0 ? "belowzero" : "overzero") + (attrs.class != null ? " " + attrs.class : ""),
391
+ tabIndex: "0",
392
+ role: "slider",
393
+ "aria-valuemin": String(min),
394
+ "aria-valuemax": String(max),
395
+ "aria-valuenow": String(value),
396
+ "aria-orientation": orientation
397
+ };
398
+ if (attrs.id) {
399
+ a.id = attrs.id;
400
+ }
401
+ // noinspection JSUnresolvedVariable
402
+ if (attrs.ariaLabelledby) {
403
+ a["aria-labelledby"] = attrs.ariaLabelledby;
404
+ }
405
+ if (attrs.disabled) {
406
+ a.style = {pointerEvents: "none"};
407
+ a["aria-disabled"] = "true";
408
+ }
409
+ const
410
+ ps = positionStyle(value),
411
+ rps = positionStyle(max - value + min);
412
+ let
413
+ handleStyle,
414
+ bar0Style,
415
+ bar1Style;
416
+
417
+ if (orientation === "vertical") {
418
+ handleStyle = {top: ps};
419
+ bar0Style = {height: ps};
420
+ bar1Style = {height: rps};
421
+ } else {
422
+ handleStyle = {left: ps};
423
+ bar0Style = {width: rps};
424
+ bar1Style = {width: ps};
425
+ }
426
+ return m("div", a, m("div", {class: "mithril-range-bar nokey"}, m("div", {
427
+ class: "mithril-range-bar-0 nokey",
428
+ style: bar0Style
429
+ }), m("div", {
430
+ class: "mithril-range-bar-1 nokey",
431
+ style: bar1Style
432
+ }), m("div", {
433
+ class: "mithril-range-handle nokey",
434
+ style: handleStyle
435
+ }, children)), !!attrs.name && m("input", {
436
+ type: "hidden",
437
+ name: attrs.name,
438
+ value: String(value)
439
+ }));
440
+ }
441
+ };
442
+ }
443
+
444
+ export default MithrilRange;
@@ -0,0 +1,10 @@
1
+ /* global EXPORTS */
2
+
3
+ const
4
+ ENABLE_SCLONE = true, // atm SLOWER than JSON
5
+ softClone = (o) => JSON.parse(JSON.stringify(o)),
6
+ clone = window.structuredClone && ENABLE_SCLONE
7
+ ? window.structuredClone
8
+ : softClone;
9
+
10
+ export default clone;
@@ -0,0 +1,227 @@
1
+ /* eslint-disable */
2
+ /**
3
+ *
4
+ * String Tokenizer functions
5
+ *
6
+ * Substitutes parameters inside strings
7
+ * Parameters are separated by $ at the beggining and end: $PARAM$, $PARAM1$
8
+ * Parameters can navigate source object like $object/very/deep$
9
+ * Parameters can include VERY SIMPLE default values like $object/very/deep=myvalue$
10
+ *
11
+ * Note that default values cannot contain conflicting regexp symbols. This is a side functionality
12
+ * and never meant to support any default value use case, but alpha chars and numbers
13
+ *
14
+ * (c) Rodolfo LP 2001 - Public Domain
15
+ *
16
+ * @typedef {{ id:string,url:string}} UrlDefinition
17
+ * @typedef {{ message:string, getTitle:boolean=false, defaultTitle:string="url" }} ResolveUrlOptions
18
+ */
19
+
20
+ /* eslint-disable no-use-before-define,no-unused-vars,prefer-destructuring,no-param-reassign,no-prototype-builtins */
21
+
22
+ // eslint-disable-next-line no-useless-escape
23
+ // noinspection RegExpRedundantEscape
24
+ const
25
+ TAG = "[TOKENIZER]",
26
+ TOK_EXPR = /\$([A-Za-z0-9_\/\:\=]+)\$/g,
27
+
28
+ tokenize = (str, obj, isEncode) => tokenizeCore(str, obj, {
29
+ encode: isEncode,
30
+ defvalue: ""
31
+ }),
32
+ tokenizeKeep = (str, obj, isEncode) => tokenizeCore(str, obj, {encode: isEncode}),
33
+ tokenizeWithDefault = (str, obj, defaultValue) => tokenizeCore(str, obj, defaultValue == null ? null : {defvalue: defaultValue}),
34
+ tokenizeStrict = (str, obj, isEncode, isUnescape) => tokenizeCore(str, obj, {
35
+ encode: isEncode,
36
+ unescape: isUnescape,
37
+ dothrow: true
38
+ }),
39
+
40
+ Parsers = {
41
+ daterel: epoch => new Date(epoch).toISOString()
42
+ };
43
+
44
+ function tokenizeCore(str, obj, options) {
45
+
46
+ if (!str) return "";
47
+ if (typeof (str) !== "string") return str;
48
+
49
+ // eslint-disable-next-line no-param-reassign
50
+ options = options || {};
51
+ obj = obj || {};
52
+
53
+ let ckey;
54
+
55
+ return str.replace(TOK_EXPR, (match, complexkey) => {
56
+
57
+ let defval = null,
58
+ isFreak = false;
59
+
60
+ if (complexkey.indexOf("=") > 0) {
61
+ ckey = complexkey.split("=");
62
+ complexkey = ckey[0];
63
+ isFreak = (ckey.length === 3);
64
+ // eslint-disable-next-line no-nested-ternary
65
+ defval = isFreak
66
+ ? options.dothrow ? ckey[2] : null
67
+ : ckey[1];
68
+ }
69
+
70
+ const typed = complexkey.split(":"),
71
+ key = typed[0],
72
+ keytype = typed[1] || null,
73
+
74
+ keys = key.split("/"),
75
+ root = keys.shift();
76
+
77
+ let
78
+ data = obj[root];
79
+
80
+ if (typeof (data) === "number") data = "" + data;
81
+
82
+ let fdata = (keys.length ? getPointedData(data, keys.join("/")) : data) || defval;
83
+
84
+ if (fdata == null && options.dothrow) {
85
+ throw new Error("Parameter " + root + (keys.length ? "/" + keys.join("/") : "") + " is required");
86
+ }
87
+
88
+ if (fdata == null) {
89
+ fdata = options.hasOwnProperty("defvalue")
90
+ ? options.defvalue
91
+ : "$" + key + (isFreak ? "==" + ckey[2] : "") + "$";
92
+ }
93
+
94
+ if (keytype && Parsers[keytype]) {
95
+ fdata = Parsers[keytype](fdata);
96
+ }
97
+
98
+ if (options.unescape) {
99
+ fdata = unescape(fdata);
100
+ }
101
+
102
+ return (options.encode ? escape(fdata) : fdata);
103
+
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Resolves a pointer into a JSON data structure
109
+ * @param {Object} data JSON Data
110
+ * @param {String} pointer Pointer expression
111
+ * @return {Object | null} resolved data
112
+ */
113
+
114
+ const getPointedData = function (data, pointer) {
115
+ if (!pointer) return data;
116
+ try {
117
+ const ps = pointer.split("/");
118
+ let d = data;
119
+ // eslint-disable-next-line guard-for-in
120
+ for (const l in ps) {
121
+ d = d[ps[l]];
122
+ if (!d) {
123
+ d = null;
124
+ break;
125
+ }
126
+ }
127
+ return d;
128
+ } catch (e) {
129
+ return null;
130
+ }
131
+ };
132
+
133
+ /**
134
+ * Tokenizes one object with another
135
+ * @param {object} source
136
+ * @param {object} tokenizeValues
137
+ * @param strict Whether to require all templates to be resolved
138
+ * @retuen {object} source
139
+ */
140
+
141
+ function tokenizeObject(source, tokenizeValues, strict, asNewObject) {
142
+
143
+ const tokenizer = strict ? tokenizeStrict : tokenizeKeep;
144
+ const retObject = asNewObject ? {} : source;
145
+
146
+ if (typeof source === "string") {
147
+ return tokenizer(source, tokenizeValues);
148
+ }
149
+
150
+ if (typeof source === "object") {
151
+
152
+ if (source.length) {
153
+ // root is an array
154
+ // an array
155
+ const arr = [];
156
+ for (const item of source) {
157
+ arr.push(tokenizeObject(item, tokenizeValues, strict, true));
158
+ }
159
+ return arr;
160
+ }
161
+
162
+ for (const prop of Object.keys(source)) {
163
+ const value = source[prop];
164
+ if (typeof value === "object") {
165
+ retObject[prop] = tokenizeObject(value, tokenizeValues, strict, true);
166
+ } else if (typeof value === "string") {
167
+ retObject[prop] = tokenizer(source[prop], tokenizeValues);
168
+ } else {
169
+ retObject[prop] = value;
170
+ }
171
+ }
172
+ }
173
+ return retObject;
174
+ }
175
+
176
+ /**
177
+ * Resolves a playout URL
178
+ * @param {string|UrlDefinition} tokenizedUrl The URL to parse
179
+ * @param {Object=} parameters Parameters to tokenize the URL with
180
+ * @param {ResolveUrlOptions=} options Miscellaneous options for this call
181
+ * @return string
182
+ */
183
+
184
+ function tokenizeUrl(tokenizedUrl, parameters, options) {
185
+
186
+ options = options || {};
187
+
188
+ const
189
+ {message, getTitle, defaultTitle} = options,
190
+ finalDefaultTitle = defaultTitle || "Url",
191
+ title = typeof tokenizedUrl === "string"
192
+ ? defaultTitle
193
+ : (typeof tokenizedUrl === "object" && tokenizedUrl.id ? tokenizedUrl.id || finalDefaultTitle : finalDefaultTitle);
194
+
195
+
196
+ // just return the title of an URL
197
+ if (getTitle) {
198
+ return title;
199
+ }
200
+
201
+ // return the url itself
202
+ const
203
+ url = typeof tokenizedUrl === "string"
204
+ ? tokenizedUrl
205
+ : (typeof tokenizedUrl === "object" && tokenizedUrl.url ? tokenizedUrl.url : null);
206
+
207
+ try {
208
+ return tokenizeStrict(
209
+ url,
210
+ parameters || {},
211
+ "..."
212
+ );
213
+ } catch (e) {
214
+ console.log(TAG, e.message);
215
+ return message || "(not enough parameters)";
216
+ }
217
+ }
218
+
219
+ export {
220
+ tokenize,
221
+ tokenizeStrict,
222
+ tokenizeObject,
223
+ tokenizeKeep,
224
+ tokenizeWithDefault,
225
+ tokenizeUrl,
226
+ getPointedData
227
+ };