@pacem/pacem-core 1.0.0-abel
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/LICENSE +191 -0
- package/NOTICE +4 -0
- package/dist/browser/pacem-core.js +9099 -0
- package/dist/browser/pacem-core.js.map +1 -0
- package/dist/browser/pacem-core.min.js +2 -0
- package/dist/browser/pacem-core.min.js.map +1 -0
- package/dist/bundle/pacem-core.min.mjs +3 -0
- package/dist/bundle/pacem-core.mjs +8479 -0
- package/dist/bundle/pacem-core.mjs.map +7 -0
- package/dist/esm/adapter.js +7 -0
- package/dist/esm/command.js +10 -0
- package/dist/esm/comparers.js +8 -0
- package/dist/esm/converters.js +70 -0
- package/dist/esm/decorators-next.js +783 -0
- package/dist/esm/decorators.js +736 -0
- package/dist/esm/defaults.js +5 -0
- package/dist/esm/events.js +89 -0
- package/dist/esm/eventtarget.js +131 -0
- package/dist/esm/expression.js +228 -0
- package/dist/esm/index-animations.js +1 -0
- package/dist/esm/index-behaviors.js +1 -0
- package/dist/esm/index-components.js +1 -0
- package/dist/esm/index-iife.js +3 -0
- package/dist/esm/index-logging.js +1 -0
- package/dist/esm/index-net.js +1 -0
- package/dist/esm/index-root.js +20 -0
- package/dist/esm/index-ui.js +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/logger.js +11 -0
- package/dist/esm/prefix.js +6 -0
- package/dist/esm/promise.js +34 -0
- package/dist/esm/regex.js +22 -0
- package/dist/esm/storage.js +56 -0
- package/dist/esm/template.js +4 -0
- package/dist/esm/transforms.js +248 -0
- package/dist/esm/types.js +10 -0
- package/dist/esm/utils-customelement.js +524 -0
- package/dist/esm/utils-customevent.js +60 -0
- package/dist/esm/utils.js +1073 -0
- package/package.json +37 -0
- package/typings/index.d.ts +2521 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
/// <reference path="expression.ts" />
|
|
2
|
+
/// <reference path="promise.ts" />
|
|
3
|
+
/// <reference path="prefix.ts" />
|
|
4
|
+
/// <reference path="trees/json.ts" />
|
|
5
|
+
/// <reference path="utils.ts" />
|
|
6
|
+
import { Utils } from './utils';
|
|
7
|
+
import { INSTANCE_SCOPE_VAR, WATCH_PROPS_VAR, INSTANCE_HOST_VAR, INSTANCE_BINDINGS_VAR } from './types';
|
|
8
|
+
import { PCSS } from './prefix';
|
|
9
|
+
import { Expression } from './expression';
|
|
10
|
+
import { TemplateElement } from './template';
|
|
11
|
+
// namespace Pacem {
|
|
12
|
+
const PACEM_BAG = '__pacem__';
|
|
13
|
+
// {{ binding.expression }}
|
|
14
|
+
const bindingPattern = /^\{\{(.|\n)+\}\}$/;
|
|
15
|
+
// TODO: CSP-proof binding (no unsafe-eval)
|
|
16
|
+
// binding syntax example: `{ binding: { source: ^item.property.path, binder: $pacem.func, parameters: [ ^index, 'foo', : host._baz], mode: 'twoway' } }`
|
|
17
|
+
export class CustomElementUtils {
|
|
18
|
+
static get polyfilling() {
|
|
19
|
+
return customElements instanceof window['CustomElementRegistry'];
|
|
20
|
+
}
|
|
21
|
+
static camelToKebab(camelCased) {
|
|
22
|
+
return camelCased && camelCased.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
23
|
+
}
|
|
24
|
+
static kebabToCamel(kebabCased) {
|
|
25
|
+
return kebabCased && kebabCased.replace(/(-[a-z])/g, (m) => {
|
|
26
|
+
return m[1].toUpperCase();
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
static getWatchedProperties(target, includeInherited = true) {
|
|
30
|
+
var properties = [];
|
|
31
|
+
var chain = target instanceof HTMLElement ? target.constructor : target;
|
|
32
|
+
do {
|
|
33
|
+
let additional = this.getAttachedPropertyValue(chain, WATCH_PROPS_VAR) || [];
|
|
34
|
+
let pblic = additional.filter(p => !p.name.startsWith('_'));
|
|
35
|
+
Array.prototype.splice.apply(properties, [0, 0].concat(pblic));
|
|
36
|
+
} while (includeInherited && (chain = Object.getPrototypeOf(chain)));
|
|
37
|
+
return properties;
|
|
38
|
+
}
|
|
39
|
+
static getWatchedProperty(target, name) {
|
|
40
|
+
return this.getWatchedProperties(target, true).find(p => p.name === name);
|
|
41
|
+
}
|
|
42
|
+
static importjs(src, integrity = null, crossorigin = false) {
|
|
43
|
+
var attrs = { 'type': 'text\/javascript', 'src': src };
|
|
44
|
+
if (!Utils.isNullOrEmpty(integrity))
|
|
45
|
+
Utils.extend(attrs, { integrity: integrity });
|
|
46
|
+
if (crossorigin)
|
|
47
|
+
Utils.extend(attrs, { 'crossorigin': '' });
|
|
48
|
+
return CustomElementUtils.import(src, 'script', attrs, (document.head || document.getElementsByTagName("head")[0]));
|
|
49
|
+
}
|
|
50
|
+
static importcss(src, integrity = null, crossorigin = false) {
|
|
51
|
+
var attrs = { 'rel': 'stylesheet', 'href': src };
|
|
52
|
+
if (!Utils.isNullOrEmpty(integrity))
|
|
53
|
+
Utils.extend(attrs, { integrity: integrity });
|
|
54
|
+
if (crossorigin)
|
|
55
|
+
Utils.extend(attrs, { 'crossorigin': 'anonymous' });
|
|
56
|
+
return CustomElementUtils.import(src, 'link', attrs, (document.head || document.getElementsByTagName("head")[0]));
|
|
57
|
+
}
|
|
58
|
+
static import(key, tagName, attrs, appendTo = document.body) {
|
|
59
|
+
const _p = Utils.core;
|
|
60
|
+
var _imports = _p['imports'] = _p['imports'] || {};
|
|
61
|
+
return _imports[key] = _imports[key] || new Promise((resolve, reject) => {
|
|
62
|
+
// try to find the equivalent element in the DOM
|
|
63
|
+
var selector = tagName;
|
|
64
|
+
for (let attr in attrs || {}) {
|
|
65
|
+
selector += `[${attr}="${attrs[attr]}"]`;
|
|
66
|
+
}
|
|
67
|
+
var script = document.querySelector(selector);
|
|
68
|
+
if (!Utils.isNull(script)) {
|
|
69
|
+
// best guess: since element was already there, it is hopefully fully loaded
|
|
70
|
+
resolve(script);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
script = document.createElement(tagName);
|
|
74
|
+
script.onerror = e => {
|
|
75
|
+
reject(e);
|
|
76
|
+
};
|
|
77
|
+
script.onload = () => {
|
|
78
|
+
resolve(script);
|
|
79
|
+
};
|
|
80
|
+
for (var attr in (attrs || {})) {
|
|
81
|
+
script.setAttribute(attr, attrs[attr]);
|
|
82
|
+
}
|
|
83
|
+
appendTo.appendChild(script);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
static { this.Theme = {
|
|
88
|
+
importWebFonts() {
|
|
89
|
+
function cleanupQuotes(input) {
|
|
90
|
+
// check if the string is wrapped around quotes to escape ';' chars
|
|
91
|
+
if (/^('|").*\1$/.test(input)) {
|
|
92
|
+
return input.substring(1, input.length - 1);
|
|
93
|
+
}
|
|
94
|
+
return input;
|
|
95
|
+
}
|
|
96
|
+
function downloadFont(fontFace) {
|
|
97
|
+
return Utils.isNullOrEmpty(fontFace) ? Promise.resolve() : document.fonts.load('1rem ' + fontFace);
|
|
98
|
+
}
|
|
99
|
+
const promises = ['icons', 'std', 'main', 'tech', 'btn'].map(f => {
|
|
100
|
+
const fontFace = Utils.Css.getVariableValue(`--${PCSS}-font-${f}`).split(',')[0];
|
|
101
|
+
const url = cleanupQuotes(Utils.Css.getVariableValue(`--${PCSS}-font-${f}-api-url`));
|
|
102
|
+
if (Utils.isNullOrEmpty(url)) {
|
|
103
|
+
return downloadFont(fontFace);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return CustomElementUtils.importcss(url).then(_ => downloadFont(fontFace));
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
const atLeast1FontFaceReady = new Promise((resolve) => {
|
|
110
|
+
function check(count = 0) {
|
|
111
|
+
// size against the specs: https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/size#specifications
|
|
112
|
+
if (count > 4 || !('size' in document.fonts) || typeof document.fonts.size === 'number' && document.fonts.size > 0) {
|
|
113
|
+
document.fonts.ready.then(resolve);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
setTimeout(check, 250, count++);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
check();
|
|
120
|
+
});
|
|
121
|
+
promises.push(atLeast1FontFaceReady);
|
|
122
|
+
return Promise.all(promises);
|
|
123
|
+
}
|
|
124
|
+
}; }
|
|
125
|
+
static setAttachedPropertyValue(target, name, value) {
|
|
126
|
+
/*(target[PACEM_BAG] = target[PACEM_BAG] || {})[name] = value;*/
|
|
127
|
+
if (Utils.isNull(target))
|
|
128
|
+
return;
|
|
129
|
+
const propKey = Object.getOwnPropertyDescriptor(target, PACEM_BAG);
|
|
130
|
+
const bag = (propKey && propKey.value)
|
|
131
|
+
|| Object.defineProperty(target, PACEM_BAG, {
|
|
132
|
+
value: {}, enumerable: false, writable: false, configurable: false
|
|
133
|
+
})[PACEM_BAG];
|
|
134
|
+
if (Utils.isNull(value))
|
|
135
|
+
delete bag[name];
|
|
136
|
+
else
|
|
137
|
+
bag[name] = value;
|
|
138
|
+
}
|
|
139
|
+
static deleteAttachedPropertyValue(target, name) {
|
|
140
|
+
CustomElementUtils.setAttachedPropertyValue(target, name, undefined);
|
|
141
|
+
}
|
|
142
|
+
static getAttachedPropertyValue(target, name, ensureValue) {
|
|
143
|
+
const propKey = Object.getOwnPropertyDescriptor(target, PACEM_BAG), bag = propKey && propKey.value;
|
|
144
|
+
if (Utils.isNull(bag && bag[name]) && !Utils.isNull(ensureValue)) {
|
|
145
|
+
CustomElementUtils.setAttachedPropertyValue(target, name, ensureValue);
|
|
146
|
+
return ensureValue;
|
|
147
|
+
}
|
|
148
|
+
return bag && bag[name];
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Resolves a dotted path into actual values.
|
|
152
|
+
* @param path Dotted path
|
|
153
|
+
* @param scope Root of the path
|
|
154
|
+
*/
|
|
155
|
+
static resolvePath(path, scope) {
|
|
156
|
+
if (!path || !scope)
|
|
157
|
+
return undefined;
|
|
158
|
+
if (/[\(\)\{\}]/.test(path))
|
|
159
|
+
throw `Security alert: not allowed to process paths like ${path}.`;
|
|
160
|
+
const trunks = path.split('.');
|
|
161
|
+
var root = scope, ref = root, parent, core = trunks[0];
|
|
162
|
+
trunks.forEach((t, i) => {
|
|
163
|
+
var trunksSq = t.split('[');
|
|
164
|
+
trunksSq.forEach((t2, j) => {
|
|
165
|
+
parent = ref;
|
|
166
|
+
if (/\]$/.test(t2)) {
|
|
167
|
+
t2 = t2.substring(0, t2.length - 1);
|
|
168
|
+
if (/^('|").+[^\\]\1$/.test(t2)) {
|
|
169
|
+
t2 = t2.substr(1, t2.length - 2);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
ref = ref[t2];
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
var name, ndx;
|
|
176
|
+
if (/\]$/.test(path)) {
|
|
177
|
+
ndx = path.lastIndexOf('[');
|
|
178
|
+
name = path.substring(ndx + 1);
|
|
179
|
+
name = name.substring(0, name.length - 1);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
ndx = path.lastIndexOf('.');
|
|
183
|
+
name = path.substring(ndx + 1);
|
|
184
|
+
}
|
|
185
|
+
return { target: { value: ref, name: name }, parent: { value: parent || scope, path: path.substring(0, ndx) }, root: { value: root, property: core } };
|
|
186
|
+
}
|
|
187
|
+
static set(element, path, value) {
|
|
188
|
+
var obj = CustomElementUtils.resolvePath(path, element);
|
|
189
|
+
var current = obj.target.value;
|
|
190
|
+
if (current !== value) {
|
|
191
|
+
if (obj.parent.value != obj.root.value /* nested property scenario */
|
|
192
|
+
&& !(obj.parent.value instanceof HTMLElement) /* and not targeting an HTMLElement (which will eventually fire autonomously) */) {
|
|
193
|
+
// 1. set new value (won't fire 'propertychange' since it's a nested property)
|
|
194
|
+
obj.parent.value[obj.target.name] = value;
|
|
195
|
+
// 2. clone the resulting object (will be set as the new one at 4.)
|
|
196
|
+
const set_val = Utils.clone(obj.root.value[obj.root.property]);
|
|
197
|
+
// 3. reset to previous value (so that when 4. will trigger the 'propertychange' we'll have meaningful arguments)
|
|
198
|
+
obj.parent.value[obj.target.name] = current;
|
|
199
|
+
// repeater-item?
|
|
200
|
+
let repItem;
|
|
201
|
+
if (obj.root.property === 'item' && !Utils.isNull(repItem = RepeaterItem.getRepeaterItem(obj.root.value))) {
|
|
202
|
+
// kind of ugly solution, I know. it works however...
|
|
203
|
+
// trigger array update
|
|
204
|
+
repItem.repeater && repItem.repeater.datasource && repItem.repeater.datasource.splice(repItem.index, 1, repItem.item = set_val);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
// 4. force root property change
|
|
208
|
+
obj.root.value[obj.root.property] = set_val;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
// parent === root, property change event fires automatically
|
|
213
|
+
obj.parent.value[obj.target.name] = value;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
static get(element, path) {
|
|
218
|
+
var obj = CustomElementUtils.resolvePath(path, element);
|
|
219
|
+
return obj && obj.target && obj.target.value;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Works for unit-test contexts.
|
|
223
|
+
* @param element
|
|
224
|
+
*/
|
|
225
|
+
static _findScopeContextLegacy(element) {
|
|
226
|
+
// old impl...
|
|
227
|
+
let el = element;
|
|
228
|
+
let retval;
|
|
229
|
+
do {
|
|
230
|
+
el = el.parentNode;
|
|
231
|
+
} while (el != null && (el != (retval = element.ownerDocument) || (el instanceof HTMLElement && (retval = el.shadowRoot) != null)));
|
|
232
|
+
//let logFn: (message?: string) => void;
|
|
233
|
+
//if (retval == null && console && (logFn = console.debug))
|
|
234
|
+
// logFn(`Element "${element.constructor.name}" isn't attached to any context yet.`);
|
|
235
|
+
return retval;
|
|
236
|
+
}
|
|
237
|
+
static findScopeContext(element) {
|
|
238
|
+
let retval = (element.isConnected) ?
|
|
239
|
+
element.getRootNode()
|
|
240
|
+
: CustomElementUtils.getAttachedPropertyValue(element, INSTANCE_SCOPE_VAR);
|
|
241
|
+
return retval ?? /* in Jasmine TESTS `element.isConnected` always returns false (attached to #document-fragment) */ CustomElementUtils._findScopeContextLegacy(element);
|
|
242
|
+
}
|
|
243
|
+
static findAncestor(element, predicate) {
|
|
244
|
+
let el = element;
|
|
245
|
+
let retval;
|
|
246
|
+
while (el && (el = el.parentNode || el.getRootNode()?.host) != null) {
|
|
247
|
+
if (el['host'] instanceof HTMLElement)
|
|
248
|
+
el = el['host'];
|
|
249
|
+
if (predicate.apply(el, [el])) {
|
|
250
|
+
retval = el;
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return retval;
|
|
255
|
+
}
|
|
256
|
+
static findAncestorShell(element) {
|
|
257
|
+
return CustomElementUtils.findAncestor(element, el => Utils.is(el, `.${PCSS}-body`) || el === document.body);
|
|
258
|
+
}
|
|
259
|
+
static findAncestorOfType(element, ctor) {
|
|
260
|
+
return CustomElementUtils.findAncestor(element, el => el instanceof ctor);
|
|
261
|
+
}
|
|
262
|
+
static findDescendants(element, predicate, firstOnly = false) {
|
|
263
|
+
const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT);
|
|
264
|
+
var retval = [];
|
|
265
|
+
while (walker.nextNode()) {
|
|
266
|
+
let n = walker.currentNode;
|
|
267
|
+
if (predicate(n) === true) {
|
|
268
|
+
retval.push(n);
|
|
269
|
+
if (firstOnly) {
|
|
270
|
+
return retval;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return retval;
|
|
275
|
+
}
|
|
276
|
+
static findFirstDescendant(element, predicate) {
|
|
277
|
+
const any = CustomElementUtils.findDescendants(element, predicate, true);
|
|
278
|
+
return Utils.isNullOrEmpty(any) ? void 0 : any[0];
|
|
279
|
+
}
|
|
280
|
+
static findAll(selector = '[pacem]', filter = (e) => true) {
|
|
281
|
+
const retval = [];
|
|
282
|
+
document.querySelectorAll(selector).forEach((e, i, arr) => {
|
|
283
|
+
if (filter(e) === true) {
|
|
284
|
+
retval.push(e);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
return retval;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* To be used back again when Edge will allow Comment subclassing
|
|
291
|
+
* https://github.com/Microsoft/ChakraCore/issues/2999 and https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12080908/
|
|
292
|
+
* @param element
|
|
293
|
+
* @param ctor
|
|
294
|
+
* @param upLevels
|
|
295
|
+
*/
|
|
296
|
+
static findPreviousSiblingOrAncestorOfType(element, ctor, upLevels = 0) {
|
|
297
|
+
let el = element;
|
|
298
|
+
let retval = null;
|
|
299
|
+
let levels = 0;
|
|
300
|
+
do {
|
|
301
|
+
el = el.previousSibling || el.parentNode;
|
|
302
|
+
if (el instanceof ctor) {
|
|
303
|
+
if (levels >= upLevels) {
|
|
304
|
+
retval = el;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
el = el.parentNode;
|
|
309
|
+
}
|
|
310
|
+
levels++;
|
|
311
|
+
}
|
|
312
|
+
} while (el != null);
|
|
313
|
+
return retval;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* To be removed when Edge will allow Comment subclassing
|
|
317
|
+
* https://github.com/Microsoft/ChakraCore/issues/2999 and https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12080908/
|
|
318
|
+
* @param element
|
|
319
|
+
* @param predicate
|
|
320
|
+
* @param upLevels
|
|
321
|
+
*/
|
|
322
|
+
static findPreviousSiblingOrAncestor(element, predicate, upLevels = 0) {
|
|
323
|
+
let el = element;
|
|
324
|
+
let retval = null;
|
|
325
|
+
let levels = 0;
|
|
326
|
+
do {
|
|
327
|
+
el = el.previousSibling || el.parentNode;
|
|
328
|
+
if (el && predicate.apply(el, [el])) {
|
|
329
|
+
if (levels >= upLevels) {
|
|
330
|
+
retval = el;
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
el = el.parentNode;
|
|
335
|
+
}
|
|
336
|
+
levels++;
|
|
337
|
+
}
|
|
338
|
+
} while (el != null);
|
|
339
|
+
return retval;
|
|
340
|
+
}
|
|
341
|
+
static assignHostContext(host, template) {
|
|
342
|
+
// navigate through the DOM hierarchy
|
|
343
|
+
const walker = document.createTreeWalker(template.content, NodeFilter.SHOW_ELEMENT + /* traverse templates' content */ NodeFilter.SHOW_DOCUMENT_FRAGMENT);
|
|
344
|
+
while (walker.nextNode()) {
|
|
345
|
+
var node = walker.currentNode;
|
|
346
|
+
/*if (node instanceof HTMLTemplateElement)
|
|
347
|
+
CustomElementUtils.assignHostContext(host, node);
|
|
348
|
+
else*/ if (Utils.isNull(CustomElementUtils.getAttachedPropertyValue(node, INSTANCE_HOST_VAR))) {
|
|
349
|
+
CustomElementUtils.setAttachedPropertyValue(node, INSTANCE_HOST_VAR, host);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
static findHostContext(element) {
|
|
354
|
+
let el = element;
|
|
355
|
+
let retval;
|
|
356
|
+
// not connected to the DOM? then exit and return undefined
|
|
357
|
+
if (!el.isConnected) {
|
|
358
|
+
return retval;
|
|
359
|
+
}
|
|
360
|
+
/*const root = element.getRootNode({ composed: false });
|
|
361
|
+
if (root instanceof ShadowRoot) {
|
|
362
|
+
return root.host as HTMLElement;
|
|
363
|
+
}*/
|
|
364
|
+
while (el != null) {
|
|
365
|
+
retval = CustomElementUtils.getAttachedPropertyValue(el, INSTANCE_HOST_VAR);
|
|
366
|
+
if (retval instanceof HTMLElement)
|
|
367
|
+
break;
|
|
368
|
+
el = el.parentElement;
|
|
369
|
+
}
|
|
370
|
+
let logFn;
|
|
371
|
+
if (retval == null && console && (logFn = console.warn))
|
|
372
|
+
logFn(`Element "${element.constructor.name}" isn't a descendant of a templated component.`);
|
|
373
|
+
return retval;
|
|
374
|
+
}
|
|
375
|
+
static isBindingAttribute(attr) {
|
|
376
|
+
/*if (/^\s*\{\s*binding\s+.*\}\s*$/.test(attr)) {
|
|
377
|
+
|
|
378
|
+
// { binding ... } syntax
|
|
379
|
+
var fn = attr.replace('binding', '').trim();
|
|
380
|
+
fn = fn.substring(1, fn.length - 1);
|
|
381
|
+
let twoway = /,\s*twoway\s*=\s*true\s*$/.test(fn);
|
|
382
|
+
let commaNdx = fn.indexOf(',');
|
|
383
|
+
if (commaNdx == -1)
|
|
384
|
+
commaNdx = fn.length;
|
|
385
|
+
var expression = Expression.parse(fn.substr(0, commaNdx), element);
|
|
386
|
+
(expression && expression.dependencies).forEach(d => {
|
|
387
|
+
d.twoway = d.twowayAllowed && twoway && expression.dependencies.length == 1;
|
|
388
|
+
});
|
|
389
|
+
return expression;
|
|
390
|
+
}
|
|
391
|
+
else*/
|
|
392
|
+
return bindingPattern.test(attr);
|
|
393
|
+
}
|
|
394
|
+
static extractBindingAttributeExpression(attr) {
|
|
395
|
+
if (CustomElementUtils.isBindingAttribute(attr)) {
|
|
396
|
+
return attr.substr(2, attr.length - 4);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
throw `Invalid attribute: incorrect binding syntax.`;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
static parseBindingAttribute(attr, element) {
|
|
403
|
+
// loose syntax {{ ... }}
|
|
404
|
+
let expression = CustomElementUtils.extractBindingAttributeExpression(attr);
|
|
405
|
+
const arr = /,\s*(twoway|once)\s*$/.exec(expression), mode = arr && arr.length > 1 && arr[1];
|
|
406
|
+
if (!Utils.isNullOrEmpty(mode)) {
|
|
407
|
+
expression = expression.substr(0, expression.lastIndexOf(','));
|
|
408
|
+
}
|
|
409
|
+
var expr = Expression.parse(expression, element);
|
|
410
|
+
(expr && expr.dependencies).forEach(d => {
|
|
411
|
+
switch (mode) {
|
|
412
|
+
case 'twoway':
|
|
413
|
+
d.mode = (d.twowayAllowed && expr.dependencies.length == 1) ? 'twoway' : undefined;
|
|
414
|
+
break;
|
|
415
|
+
case 'once':
|
|
416
|
+
d.mode = mode;
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
return expr;
|
|
421
|
+
}
|
|
422
|
+
static ensureMember(o, name, attributes) {
|
|
423
|
+
let original = Object.getOwnPropertyDescriptor(o, name);
|
|
424
|
+
if (original != undefined)
|
|
425
|
+
return original;
|
|
426
|
+
Object.defineProperty(o, name, attributes);
|
|
427
|
+
return Object.getOwnPropertyDescriptor(o, name);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Strips all the observed attributes of an element and its descendants, in order to seal that dom branch and to avoid bindings evaluation.
|
|
431
|
+
* @param element Input element
|
|
432
|
+
*/
|
|
433
|
+
static stripObservedAttributes(element) {
|
|
434
|
+
avoidObservedAttributes(element, false);
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Freezes all the observed attributes of an element and its descendants, in order to seal that dom branch and to avoid bindings evaluation.
|
|
438
|
+
* @param element Input element
|
|
439
|
+
*/
|
|
440
|
+
static freezeObservedAttributes(element) {
|
|
441
|
+
avoidObservedAttributes(element);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
function avoidObservedAttributes(element, freeze = true) {
|
|
445
|
+
const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT);
|
|
446
|
+
do {
|
|
447
|
+
var node = walker.currentNode;
|
|
448
|
+
if (node instanceof HTMLElement) {
|
|
449
|
+
const attrs = node.constructor['observedAttributes'];
|
|
450
|
+
if (!Utils.isNull(attrs)) {
|
|
451
|
+
CustomElementUtils.deleteAttachedPropertyValue(node, INSTANCE_BINDINGS_VAR);
|
|
452
|
+
let revertFn = function (v) {
|
|
453
|
+
if (Utils.isArray(v)) {
|
|
454
|
+
return '[' + v.map(revertFn).join(',') + ']';
|
|
455
|
+
}
|
|
456
|
+
if (v instanceof Element) {
|
|
457
|
+
const vid = v.id = v.id || ('_' + Utils.uniqueCode());
|
|
458
|
+
return `#${vid}`;
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
return `${Utils.Json.stringify(v)}`;
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
for (var attr of attrs) {
|
|
465
|
+
if (attr in node.attributes) {
|
|
466
|
+
let prop;
|
|
467
|
+
let val;
|
|
468
|
+
if (freeze && !Utils.isNull(val = node[prop = CustomElementUtils.kebabToCamel(attr)])) {
|
|
469
|
+
let reverted = revertFn(val);
|
|
470
|
+
node.setAttribute(attr, `{{ ${reverted} }}`);
|
|
471
|
+
}
|
|
472
|
+
else
|
|
473
|
+
node.removeAttribute(attr);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
} while (walker.nextNode());
|
|
479
|
+
}
|
|
480
|
+
const GET_VAL = CustomElementUtils.getAttachedPropertyValue;
|
|
481
|
+
const SET_VAL = CustomElementUtils.setAttachedPropertyValue;
|
|
482
|
+
const REPEATERITEM_PLACEHOLDER = 'pacem:repeater-item';
|
|
483
|
+
export class Repeater {
|
|
484
|
+
/**
|
|
485
|
+
* Seeks for a PacemRepeaterItem element upwards through the DOM tree, given a starting element and the number of nesting levels.
|
|
486
|
+
* @param element {HTMLElement} Element to start the upward search from
|
|
487
|
+
* @param Optional upLevels {Number} 0-based nesting level
|
|
488
|
+
*/
|
|
489
|
+
static findItemContext(element, upLevels = 0, logFn = console.warn) {
|
|
490
|
+
let retval = RepeaterItem.findUpwards(element, upLevels, logFn);
|
|
491
|
+
if (retval == null && logFn && element instanceof HTMLElement && element['isConnected']) {
|
|
492
|
+
logFn(`Couldn't find a ${RepeaterItem.name} up ${upLevels} level${((upLevels === 1) ? "" : "s")} from element "${element.constructor.name}".`);
|
|
493
|
+
}
|
|
494
|
+
return retval;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
export class RepeaterItem {
|
|
498
|
+
static findUpwards(element, upLevels = 0, logFn = console.warn) {
|
|
499
|
+
if (Utils.isNull(element) || element.localName === 'template' || element instanceof /* template-like element? */ TemplateElement) {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
let item = null;
|
|
503
|
+
let predicate = (node) => !Utils.isNull(item = RepeaterItem.getRepeaterItem(node));
|
|
504
|
+
let retval = CustomElementUtils.findPreviousSiblingOrAncestor(element, predicate, upLevels);
|
|
505
|
+
if (retval == null && logFn && element instanceof HTMLElement && element['isConnected']) {
|
|
506
|
+
logFn(`Couldn't find a ${RepeaterItem.name} up ${upLevels} level${((upLevels === 1) ? "" : "s")} from element "${element.constructor.name}".`);
|
|
507
|
+
}
|
|
508
|
+
return item;
|
|
509
|
+
}
|
|
510
|
+
static isRepeaterItem(node) {
|
|
511
|
+
return !Utils.isNull(GET_VAL(node, REPEATERITEM_PLACEHOLDER));
|
|
512
|
+
}
|
|
513
|
+
static getRepeaterItem(node) {
|
|
514
|
+
return GET_VAL(node, REPEATERITEM_PLACEHOLDER);
|
|
515
|
+
}
|
|
516
|
+
constructor(_placeholder) {
|
|
517
|
+
this._placeholder = _placeholder;
|
|
518
|
+
SET_VAL(_placeholder, REPEATERITEM_PLACEHOLDER, this);
|
|
519
|
+
}
|
|
520
|
+
get placeholder() {
|
|
521
|
+
return this._placeholder;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
// }
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Utils } from './utils';
|
|
2
|
+
export var EventKeyModifier;
|
|
3
|
+
(function (EventKeyModifier) {
|
|
4
|
+
EventKeyModifier["AltKey"] = "Alt";
|
|
5
|
+
EventKeyModifier["CtrlKey"] = "Ctrl";
|
|
6
|
+
EventKeyModifier["ShiftKey"] = "Shift";
|
|
7
|
+
EventKeyModifier["MetaKey"] = "Cmd";
|
|
8
|
+
})(EventKeyModifier || (EventKeyModifier = {}));
|
|
9
|
+
export class CustomEventUtils {
|
|
10
|
+
static isInstanceOf(evt, type) {
|
|
11
|
+
return evt instanceof type;
|
|
12
|
+
// || CustomElementUtils.getAttachedPropertyValue(evt, 'pacem:custom-event') === evt.type;
|
|
13
|
+
}
|
|
14
|
+
static getEventCoordinates(evt) {
|
|
15
|
+
var src;
|
|
16
|
+
if ('touches' in evt) {
|
|
17
|
+
if (!Utils.isNullOrEmpty(evt.touches)) {
|
|
18
|
+
src = evt.touches[0];
|
|
19
|
+
}
|
|
20
|
+
else if (!Utils.isNullOrEmpty(evt.changedTouches)) {
|
|
21
|
+
src = evt.changedTouches[0];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
src = evt;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
page: { x: src.pageX, y: src.pageY },
|
|
29
|
+
screen: { x: src.screenX, y: src.screenY },
|
|
30
|
+
client: { x: src.clientX, y: src.clientY }
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
static getEventKeyModifiers(evt) {
|
|
34
|
+
var which = evt.which;
|
|
35
|
+
if (evt instanceof MouseEvent) {
|
|
36
|
+
which = evt.button;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
which: which, shiftKey: evt.shiftKey, altKey: evt.altKey, ctrlKey: evt.ctrlKey, metaKey: evt.metaKey
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
static matchModifiers(evt, ...args) {
|
|
43
|
+
const modifiers = !Utils.isNull(args) && (args.length === 1 && Utils.isArray(args[0]) ? args[0] : Array.from(args).filter(i => !Utils.isNullOrEmpty(i))), lowerCaseModifiers = (modifiers || []).map(i => i.toLowerCase());
|
|
44
|
+
const altKey = lowerCaseModifiers.indexOf('alt') >= 0 || lowerCaseModifiers.indexOf('option') >= 0, shiftKey = lowerCaseModifiers.indexOf('shift') >= 0, ctrlKey = lowerCaseModifiers.indexOf('ctrl') >= 0, metaKey = lowerCaseModifiers.indexOf('win') >= 0 || lowerCaseModifiers.indexOf('cmd') >= 0;
|
|
45
|
+
if (altKey !== evt.altKey
|
|
46
|
+
|| shiftKey !== evt.shiftKey
|
|
47
|
+
|| metaKey !== evt.metaKey
|
|
48
|
+
|| ctrlKey !== evt.ctrlKey) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
static fixEdgeCustomEventSubClassInstance(evt, type) {
|
|
54
|
+
// this an optimistic BUGGED workaround due to a damn' Edge bug: https://github.com/Microsoft/ChakraCore/issues/3952
|
|
55
|
+
if (!(evt instanceof type)) {
|
|
56
|
+
Object.setPrototypeOf(evt, type);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//}
|