@qbix/q 1.0.5

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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +678 -0
  3. package/dist/Metrics.js +2873 -0
  4. package/dist/Metrics.min.js +95 -0
  5. package/dist/Q.js +15677 -0
  6. package/dist/Q.min.js +385 -0
  7. package/dist/Q.minimal.js +11594 -0
  8. package/dist/Q.minimal.min.js +286 -0
  9. package/dist/handlebars-v4.0.10.min.js +29 -0
  10. package/dist/handlebars.minimal.min.js +1 -0
  11. package/dist/img/hints/rotate-left.gif +0 -0
  12. package/dist/img/hints/swipe-down.gif +0 -0
  13. package/dist/img/hints/swipe-up.gif +0 -0
  14. package/dist/img/hints/tap.gif +0 -0
  15. package/dist/img/throbbers/loading.gif +0 -0
  16. package/dist/jquery.minimal.min.js +18 -0
  17. package/dist/methods/Q/Audio/load.js +29 -0
  18. package/dist/methods/Q/Audio/loadVoices.js +36 -0
  19. package/dist/methods/Q/Audio/play.js +47 -0
  20. package/dist/methods/Q/Audio/speak.js +149 -0
  21. package/dist/methods/Q/Crypto/delegate.js +186 -0
  22. package/dist/methods/Q/Crypto/internalKeypair.js +170 -0
  23. package/dist/methods/Q/Crypto/sign.js +200 -0
  24. package/dist/methods/Q/Crypto/verify.js +212 -0
  25. package/dist/methods/Q/Crypto/verifyDelegated.js +214 -0
  26. package/dist/methods/Q/Data/Bloom/_internal.js +163 -0
  27. package/dist/methods/Q/Data/Bloom/create.js +23 -0
  28. package/dist/methods/Q/Data/Bloom/fromBase64.js +21 -0
  29. package/dist/methods/Q/Data/Bloom/fromBytes.js +15 -0
  30. package/dist/methods/Q/Data/Bloom/fromElements.js +33 -0
  31. package/dist/methods/Q/Data/Merkle/_internal.js +68 -0
  32. package/dist/methods/Q/Data/Merkle/build.js +29 -0
  33. package/dist/methods/Q/Data/Merkle/proof.js +50 -0
  34. package/dist/methods/Q/Data/Merkle/verify.js +32 -0
  35. package/dist/methods/Q/Data/Prolly/_internal.js +190 -0
  36. package/dist/methods/Q/Data/Prolly/build.js +28 -0
  37. package/dist/methods/Q/Data/Prolly/delete.js +28 -0
  38. package/dist/methods/Q/Data/Prolly/diff.js +70 -0
  39. package/dist/methods/Q/Data/Prolly/get.js +38 -0
  40. package/dist/methods/Q/Data/Prolly/set.js +32 -0
  41. package/dist/methods/Q/Data/compress.js +45 -0
  42. package/dist/methods/Q/Data/decompress.js +35 -0
  43. package/dist/methods/Q/Data/decrypt.js +55 -0
  44. package/dist/methods/Q/Data/derive.js +76 -0
  45. package/dist/methods/Q/Data/digest.js +29 -0
  46. package/dist/methods/Q/Data/encrypt.js +61 -0
  47. package/dist/methods/Q/Data/generateKey.js +40 -0
  48. package/dist/methods/Q/Data/hkdf.js +44 -0
  49. package/dist/methods/Q/Data/importKey.js +34 -0
  50. package/dist/methods/Q/Data/sign.js +42 -0
  51. package/dist/methods/Q/Data/verify.js +45 -0
  52. package/dist/methods/Q/Onboarding/handle.js +50 -0
  53. package/dist/methods/Q/Onboarding/start.js +165 -0
  54. package/dist/methods/Q/Onboarding/stop.js +21 -0
  55. package/dist/methods/Q/Sandbox/run.js +392 -0
  56. package/dist/methods/Q/Tool/define/component.js +218 -0
  57. package/dist/methods/Q/globalMemoryWalk.js +82 -0
  58. package/dist/methods/Q/leaves.js +34 -0
  59. package/dist/methods/Q/registerWebComponent.js +0 -0
  60. package/dist/methods/Q/sanitize.js +142 -0
  61. package/dist/test.html +6 -0
  62. package/dist/tools/Q/lazyload.js +433 -0
  63. package/package.json +26 -0
@@ -0,0 +1,34 @@
1
+ Q.exports(function (Q) {
2
+ /**
3
+ * Q plugin's front end code
4
+ *
5
+ * @module Q
6
+ * @class Q
7
+ */
8
+
9
+ /**
10
+ * Traverse all the leaves and optionally modify the values
11
+ * @static
12
+ * @method leaves
13
+ * @param {Object|Array|mixed} structure
14
+ * @param {Function} callback This will be called for every leaf.
15
+ * It receives the current value of the leaf, and must return a value
16
+ * that will be set there (to skip changes, simply return the current value)
17
+ * @return {mixed} the first parameter passed, which was modified during the loop
18
+ */
19
+ Q.leaves = function Q_leaves(structure, callback) {
20
+ if (Q.isArrayLike(structure)) {
21
+ for (var i=0, l=structure.length; i<l; ++i) {
22
+ structure[i] = Q.leaves(structure[i], callback);
23
+ }
24
+ } else if (typeof structure === 'object') {
25
+ for (var k in structure) {
26
+ structure[k] = Q.leaves(structure[k], callback);
27
+ }
28
+ } else { // we found a scalar leaf
29
+ structure = callback(structure);
30
+ }
31
+ return structure;
32
+ };
33
+
34
+ });
File without changes
@@ -0,0 +1,142 @@
1
+ Q.exports(function (Q) {
2
+ /**
3
+ * Q plugin's front end code
4
+ *
5
+ * @module Q
6
+ * @class Q
7
+ */
8
+
9
+ /**
10
+ * Sanitizes a DOM element and removes potentially dangerous tags and attributes.
11
+ * Use this to clean user-provided HTML before inserting it into the DOM.
12
+ *
13
+ * @static
14
+ * @method sanitize
15
+ * @param {HTMLElement} container A DOM element (e.g. div) containing the HTML to sanitize.
16
+ * @param {Function} callback A function to call after sanitization is complete.
17
+ * It receives the sanitized container as its only argument.
18
+ * @param {Object} [options] Optional configuration:
19
+ * @param {Array} [options.allowedSrc] Allowed URL prefixes for `src` attributes.
20
+ * @param {Array} [options.allowedHref] Allowed URL prefixes for `href` attributes.
21
+ * @param {Array} [options.preserveAttributes] Attributes that should not be removed.
22
+ * @param {Array} [options.dangerousAttributes] Additional dangerous attributes to remove.
23
+ * @param {Boolean} [options.createShadowRoot] Whether this will be used inside a shadow DOM.
24
+ */
25
+ Q.sanitize = function Q_sanitize(container, callback, options) {
26
+ var dangerousTags = ['script', 'iframe', 'object', 'embed', 'svg', 'math', 'base', 'link'];
27
+ var allowedSrc = (options && options.allowedSrc) || ['data:image/', 'https://', 'http://'];
28
+ var allowedHref = (options && options.allowedHref) || [];
29
+ var preserveAttributes = (options && options.preserveAttributes) || [];
30
+ var customDangerousAttributes = (options && options.dangerousAttributes) || [];
31
+
32
+ var dangerousAttributes = [
33
+ 'srcdoc', 'sandbox', 'allowfullscreen', 'autofocus'
34
+ ].concat(customDangerousAttributes);
35
+
36
+ var all = container.querySelectorAll('*');
37
+
38
+ for (var i = 0; i < all.length; i++) {
39
+ var element = all[i];
40
+ var tagName = element.tagName.toLowerCase();
41
+ var attrs = element.attributes;
42
+
43
+ // Remove entire element if it's a dangerous tag
44
+ if (dangerousTags.includes(tagName)) {
45
+ if (element.parentNode) {
46
+ element.parentNode.removeChild(element);
47
+ }
48
+ continue;
49
+ }
50
+
51
+ for (var j = attrs.length - 1; j >= 0; j--) {
52
+ var attr = attrs[j];
53
+ var name = attr.name.toLowerCase();
54
+ var value = (attr.value || '').trim();
55
+ var shouldRemove = false;
56
+
57
+ // Remove event handler attributes dynamically
58
+ if (name.startsWith('on')) {
59
+ shouldRemove = true;
60
+ }
61
+
62
+ // Remove if explicitly in dangerous attributes
63
+ if (!shouldRemove && dangerousAttributes.includes(name)) {
64
+ shouldRemove = true;
65
+ }
66
+
67
+ // Remove javascript: URLs
68
+ if (!shouldRemove && value.toLowerCase().startsWith('javascript:')) {
69
+ shouldRemove = true;
70
+ }
71
+
72
+ // Remove suspicious data: URLs (except for images in src)
73
+ if (!shouldRemove && value.toLowerCase().startsWith('data:') && name !== 'src') {
74
+ var dataType = value.substring(5).split(';')[0].split(',')[0];
75
+ if (!dataType.startsWith('image/')) {
76
+ shouldRemove = true;
77
+ }
78
+ }
79
+
80
+ // Whitelist-based href handling
81
+ if (name === 'href') {
82
+ if (allowedHref.length === 0) {
83
+ shouldRemove = true;
84
+ } else {
85
+ var allowed = allowedHref.some(prefix => value.toLowerCase().startsWith(prefix));
86
+ if (!allowed) {
87
+ shouldRemove = true;
88
+ }
89
+ }
90
+ }
91
+
92
+ // Whitelist-based src handling
93
+ if (name === 'src') {
94
+ var allowed = allowedSrc.some(prefix => value.toLowerCase().startsWith(prefix));
95
+ if (!allowed) {
96
+ shouldRemove = true;
97
+ }
98
+ }
99
+
100
+ // Always remove form action
101
+ if (name === 'action') {
102
+ shouldRemove = true;
103
+ }
104
+
105
+ // Override with preserve list
106
+ if (preserveAttributes.includes(name)) {
107
+ shouldRemove = false;
108
+ }
109
+
110
+ if (shouldRemove) {
111
+ element.removeAttribute(attr.name);
112
+ }
113
+ }
114
+
115
+ // Shadow DOM safety for form elements
116
+ if (options && options.createShadowRoot) {
117
+ if (['form', 'input', 'textarea', 'select', 'button'].includes(tagName)) {
118
+ if (!element.hasAttribute('data-shadow-form')) {
119
+ element.setAttribute('data-shadow-form', 'true');
120
+ }
121
+ }
122
+ }
123
+ }
124
+
125
+ // Additional cleanup for shadow DOM: remove suspicious scripts from text content
126
+ if (options && options.createShadowRoot) {
127
+ var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, null, false);
128
+ var node;
129
+ while ((node = walker.nextNode())) {
130
+ var content = node.textContent;
131
+ if (content.toLowerCase().includes('<script') || content.toLowerCase().includes('javascript:')) {
132
+ node.textContent = content.replace(/<script[\s\S]*?<\/script>/gi, '')
133
+ .replace(/javascript:/gi, '');
134
+ }
135
+ }
136
+ }
137
+
138
+ if (typeof callback === 'function') {
139
+ callback(container);
140
+ }
141
+ };
142
+ });
package/dist/test.html ADDED
@@ -0,0 +1,6 @@
1
+ <html>
2
+ <body>
3
+ <script src="Q.min.js"></script>
4
+ <!-- <script src="Q.min.js"></script> -->
5
+ </body>
6
+ </html>
@@ -0,0 +1,433 @@
1
+ (function (Q, $) {
2
+ /**
3
+ * @module Q-tools
4
+ */
5
+
6
+ /**
7
+ * Implements lazy-loading for various types of elements.
8
+ * By default, has implementations for "img" and "Q_tool" selectors.
9
+ * The "Q_tool" handler only works with tools whose elements have a "data-q-lazyload" attribute .
10
+ * Note that the elements must have a stable, nonzero width height set in the CSS even
11
+ * if they are empty, otherwise they might not be lazy-loaded, and they might
12
+ * thrash back and forth if removed.
13
+ * @class Q lazyload
14
+ * @constructor
15
+ * @param {Object} [options] Override various options for this tool
16
+ * @param {Array} [options.handle] Set this to
17
+ * @param {Object} [options.handlers] You can modify the defaults by passing an object
18
+ * that looks like {handlerName: { selector: String, entering: Function, exiting: Function, preparing: Function}}
19
+ * Function "entering" receives (element, intersectionObserverEntry)
20
+ * Function "exiting" receives (element, intersectionObserverEntry)
21
+ * Function "preparing" receives (element) and is used the first time to prepare the element
22
+ * Both functions must return true if the element was modified.
23
+ * @param {Element} [options.root=tool.element] The container inside which to watch for intesections
24
+ * @param {Number} [options.waitUntilSlowerThan=1000] How many milliseconds to wait between entering events finally lazy-loading.
25
+ * Set to 0 if you want to load continuously while scrolling -- but this might cause jitter.
26
+ * @param {Object} [options.observerOptions] Override any options to pass to IntersectionObserver
27
+ * @param {Element} [options.observerOptions.root=tool.element.scrollingParent(true)]
28
+ * @param {String} [options.observerOptions.rootMargin='0px']
29
+ * @param {String} [options.observerOptions.threshold=0]
30
+ * @param {Boolean} [options.dontFreezeDimensions=false] Pass true to skip freezing dimensions when tools are removed
31
+ * @return {Q.Tool}
32
+ */
33
+ Q.Tool.define('Q/lazyload', function (options) {
34
+
35
+ var tool = this;
36
+ var state = this.state;
37
+ state.root = state.root || this.element;
38
+
39
+ tool.onEnteringStopped = new Q.Event();
40
+ tool.frozenDimensions = new WeakMap();
41
+
42
+ var Elp = Element.prototype;
43
+
44
+ Q.ensure('IntersectionObserver', function () {
45
+ // Observe whatever is on the page already
46
+ var p = tool.element.scrollingParent(true);
47
+ if (p === document.body) {
48
+ p = document.documentElement;
49
+ }
50
+ tool.observer = _createObserver(tool, p);
51
+ tool.observe(tool.prepare(tool.element, false));
52
+
53
+ // Add mutation observer to detect direct DOM removals
54
+ Q.ensure('MutationObserver', function () {
55
+ var removalObserver = new MutationObserver(function (mutations) {
56
+ for (var mutation of mutations) {
57
+ for (var node of mutation.removedNodes) {
58
+ if (!(node instanceof HTMLElement)) continue;
59
+ tool.unobserve([node]); // safety
60
+ forEachLazyElement(tool, node, function (element) {
61
+ for (var name in tool.state.handlers) {
62
+ var info = tool.state.handlers[name];
63
+ if (element.matches(info.selector)) {
64
+ if (element._Q_lazyload_exited
65
+ && element._Q_lazyload_exited[name]
66
+ ) {
67
+ continue;
68
+ }
69
+ element._Q_lazyload_exited = element._Q_lazyload_exited || {};
70
+ element._Q_lazyload_exited[name] = true;
71
+ info.exiting && info.exiting.call(tool, element);
72
+ }
73
+ }
74
+ });
75
+ }
76
+ }
77
+ });
78
+ removalObserver.observe(state.root || document.body, {
79
+ childList: true,
80
+ subtree: true
81
+ });
82
+ });
83
+
84
+ // Override innerHTML
85
+
86
+ var originalSet = Object.getOwnPropertyDescriptor(Elp, 'innerHTML').set;
87
+ var originalGet = Object.getOwnPropertyDescriptor(Elp, 'innerHTML').get;
88
+
89
+ Object.defineProperty(Elp, 'innerHTML', {
90
+ set: function (html) {
91
+ var element = document.createElement('div');
92
+ var root = state.root || document.documentElement;
93
+ var inside = (root === this) || root.contains(this);
94
+ if (!inside) {
95
+ originalSet.call(this, html);
96
+ return html;
97
+ }
98
+ originalSet.call(element, html);
99
+ var found = false;
100
+ Q.each(state.handlers, function (name, info) {
101
+ var elements = element.querySelectorAll
102
+ ? Array.from(element.querySelectorAll(info.selector))
103
+ : [];
104
+ if (elements.length) {
105
+ found = true;
106
+ return false;
107
+ }
108
+ });
109
+ if (found) {
110
+ // prepare all images
111
+ tool.prepare(element, true);
112
+ }
113
+ originalSet.call(this, originalGet.call(element));
114
+ tool.observe(tool.prepare(this, true));
115
+ return html;
116
+ },
117
+ get: originalGet
118
+ });
119
+
120
+ Q.each(['insertBefore', 'appendChild', 'append', 'prepend'], function (i, fn) {
121
+ var orig = Elp[fn];
122
+ Elp[fn] = function smartInsert(element) {
123
+ if (element instanceof DocumentFragment) {
124
+ var children = Array.from(element.children);
125
+ for (var i=0, l=children.length; i<l; i++) {
126
+ smartInsert.apply(this, [children[i], arguments[1]]);
127
+ }
128
+ return;
129
+ }
130
+ if (!(element instanceof HTMLElement)
131
+ || Q.replace.lazyloadDontPrepare) {
132
+ return orig.apply(this, arguments);
133
+ }
134
+ var root = state.root || document.documentElement;
135
+ var inside = (root === this) || root.contains(this);
136
+ if (!inside) {
137
+ return orig.apply(this, arguments);
138
+ }
139
+ var found = false;
140
+ Q.each(state.handlers, function (name, info) {
141
+ if (element.matches && element.matches(info.selector)) {
142
+ found = true;
143
+ return false;
144
+ }
145
+ var elements = element.querySelectorAll
146
+ ? Array.from(element.querySelectorAll(info.selector))
147
+ : [];
148
+ if (elements.length) {
149
+ found = true;
150
+ return false;
151
+ }
152
+ });
153
+ if (found) {
154
+ tool.observe(tool.prepare(element, true));
155
+ }
156
+ return orig.apply(this, arguments);
157
+ };
158
+ });
159
+ });
160
+
161
+ },
162
+
163
+ {
164
+ handlers: {
165
+ img: {
166
+ selector: 'img',
167
+ entering: function (img, entry) {
168
+ var tool = this;
169
+ if (Q.Visual.intersection(img, tool.state.root)) {
170
+ return _load();
171
+ }
172
+ tool.timeout && clearTimeout(tool.timeout);
173
+ tool.timeout = setTimeout(function () {
174
+ Q.handle(tool.onEnteringStopped, tool);
175
+ }, tool.state.waitUntilSlowerThan);
176
+ tool.onEnteringStopped.setOnce(_load);
177
+ return true;
178
+
179
+ function _load() {
180
+ var src = img.getAttribute('data-lazyload-src');
181
+ if (!src) {
182
+ return;
183
+ }
184
+ img.setAttribute('src', Q.url(src));
185
+ img.removeAttribute('data-lazyload-src');
186
+ setTimeout(function () {
187
+ if (!_loadedImages[src]
188
+ && !img.complete) {
189
+ img.addClass('Q_lazy_load');
190
+ }
191
+ _loadedImages[src] = true;
192
+ if (img.complete) {
193
+ _loaded();
194
+ }
195
+ img.addEventListener('load', _loaded);
196
+ }, 0);
197
+
198
+ function _loaded() {
199
+ img.addClass('Q_lazy_loaded');
200
+ }
201
+ }
202
+ },
203
+ exiting: function (img) {
204
+ return true; // no need to do anything else
205
+ },
206
+ preparing: function (img, beingInsertedIntoDOM) {
207
+ if (!beingInsertedIntoDOM) {
208
+ return true; // too late anyway, browser will load image
209
+ }
210
+ if (img.hasClass('Q_lazy_load')
211
+ || img.hasClass('Q_lazy_loaded')
212
+ || img.hasClass('Q_no_lazyload')) {
213
+ return true; // this was already processed by lazy-loading
214
+ }
215
+ var src = img.getAttribute('src');
216
+ if (src && src.substring(0, 5) !== 'data:'
217
+ && !img.hasAttribute('data-lazyload-src')) {
218
+ img.setAttribute('data-lazyload-src', Q.url(src));
219
+ img.setAttribute('src', Q.url(
220
+ Q.getObject('Q.images.lazyload.loadingSrc')
221
+ || "{{Q}}/img/throbbers/transparent.gif"
222
+ ));
223
+ }
224
+ return true;
225
+ }
226
+ },
227
+ tool: {
228
+ selector: '.Q_tool',
229
+ entering: function (element, entry) {
230
+ var tool = this;
231
+ if (Q.Visual.intersection(element, tool.state.root)) {
232
+ return _activate();
233
+ }
234
+ tool.timeout && clearTimeout(tool.timeout);
235
+ tool.timeout = setTimeout(function () {
236
+ Q.handle(tool.onEnteringStopped, tool);
237
+ }, tool.state.waitUntilSlowerThan);
238
+ tool.onEnteringStopped.setOnce(_activate);
239
+ return true;
240
+
241
+ function _activate() {
242
+ var ep = tool.frozenDimensions.get(element);
243
+ var c = element.parentElement;
244
+ var unfreeze = false;
245
+ if (!ep || !c) {
246
+ // element didn't exit before, so its dimensions weren't frozen
247
+ } else if (!tool.state.dontFreezeDimensions) {
248
+ var r = c.getBoundingClientRect();
249
+ if (!ep.containerRect || ep.containerRect.width !== r.width) {
250
+ // container width was resized, so throw away the frozen dimensions
251
+ // because a reflow should happen anyway
252
+ tool.frozenDimensions.delete(element);
253
+ element.removeClass('Q_frozen_dimensions');
254
+ } else {
255
+ // inform tools that their element has frozen dimensions,
256
+ // so the tools may want to revert the frozen dimensions
257
+ element.addClass('Q_frozen_dimensions');
258
+ element.setAttribute('data-Q-frozenDimensions', JSON.stringify(ep));
259
+ unfreeze = true;
260
+ }
261
+ }
262
+ if (element.hasAttribute('data-q-lazyload')
263
+ && (!element.Q || !element.Q.tool)) {
264
+ element.addClass('Q_lazy_load');
265
+ element.setAttribute('data-q-lazyload', 'activating');
266
+ Q.activate(element, {}, function () {
267
+ element.setAttribute('data-q-lazyload', 'activated');
268
+ element.addClass('Q_lazy_loaded');
269
+ if (unfreeze) {
270
+ tool.unfreezeDimensions(element);
271
+ }
272
+ }, {lazyload: true});
273
+ }
274
+ }
275
+ },
276
+ exiting: function (element, entry) {
277
+ var tool = this;
278
+ if (element.hasAttribute('data-q-lazyload')
279
+ && element.Q.tool) {
280
+ // Take a snapshot of the current width and height of the element,
281
+ // to restore it when it's later inserted back into the container,
282
+ // and prevent all the elements shifting. However, if the container
283
+ // itself is resizing, then we will remove this snapshot since things
284
+ // will shift anyway.
285
+ var cs = element.computedStyle();
286
+ tool.frozenDimensions.set(element, {
287
+ width: element.style.width,
288
+ height: element.style.height,
289
+ containerRect: element.parentElement.getBoundingClientRect()
290
+ });
291
+ if (!tool.state.dontFreezeDimensions) {
292
+ element.style.width = cs.width;
293
+ element.style.height = cs.height;
294
+ }
295
+ Q.Tool.remove(element);
296
+ element.removeClass('Q_lazy_loading');
297
+ element.removeClass('Q_lazy_loaded');
298
+ element.setAttribute('data-q-lazyload', 'removed');
299
+ if (this.state.handlers.tool.exitingRemoveHTML) {
300
+ element.innerHTML = '';
301
+ }
302
+ }
303
+ return true;
304
+ },
305
+ preparing: function (element) {
306
+ return true;
307
+ },
308
+ exitingRemoveHTML: true
309
+ }
310
+ },
311
+ root: undefined,
312
+ observerOptions: {
313
+ root: undefined,
314
+ rootMargin: '0px',
315
+ threshold: 0
316
+ },
317
+ waitUntilSlowerThan: 1000,
318
+ dontFreezeDimensions: false
319
+ },
320
+
321
+ {
322
+ prepare: function (container, beingInsertedIntoDOM) {
323
+ var tool = this;
324
+ var found = [];
325
+ Q.each(tool.state.handlers, function (name, info) {
326
+ var elements = container.querySelectorAll
327
+ ? Array.from(container.querySelectorAll(info.selector))
328
+ : [];
329
+ if (container.matches && container.matches(info.selector)) {
330
+ elements.push(container);
331
+ }
332
+ Q.each(elements, function (i, element) {
333
+ let ancestor = element.closest('[data-Q-retain]');
334
+ if (ancestor && Q.replace.retainedElements[ancestor.id]) {
335
+ return;
336
+ }
337
+ if (info.preparing.call(tool, element, beingInsertedIntoDOM) === true) {
338
+ found.push(element);
339
+ }
340
+ });
341
+ });
342
+ return found;
343
+ },
344
+ observe: function (elements) {
345
+ var tool = this;
346
+ tool.observer && Q.each(elements, function (i, element) {
347
+ tool.observer.observe(element);
348
+ });
349
+ },
350
+ unobserve: function (elements) {
351
+ var tool = this;
352
+ tool.observer && Q.each(elements, function (i, element) {
353
+ tool.observer.unobserve(element);
354
+ });
355
+ },
356
+ unfreezeDimensions: function(element) {
357
+ var ep = this.frozenDimensions.get(element);
358
+ if (!ep) {
359
+ return false;
360
+ }
361
+ if (ep.width) {
362
+ element.style.width = ep.width;
363
+ } else {
364
+ element.style.removeProperty('width');
365
+ }
366
+ if (ep.height) {
367
+ element.style.height = ep.height;
368
+ } else {
369
+ element.style.removeProperty('height');
370
+ }
371
+ },
372
+ Q: {
373
+ beforeRemove: function () {
374
+ this.observer && this.observer.disconnect();
375
+ }
376
+ }
377
+ });
378
+
379
+ function _createObserver(tool, container) {
380
+ var o = Q.copy(tool.state.observerOptions);
381
+ if (o.root === undefined) {
382
+ o.root = (container === document.documentElement) ? null : (container || null);
383
+ }
384
+ return new IntersectionObserver(function (entries, observer) {
385
+ Q.each(entries, function (i, entry) {
386
+ Q.each(tool.state.handlers, function (name, info) {
387
+ if (Q.replace.retainedElements[entry.target.id]) {
388
+ // next time around, we'll process this element
389
+ delete Q.replace.retainedElements[entry.target.id];
390
+ return;
391
+ }
392
+ if (entry.target.matches && entry.target.matches(info.selector)) {
393
+ if (entry.isIntersecting) {
394
+ if (entry.target._Q_lazyload_exited) {
395
+ delete entry.target._Q_lazyload_exited[name];
396
+ }
397
+ info.entering.call(tool, entry.target, entry);
398
+ } else {
399
+ var rect = entry.target.getBoundingClientRect();
400
+ if (rect.width > 0 && rect.height > 0) {
401
+ // also covers !document.body.contains(entry.target)
402
+ info.exiting.call(tool, entry.target, entry);
403
+ } // otherwise it might get a false positive
404
+ }
405
+ }
406
+ });
407
+ });
408
+ }, o);
409
+ }
410
+
411
+ Q.lazyload = Q.lazyload || {};
412
+ var _loadedImages = Q.lazyload.loadedImages = {};
413
+
414
+ function forEachLazyElement(tool, container, callback) {
415
+ for (var name in tool.state.handlers || {}) {
416
+ var handler = tool.state.handlers[name];
417
+ if (!handler || !handler.selector) {
418
+ continue;
419
+ }
420
+ if (!(container instanceof HTMLElement)) {
421
+ continue;
422
+ }
423
+ var elements = container.querySelectorAll(handler.selector) || [];
424
+ if (container.matches(handler.selector)) {
425
+ callback(container);
426
+ }
427
+ for (var i=0; i<elements.length; ++i) {
428
+ callback(elements[i]);
429
+ }
430
+ }
431
+ }
432
+
433
+ })(Q, Q.jQuery);
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@qbix/q",
3
+ "version": "1.0.5",
4
+ "description": "Q.js, the Qbix JavaScript framework",
5
+ "main": "dist/Q.min.js",
6
+ "files": [
7
+ "dist/"
8
+ ],
9
+ "author": "Qbix Inc.",
10
+ "license": "MIT",
11
+ "scripts": {
12
+ "test": "node test/metrics.test.js"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/Qbix/Q.js.git"
17
+ },
18
+ "keywords": [],
19
+ "bugs": {
20
+ "url": "https://github.com/Qbix/Q.js/issues"
21
+ },
22
+ "homepage": "https://github.com/Qbix/Q.js#readme",
23
+ "devDependencies": {
24
+ "jsdom": "^30.0.1"
25
+ }
26
+ }