futurism 1.2.0.pre9 → 1.2.0.pre10

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +24 -0
  3. data/Appraisals~ +24 -0
  4. data/CHANGELOG.md +370 -0
  5. data/Gemfile +15 -0
  6. data/Gemfile.lock +215 -0
  7. data/Gemfile~ +17 -0
  8. data/README.md +44 -9
  9. data/README.md~ +405 -0
  10. data/app/assets/javascripts/futurism.js +191 -0
  11. data/app/assets/javascripts/futurism.min.js +2 -0
  12. data/app/assets/javascripts/futurism.min.js.map +1 -0
  13. data/app/assets/javascripts/futurism.umd.js +188 -0
  14. data/app/assets/javascripts/futurism.umd.min.js +2 -0
  15. data/app/assets/javascripts/futurism.umd.min.js.map +1 -0
  16. data/{lib → app/channels}/futurism/channel.rb +1 -1
  17. data/bin/rails +25 -0
  18. data/bin/standardize +5 -0
  19. data/bin/test +5 -0
  20. data/futurism.gemspec +38 -0
  21. data/futurism.gemspec~ +30 -0
  22. data/lib/futurism/configuration.rb +21 -0
  23. data/lib/futurism/engine.rb +19 -0
  24. data/lib/futurism/helpers.rb +1 -1
  25. data/lib/futurism/importmap.rb +2 -0
  26. data/lib/futurism/version.rb +1 -1
  27. data/lib/futurism.rb +2 -1
  28. data/package.json +47 -0
  29. data/package.json~ +51 -0
  30. data/rollup.config.js +77 -0
  31. data/rollup.config.js~ +73 -0
  32. data/test/cable/channel_test.rb +319 -0
  33. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  34. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  35. data/test/dummy/app/controllers/application_controller.rb +2 -0
  36. data/test/dummy/app/controllers/home_controller.rb +6 -0
  37. data/test/dummy/app/controllers/posts_controller.rb +59 -0
  38. data/test/dummy/app/helpers/application_helper.rb +2 -0
  39. data/test/dummy/app/helpers/posts_helper.rb +2 -0
  40. data/test/dummy/app/jobs/application_job.rb +7 -0
  41. data/test/dummy/app/models/action_item.rb +2 -0
  42. data/test/dummy/app/models/application_record.rb +3 -0
  43. data/test/dummy/app/models/post.rb +2 -0
  44. data/test/dummy/config/application.rb +29 -0
  45. data/test/dummy/config/boot.rb +5 -0
  46. data/test/dummy/config/environment.rb +5 -0
  47. data/test/dummy/config/environments/development.rb +40 -0
  48. data/test/dummy/config/environments/production.rb +94 -0
  49. data/test/dummy/config/environments/test.rb +39 -0
  50. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  51. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/test/dummy/config/initializers/content_security_policy.rb +28 -0
  53. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  54. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  55. data/test/dummy/config/initializers/inflections.rb +16 -0
  56. data/test/dummy/config/initializers/mime_types.rb +4 -0
  57. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  58. data/test/dummy/config/puma.rb +38 -0
  59. data/test/dummy/config/routes.rb +12 -0
  60. data/test/dummy/config/spring.rb +6 -0
  61. data/test/dummy/db/migrate/20200711122838_create_posts.rb +9 -0
  62. data/test/dummy/db/migrate/2021042923813_create_action_items.rb +9 -0
  63. data/test/dummy/db/schema.rb +27 -0
  64. data/test/futurism_test.rb +28 -0
  65. data/test/helper/helper_test.rb +206 -0
  66. data/test/integration/navigation_test.rb +7 -0
  67. data/test/resolver/controller/renderer_test.rb +120 -0
  68. data/test/resolver/controller_test.rb +26 -0
  69. data/test/test_helper.rb +14 -0
  70. data/yarn.lock +3263 -0
  71. metadata +122 -18
  72. data/config/routes.rb +0 -2
  73. data/lib/futurism/channel.rb~ +0 -31
  74. data/lib/futurism/helpers.rb~ +0 -112
  75. data/lib/futurism/options_transformer.rb~ +0 -0
  76. data/lib/futurism/resolver/controller/renderer.rb~ +0 -76
  77. data/lib/futurism/resolver/controller.rb~ +0 -17
  78. data/lib/futurism/resolver/resources.rb~ +0 -101
  79. data/lib/futurism/version.rb~ +0 -3
  80. data/lib/futurism.rb~ +0 -30
  81. data/lib/tasks/futurism_tasks.rake +0 -39
  82. data/lib/tasks/futurism_tasks.rake~ +0 -39
@@ -0,0 +1,191 @@
1
+ import CableReady from "cable_ready";
2
+
3
+ const debounceEvents = (callback, delay = 20) => {
4
+ let timeoutId;
5
+ let events = [];
6
+ return (...args) => {
7
+ clearTimeout(timeoutId);
8
+ events = [ ...events, ...args ];
9
+ timeoutId = setTimeout((() => {
10
+ timeoutId = null;
11
+ callback(events);
12
+ events = [];
13
+ }), delay);
14
+ };
15
+ };
16
+
17
+ const createSubscription = consumer => {
18
+ consumer.subscriptions.create("Futurism::Channel", {
19
+ connected() {
20
+ window.Futurism = this;
21
+ document.addEventListener("futurism:appear", debounceEvents((events => {
22
+ this.send({
23
+ signed_params: events.map((e => e.target.dataset.signedParams)),
24
+ sgids: events.map((e => e.target.dataset.sgid)),
25
+ signed_controllers: events.map((e => e.target.dataset.signedController)),
26
+ urls: events.map((_ => window.location.href)),
27
+ broadcast_each: events.map((e => e.target.dataset.broadcastEach))
28
+ });
29
+ })));
30
+ },
31
+ received(data) {
32
+ if (data.cableReady) {
33
+ CableReady.perform(data.operations, {
34
+ emitMissingElementWarnings: false
35
+ });
36
+ document.dispatchEvent(new CustomEvent("futurism:appeared", {
37
+ bubbles: true,
38
+ cancelable: true
39
+ }));
40
+ }
41
+ }
42
+ });
43
+ };
44
+
45
+ const dispatchAppearEvent = (entry, observer = null) => {
46
+ if (!window.Futurism) {
47
+ return () => {
48
+ setTimeout((() => dispatchAppearEvent(entry, observer)()), 1);
49
+ };
50
+ }
51
+ const target = entry.target ? entry.target : entry;
52
+ const evt = new CustomEvent("futurism:appear", {
53
+ bubbles: true,
54
+ detail: {
55
+ target: target,
56
+ observer: observer
57
+ }
58
+ });
59
+ return () => {
60
+ target.dispatchEvent(evt);
61
+ };
62
+ };
63
+
64
+ const wait = ms => new Promise((resolve => setTimeout(resolve, ms)));
65
+
66
+ const callWithRetry = async (fn, depth = 0) => {
67
+ try {
68
+ return await fn();
69
+ } catch (e) {
70
+ if (depth > 10) {
71
+ throw e;
72
+ }
73
+ await wait(1.15 ** depth * 2e3);
74
+ return callWithRetry(fn, depth + 1);
75
+ }
76
+ };
77
+
78
+ const observerCallback = (entries, observer) => {
79
+ entries.forEach((async entry => {
80
+ if (!entry.isIntersecting) return;
81
+ await callWithRetry(dispatchAppearEvent(entry, observer));
82
+ }));
83
+ };
84
+
85
+ const extendElementWithIntersectionObserver = element => {
86
+ Object.assign(element, {
87
+ observer: new IntersectionObserver(observerCallback.bind(element), {})
88
+ });
89
+ if (!element.hasAttribute("keep")) {
90
+ element.observer.observe(element);
91
+ }
92
+ };
93
+
94
+ const extendElementWithEagerLoading = element => {
95
+ if (element.dataset.eager === "true") {
96
+ if (element.observer) element.observer.disconnect();
97
+ callWithRetry(dispatchAppearEvent(element));
98
+ }
99
+ };
100
+
101
+ class FuturismElement extends HTMLElement {
102
+ connectedCallback() {
103
+ extendElementWithIntersectionObserver(this);
104
+ extendElementWithEagerLoading(this);
105
+ }
106
+ }
107
+
108
+ class FuturismTableRow extends HTMLTableRowElement {
109
+ connectedCallback() {
110
+ extendElementWithIntersectionObserver(this);
111
+ extendElementWithEagerLoading(this);
112
+ }
113
+ }
114
+
115
+ class FuturismLI extends HTMLLIElement {
116
+ connectedCallback() {
117
+ extendElementWithIntersectionObserver(this);
118
+ extendElementWithEagerLoading(this);
119
+ }
120
+ }
121
+
122
+ async function sha256(message) {
123
+ const msgBuffer = new TextEncoder("utf-8").encode(message);
124
+ const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
125
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
126
+ const hashHex = hashArray.map((b => ("00" + b.toString(16)).slice(-2))).join("");
127
+ return hashHex;
128
+ }
129
+
130
+ const polyfillCustomElements = () => {
131
+ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
132
+ if (customElements) {
133
+ if (isSafari) {
134
+ document.write('<script src="//unpkg.com/@ungap/custom-elements-builtin"><\/script>');
135
+ } else {
136
+ try {
137
+ customElements.define("built-in", document.createElement("tr").constructor, {
138
+ extends: "tr"
139
+ });
140
+ } catch (_) {
141
+ document.write('<script src="//unpkg.com/@ungap/custom-elements-builtin"><\/script>');
142
+ }
143
+ }
144
+ } else {
145
+ document.write('<script src="//unpkg.com/document-register-element"><\/script>');
146
+ }
147
+ };
148
+
149
+ const defineElements = e => {
150
+ if (!customElements.get("futurism-element")) {
151
+ customElements.define("futurism-element", FuturismElement);
152
+ customElements.define("futurism-table-row", FuturismTableRow, {
153
+ extends: "tr"
154
+ });
155
+ customElements.define("futurism-li", FuturismLI, {
156
+ extends: "li"
157
+ });
158
+ }
159
+ };
160
+
161
+ const cachePlaceholders = e => {
162
+ sha256(e.detail.element.outerHTML).then((hashedContent => {
163
+ e.detail.element.setAttribute("keep", "");
164
+ sessionStorage.setItem(`futurism-${hashedContent}`, e.detail.element.outerHTML);
165
+ e.target.dataset.futurismHash = hashedContent;
166
+ }));
167
+ };
168
+
169
+ const restorePlaceholders = e => {
170
+ const inNamespace = ([key, _payload]) => key.startsWith("futurism-");
171
+ Object.entries(sessionStorage).filter(inNamespace).forEach((([key, payload]) => {
172
+ const match = /^futurism-(.*)/.exec(key);
173
+ const targetElement = document.querySelector(`[data-futurism-hash="${match[1]}"]`);
174
+ if (targetElement) {
175
+ targetElement.outerHTML = payload;
176
+ sessionStorage.removeItem(key);
177
+ }
178
+ }));
179
+ };
180
+
181
+ const initializeElements = () => {
182
+ polyfillCustomElements();
183
+ document.addEventListener("DOMContentLoaded", defineElements);
184
+ document.addEventListener("turbo:load", defineElements);
185
+ document.addEventListener("turbo:before-cache", restorePlaceholders);
186
+ document.addEventListener("turbolinks:load", defineElements);
187
+ document.addEventListener("turbolinks:before-cache", restorePlaceholders);
188
+ document.addEventListener("cable-ready:after-outer-html", cachePlaceholders);
189
+ };
190
+
191
+ export { createSubscription, initializeElements };
@@ -0,0 +1,2 @@
1
+ import e from"cable_ready";const t=t=>{t.subscriptions.create("Futurism::Channel",{connected(){window.Futurism=this,document.addEventListener("futurism:appear",((e,t=20)=>{let s,r=[];return(...n)=>{clearTimeout(s),r=[...r,...n],s=setTimeout((()=>{s=null,e(r),r=[]}),t)}})((e=>{this.send({signed_params:e.map((e=>e.target.dataset.signedParams)),sgids:e.map((e=>e.target.dataset.sgid)),signed_controllers:e.map((e=>e.target.dataset.signedController)),urls:e.map((e=>window.location.href)),broadcast_each:e.map((e=>e.target.dataset.broadcastEach))})})))},received(t){t.cableReady&&(e.perform(t.operations,{emitMissingElementWarnings:!1}),document.dispatchEvent(new CustomEvent("futurism:appeared",{bubbles:!0,cancelable:!0})))}})},s=(e,t=null)=>{if(!window.Futurism)return()=>{setTimeout((()=>s(e,t)()),1)};const r=e.target?e.target:e,n=new CustomEvent("futurism:appear",{bubbles:!0,detail:{target:r,observer:t}});return()=>{r.dispatchEvent(n)}},r=async(e,t=0)=>{try{return await e()}catch(n){if(t>10)throw n;return await(s=1.15**t*2e3,new Promise((e=>setTimeout(e,s)))),r(e,t+1)}var s},n=(e,t)=>{e.forEach((async e=>{e.isIntersecting&&await r(s(e,t))}))},a=e=>{Object.assign(e,{observer:new IntersectionObserver(n.bind(e),{})}),e.hasAttribute("keep")||e.observer.observe(e)},i=e=>{"true"===e.dataset.eager&&(e.observer&&e.observer.disconnect(),r(s(e)))};class o extends HTMLElement{connectedCallback(){a(this),i(this)}}class c extends HTMLTableRowElement{connectedCallback(){a(this),i(this)}}class u extends HTMLLIElement{connectedCallback(){a(this),i(this)}}const m=e=>{customElements.get("futurism-element")||(customElements.define("futurism-element",o),customElements.define("futurism-table-row",c,{extends:"tr"}),customElements.define("futurism-li",u,{extends:"li"}))},d=e=>{(async function(e){const t=new TextEncoder("utf-8").encode(e),s=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(s)).map((e=>("00"+e.toString(16)).slice(-2))).join("")})(e.detail.element.outerHTML).then((t=>{e.detail.element.setAttribute("keep",""),sessionStorage.setItem(`futurism-${t}`,e.detail.element.outerHTML),e.target.dataset.futurismHash=t}))},l=e=>{Object.entries(sessionStorage).filter((([e,t])=>e.startsWith("futurism-"))).forEach((([e,t])=>{const s=/^futurism-(.*)/.exec(e),r=document.querySelector(`[data-futurism-hash="${s[1]}"]`);r&&(r.outerHTML=t,sessionStorage.removeItem(e))}))},b=()=>{(()=>{const e=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);if(customElements)if(e)document.write('<script src="//unpkg.com/@ungap/custom-elements-builtin"><\/script>');else try{customElements.define("built-in",document.createElement("tr").constructor,{extends:"tr"})}catch(e){document.write('<script src="//unpkg.com/@ungap/custom-elements-builtin"><\/script>')}else document.write('<script src="//unpkg.com/document-register-element"><\/script>')})(),document.addEventListener("DOMContentLoaded",m),document.addEventListener("turbo:load",m),document.addEventListener("turbo:before-cache",l),document.addEventListener("turbolinks:load",m),document.addEventListener("turbolinks:before-cache",l),document.addEventListener("cable-ready:after-outer-html",d)};export{t as createSubscription,b as initializeElements};
2
+ //# sourceMappingURL=futurism.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"futurism.min.js","sources":["../../../javascript/futurism_channel.js","../../../javascript/elements/futurism_utils.js","../../../javascript/elements/futurism_element.js","../../../javascript/elements/futurism_table_row.js","../../../javascript/elements/futurism_li.js","../../../javascript/elements/index.js","../../../javascript/utils/crypto.js"],"sourcesContent":["/* global CustomEvent, setTimeout */\n\nimport CableReady from 'cable_ready'\n\nconst debounceEvents = (callback, delay = 20) => {\n let timeoutId\n let events = []\n return (...args) => {\n clearTimeout(timeoutId)\n events = [...events, ...args]\n timeoutId = setTimeout(() => {\n timeoutId = null\n callback(events)\n events = []\n }, delay)\n }\n}\n\nexport const createSubscription = consumer => {\n consumer.subscriptions.create('Futurism::Channel', {\n connected () {\n window.Futurism = this\n document.addEventListener(\n 'futurism:appear',\n debounceEvents(events => {\n this.send({\n signed_params: events.map(e => e.target.dataset.signedParams),\n sgids: events.map(e => e.target.dataset.sgid),\n signed_controllers: events.map(\n e => e.target.dataset.signedController\n ),\n urls: events.map(_ => window.location.href),\n broadcast_each: events.map(e => e.target.dataset.broadcastEach)\n })\n })\n )\n },\n\n received (data) {\n if (data.cableReady) {\n CableReady.perform(data.operations, {\n emitMissingElementWarnings: false\n })\n\n document.dispatchEvent(\n new CustomEvent('futurism:appeared', {\n bubbles: true,\n cancelable: true\n })\n )\n }\n }\n })\n}\n","/* global IntersectionObserver, CustomEvent, setTimeout */\n\nconst dispatchAppearEvent = (entry, observer = null) => {\n if (!window.Futurism) {\n return () => {\n setTimeout(() => dispatchAppearEvent(entry, observer)(), 1)\n }\n }\n\n const target = entry.target ? entry.target : entry\n\n const evt = new CustomEvent('futurism:appear', {\n bubbles: true,\n detail: {\n target,\n observer\n }\n })\n\n return () => {\n target.dispatchEvent(evt)\n }\n}\n\n// from https://advancedweb.hu/how-to-implement-an-exponential-backoff-retry-strategy-in-javascript/#rejection-based-retrying\nconst wait = ms => new Promise(resolve => setTimeout(resolve, ms))\n\nconst callWithRetry = async (fn, depth = 0) => {\n try {\n return await fn()\n } catch (e) {\n if (depth > 10) {\n throw e\n }\n await wait(1.15 ** depth * 2000)\n\n return callWithRetry(fn, depth + 1)\n }\n}\n\nconst observerCallback = (entries, observer) => {\n entries.forEach(async entry => {\n if (!entry.isIntersecting) return\n\n await callWithRetry(dispatchAppearEvent(entry, observer))\n })\n}\n\nexport const extendElementWithIntersectionObserver = element => {\n Object.assign(element, {\n observer: new IntersectionObserver(observerCallback.bind(element), {})\n })\n\n if (!element.hasAttribute('keep')) {\n element.observer.observe(element)\n }\n}\n\nexport const extendElementWithEagerLoading = element => {\n if (element.dataset.eager === 'true') {\n if (element.observer) element.observer.disconnect()\n callWithRetry(dispatchAppearEvent(element))\n }\n}\n","/* global HTMLElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismElement extends HTMLElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLTableRowElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismTableRow extends HTMLTableRowElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLLIElement */\r\n\r\nimport {\r\n extendElementWithIntersectionObserver,\r\n extendElementWithEagerLoading\r\n} from './futurism_utils'\r\n\r\nexport default class FuturismLI extends HTMLLIElement {\r\n connectedCallback () {\r\n extendElementWithIntersectionObserver(this)\r\n extendElementWithEagerLoading(this)\r\n }\r\n}\r\n","/* global customElements, sessionStorage */\n\nimport FuturismElement from './futurism_element'\nimport FuturismTableRow from './futurism_table_row'\nimport FuturismLI from './futurism_li'\n\nimport { sha256 } from '../utils/crypto'\n\nconst polyfillCustomElements = () => {\n const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)\n\n if (customElements) {\n if (isSafari) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n } else {\n try {\n customElements.define(\n 'built-in',\n document.createElement('tr').constructor,\n { extends: 'tr' }\n )\n } catch (_) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n }\n }\n } else {\n document.write(\n '<script src=\"//unpkg.com/document-register-element\"><\\x2fscript>'\n )\n }\n}\n\nconst defineElements = e => {\n if (!customElements.get('futurism-element')) {\n customElements.define('futurism-element', FuturismElement)\n customElements.define('futurism-table-row', FuturismTableRow, {\n extends: 'tr'\n })\n customElements.define('futurism-li', FuturismLI, { extends: 'li' })\n }\n}\n\nconst cachePlaceholders = e => {\n sha256(e.detail.element.outerHTML).then(hashedContent => {\n e.detail.element.setAttribute('keep', '')\n sessionStorage.setItem(\n `futurism-${hashedContent}`,\n e.detail.element.outerHTML\n )\n e.target.dataset.futurismHash = hashedContent\n })\n}\n\nconst restorePlaceholders = e => {\n const inNamespace = ([key, _payload]) => key.startsWith('futurism-')\n Object.entries(sessionStorage)\n .filter(inNamespace)\n .forEach(([key, payload]) => {\n const match = /^futurism-(.*)/.exec(key)\n const targetElement = document.querySelector(\n `[data-futurism-hash=\"${match[1]}\"]`\n )\n\n if (targetElement) {\n targetElement.outerHTML = payload\n sessionStorage.removeItem(key)\n }\n })\n}\n\nexport const initializeElements = () => {\n polyfillCustomElements()\n document.addEventListener('DOMContentLoaded', defineElements)\n document.addEventListener('turbo:load', defineElements)\n document.addEventListener('turbo:before-cache', restorePlaceholders)\n document.addEventListener('turbolinks:load', defineElements)\n document.addEventListener('turbolinks:before-cache', restorePlaceholders)\n document.addEventListener('cable-ready:after-outer-html', cachePlaceholders)\n}\n","/* global crypto */\n\nexport async function sha256 (message) {\n // encode as UTF-8\n const msgBuffer = new TextEncoder('utf-8').encode(message)\n\n // hash the message\n const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer)\n\n // convert ArrayBuffer to Array\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n\n // convert bytes to hex string\n const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('')\n\n return hashHex\n}\n"],"names":["createSubscription","consumer","subscriptions","create","connected","window","Futurism","this","document","addEventListener","callback","delay","timeoutId","events","args","clearTimeout","setTimeout","debounceEvents","send","signed_params","map","e","target","dataset","signedParams","sgids","sgid","signed_controllers","signedController","urls","_","location","href","broadcast_each","broadcastEach","received","data","cableReady","CableReady","perform","operations","emitMissingElementWarnings","dispatchEvent","CustomEvent","bubbles","cancelable","dispatchAppearEvent","entry","observer","evt","detail","callWithRetry","async","fn","depth","ms","Promise","resolve","observerCallback","entries","forEach","isIntersecting","extendElementWithIntersectionObserver","element","Object","assign","IntersectionObserver","bind","hasAttribute","observe","extendElementWithEagerLoading","eager","disconnect","FuturismElement","HTMLElement","connectedCallback","FuturismTableRow","HTMLTableRowElement","FuturismLI","HTMLLIElement","defineElements","customElements","get","define","extends","cachePlaceholders","message","msgBuffer","TextEncoder","encode","hashBuffer","crypto","subtle","digest","Array","from","Uint8Array","b","toString","slice","join","sha256","outerHTML","then","hashedContent","setAttribute","sessionStorage","setItem","futurismHash","restorePlaceholders","filter","key","_payload","startsWith","payload","match","exec","targetElement","querySelector","removeItem","initializeElements","isSafari","test","navigator","userAgent","write","createElement","constructor","polyfillCustomElements"],"mappings":"2BAIA,MAcaA,EAAqBC,IAChCA,EAASC,cAAcC,OAAO,oBAAqB,CACjDC,YACEC,OAAOC,SAAWC,KAClBC,SAASC,iBACP,kBAnBe,EAACC,EAAUC,EAAQ,MACxC,IAAIC,EACAC,EAAS,GACb,MAAO,IAAIC,KACTC,aAAaH,GACbC,EAAS,IAAIA,KAAWC,GACxBF,EAAYI,YAAW,KACrBJ,EAAY,KACZF,EAASG,GACTA,EAAS,KACRF,KAUCM,EAAeJ,IACbN,KAAKW,KAAK,CACRC,cAAeN,EAAOO,KAAIC,GAAKA,EAAEC,OAAOC,QAAQC,eAChDC,MAAOZ,EAAOO,KAAIC,GAAKA,EAAEC,OAAOC,QAAQG,OACxCC,mBAAoBd,EAAOO,KACzBC,GAAKA,EAAEC,OAAOC,QAAQK,mBAExBC,KAAMhB,EAAOO,KAAIU,GAAKzB,OAAO0B,SAASC,OACtCC,eAAgBpB,EAAOO,KAAIC,GAAKA,EAAEC,OAAOC,QAAQW,uBAMzDC,SAAUC,GACJA,EAAKC,aACPC,EAAWC,QAAQH,EAAKI,WAAY,CAClCC,4BAA4B,IAG9BjC,SAASkC,cACP,IAAIC,YAAY,oBAAqB,CACnCC,SAAS,EACTC,YAAY,UC7ClBC,EAAsB,CAACC,EAAOC,EAAW,QAC7C,IAAK3C,OAAOC,SACV,MAAO,KACLU,YAAW,IAAM8B,EAAoBC,EAAOC,EAA3BF,IAAwC,IAI7D,MAAMxB,EAASyB,EAAMzB,OAASyB,EAAMzB,OAASyB,EAEvCE,EAAM,IAAIN,YAAY,kBAAmB,CAC7CC,SAAS,EACTM,OAAQ,CACN5B,OAAAA,EACA0B,SAAAA,KAIJ,MAAO,KACL1B,EAAOoB,cAAcO,KAOnBE,EAAgBC,MAAOC,EAAIC,EAAQ,KACvC,IACE,aAAaD,IACb,MAAOhC,GACP,GAAIiC,EAAQ,GACV,MAAMjC,EAIR,aAXSkC,EASE,MAAQD,EAAQ,IATZ,IAAIE,SAAQC,GAAWzC,WAAWyC,EAASF,MAWnDJ,EAAcE,EAAIC,EAAQ,GAXxBC,IAAAA,GAePG,EAAmB,CAACC,EAASX,KACjCW,EAAQC,SAAQR,MAAAA,IACTL,EAAMc,sBAELV,EAAcL,EAAoBC,EAAOC,QAItCc,EAAwCC,IACnDC,OAAOC,OAAOF,EAAS,CACrBf,SAAU,IAAIkB,qBAAqBR,EAAiBS,KAAKJ,GAAU,MAGhEA,EAAQK,aAAa,SACxBL,EAAQf,SAASqB,QAAQN,IAIhBO,EAAgCP,IACb,SAA1BA,EAAQxC,QAAQgD,QACdR,EAAQf,UAAUe,EAAQf,SAASwB,aACvCrB,EAAcL,EAAoBiB,MCtDvB,MAAMU,UAAwBC,YAC3CC,oBACEb,EAAsCvD,MACtC+D,EAA8B/D,OCHnB,MAAMqE,UAAyBC,oBAC5CF,oBACEb,EAAsCvD,MACtC+D,EAA8B/D,OCHnB,MAAMuE,UAAmBC,cACtCJ,oBACEb,EAAsCvD,MACtC+D,EAA8B/D,OCFlC,MA4BMyE,EAAiB3D,IAChB4D,eAAeC,IAAI,sBACtBD,eAAeE,OAAO,mBAAoBV,GAC1CQ,eAAeE,OAAO,qBAAsBP,EAAkB,CAC5DQ,QAAS,OAEXH,eAAeE,OAAO,cAAeL,EAAY,CAAEM,QAAS,SAI1DC,EAAoBhE,KC5CnB+B,eAAuBkC,GAE5B,MAAMC,EAAY,IAAIC,YAAY,SAASC,OAAOH,GAG5CI,QAAmBC,OAAOC,OAAOC,OAAO,UAAWN,GAQzD,OALkBO,MAAMC,KAAK,IAAIC,WAAWN,IAGlBtE,KAAI6E,IAAM,KAAOA,EAAEC,SAAS,KAAKC,OAAO,KAAIC,KAAK,KDkC3EC,CAAOhF,EAAE6B,OAAOa,QAAQuC,WAAWC,MAAKC,IACtCnF,EAAE6B,OAAOa,QAAQ0C,aAAa,OAAQ,IACtCC,eAAeC,QACb,YAAYH,IACZnF,EAAE6B,OAAOa,QAAQuC,WAEnBjF,EAAEC,OAAOC,QAAQqF,aAAeJ,MAI9BK,EAAsBxF,IAE1B2C,OAAOL,QAAQ+C,gBACZI,QAFiB,EAAEC,EAAKC,KAAcD,EAAIE,WAAW,eAGrDrD,SAAQ,EAAEmD,EAAKG,MACd,MAAMC,EAAQ,iBAAiBC,KAAKL,GAC9BM,EAAgB7G,SAAS8G,cAC7B,wBAAwBH,EAAM,QAG5BE,IACFA,EAAcf,UAAYY,EAC1BR,eAAea,WAAWR,QAKrBS,EAAqB,KAlEH,MAC7B,MAAMC,EAAW,iCAAiCC,KAAKC,UAAUC,WAEjE,GAAI3C,eACF,GAAIwC,EACFjH,SAASqH,MACP,4EAGF,IACE5C,eAAeE,OACb,WACA3E,SAASsH,cAAc,MAAMC,YAC7B,CAAE3C,QAAS,OAEb,MAAOtD,GACPtB,SAASqH,MACP,4EAKNrH,SAASqH,MACP,mEA4CJG,GACAxH,SAASC,iBAAiB,mBAAoBuE,GAC9CxE,SAASC,iBAAiB,aAAcuE,GACxCxE,SAASC,iBAAiB,qBAAsBoG,GAChDrG,SAASC,iBAAiB,kBAAmBuE,GAC7CxE,SAASC,iBAAiB,0BAA2BoG,GACrDrG,SAASC,iBAAiB,+BAAgC4E"}
@@ -0,0 +1,188 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("cable_ready")) : typeof define === "function" && define.amd ? define([ "exports", "cable_ready" ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self,
3
+ factory(global.Futurism = {}, global.CableReady));
4
+ })(this, (function(exports, CableReady) {
5
+ "use strict";
6
+ function _interopDefaultLegacy(e) {
7
+ return e && typeof e === "object" && "default" in e ? e : {
8
+ default: e
9
+ };
10
+ }
11
+ var CableReady__default = _interopDefaultLegacy(CableReady);
12
+ const debounceEvents = (callback, delay = 20) => {
13
+ let timeoutId;
14
+ let events = [];
15
+ return (...args) => {
16
+ clearTimeout(timeoutId);
17
+ events = [ ...events, ...args ];
18
+ timeoutId = setTimeout((() => {
19
+ timeoutId = null;
20
+ callback(events);
21
+ events = [];
22
+ }), delay);
23
+ };
24
+ };
25
+ const createSubscription = consumer => {
26
+ consumer.subscriptions.create("Futurism::Channel", {
27
+ connected() {
28
+ window.Futurism = this;
29
+ document.addEventListener("futurism:appear", debounceEvents((events => {
30
+ this.send({
31
+ signed_params: events.map((e => e.target.dataset.signedParams)),
32
+ sgids: events.map((e => e.target.dataset.sgid)),
33
+ signed_controllers: events.map((e => e.target.dataset.signedController)),
34
+ urls: events.map((_ => window.location.href)),
35
+ broadcast_each: events.map((e => e.target.dataset.broadcastEach))
36
+ });
37
+ })));
38
+ },
39
+ received(data) {
40
+ if (data.cableReady) {
41
+ CableReady__default["default"].perform(data.operations, {
42
+ emitMissingElementWarnings: false
43
+ });
44
+ document.dispatchEvent(new CustomEvent("futurism:appeared", {
45
+ bubbles: true,
46
+ cancelable: true
47
+ }));
48
+ }
49
+ }
50
+ });
51
+ };
52
+ const dispatchAppearEvent = (entry, observer = null) => {
53
+ if (!window.Futurism) {
54
+ return () => {
55
+ setTimeout((() => dispatchAppearEvent(entry, observer)()), 1);
56
+ };
57
+ }
58
+ const target = entry.target ? entry.target : entry;
59
+ const evt = new CustomEvent("futurism:appear", {
60
+ bubbles: true,
61
+ detail: {
62
+ target: target,
63
+ observer: observer
64
+ }
65
+ });
66
+ return () => {
67
+ target.dispatchEvent(evt);
68
+ };
69
+ };
70
+ const wait = ms => new Promise((resolve => setTimeout(resolve, ms)));
71
+ const callWithRetry = async (fn, depth = 0) => {
72
+ try {
73
+ return await fn();
74
+ } catch (e) {
75
+ if (depth > 10) {
76
+ throw e;
77
+ }
78
+ await wait(1.15 ** depth * 2e3);
79
+ return callWithRetry(fn, depth + 1);
80
+ }
81
+ };
82
+ const observerCallback = (entries, observer) => {
83
+ entries.forEach((async entry => {
84
+ if (!entry.isIntersecting) return;
85
+ await callWithRetry(dispatchAppearEvent(entry, observer));
86
+ }));
87
+ };
88
+ const extendElementWithIntersectionObserver = element => {
89
+ Object.assign(element, {
90
+ observer: new IntersectionObserver(observerCallback.bind(element), {})
91
+ });
92
+ if (!element.hasAttribute("keep")) {
93
+ element.observer.observe(element);
94
+ }
95
+ };
96
+ const extendElementWithEagerLoading = element => {
97
+ if (element.dataset.eager === "true") {
98
+ if (element.observer) element.observer.disconnect();
99
+ callWithRetry(dispatchAppearEvent(element));
100
+ }
101
+ };
102
+ class FuturismElement extends HTMLElement {
103
+ connectedCallback() {
104
+ extendElementWithIntersectionObserver(this);
105
+ extendElementWithEagerLoading(this);
106
+ }
107
+ }
108
+ class FuturismTableRow extends HTMLTableRowElement {
109
+ connectedCallback() {
110
+ extendElementWithIntersectionObserver(this);
111
+ extendElementWithEagerLoading(this);
112
+ }
113
+ }
114
+ class FuturismLI extends HTMLLIElement {
115
+ connectedCallback() {
116
+ extendElementWithIntersectionObserver(this);
117
+ extendElementWithEagerLoading(this);
118
+ }
119
+ }
120
+ async function sha256(message) {
121
+ const msgBuffer = new TextEncoder("utf-8").encode(message);
122
+ const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
123
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
124
+ const hashHex = hashArray.map((b => ("00" + b.toString(16)).slice(-2))).join("");
125
+ return hashHex;
126
+ }
127
+ const polyfillCustomElements = () => {
128
+ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
129
+ if (customElements) {
130
+ if (isSafari) {
131
+ document.write('<script src="//unpkg.com/@ungap/custom-elements-builtin"><\/script>');
132
+ } else {
133
+ try {
134
+ customElements.define("built-in", document.createElement("tr").constructor, {
135
+ extends: "tr"
136
+ });
137
+ } catch (_) {
138
+ document.write('<script src="//unpkg.com/@ungap/custom-elements-builtin"><\/script>');
139
+ }
140
+ }
141
+ } else {
142
+ document.write('<script src="//unpkg.com/document-register-element"><\/script>');
143
+ }
144
+ };
145
+ const defineElements = e => {
146
+ if (!customElements.get("futurism-element")) {
147
+ customElements.define("futurism-element", FuturismElement);
148
+ customElements.define("futurism-table-row", FuturismTableRow, {
149
+ extends: "tr"
150
+ });
151
+ customElements.define("futurism-li", FuturismLI, {
152
+ extends: "li"
153
+ });
154
+ }
155
+ };
156
+ const cachePlaceholders = e => {
157
+ sha256(e.detail.element.outerHTML).then((hashedContent => {
158
+ e.detail.element.setAttribute("keep", "");
159
+ sessionStorage.setItem(`futurism-${hashedContent}`, e.detail.element.outerHTML);
160
+ e.target.dataset.futurismHash = hashedContent;
161
+ }));
162
+ };
163
+ const restorePlaceholders = e => {
164
+ const inNamespace = ([key, _payload]) => key.startsWith("futurism-");
165
+ Object.entries(sessionStorage).filter(inNamespace).forEach((([key, payload]) => {
166
+ const match = /^futurism-(.*)/.exec(key);
167
+ const targetElement = document.querySelector(`[data-futurism-hash="${match[1]}"]`);
168
+ if (targetElement) {
169
+ targetElement.outerHTML = payload;
170
+ sessionStorage.removeItem(key);
171
+ }
172
+ }));
173
+ };
174
+ const initializeElements = () => {
175
+ polyfillCustomElements();
176
+ document.addEventListener("DOMContentLoaded", defineElements);
177
+ document.addEventListener("turbo:load", defineElements);
178
+ document.addEventListener("turbo:before-cache", restorePlaceholders);
179
+ document.addEventListener("turbolinks:load", defineElements);
180
+ document.addEventListener("turbolinks:before-cache", restorePlaceholders);
181
+ document.addEventListener("cable-ready:after-outer-html", cachePlaceholders);
182
+ };
183
+ exports.createSubscription = createSubscription;
184
+ exports.initializeElements = initializeElements;
185
+ Object.defineProperty(exports, "__esModule", {
186
+ value: true
187
+ });
188
+ }));
@@ -0,0 +1,2 @@
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("cable_ready")):"function"==typeof define&&define.amd?define(["exports","cable_ready"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Futurism={},e.CableReady)}(this,(function(e,t){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var s=n(t);const r=(e,t=null)=>{if(!window.Futurism)return()=>{setTimeout((()=>r(e,t)()),1)};const n=e.target?e.target:e,s=new CustomEvent("futurism:appear",{bubbles:!0,detail:{target:n,observer:t}});return()=>{n.dispatchEvent(s)}},i=async(e,t=0)=>{try{return await e()}catch(s){if(t>10)throw s;return await(n=1.15**t*2e3,new Promise((e=>setTimeout(e,n)))),i(e,t+1)}var n},a=(e,t)=>{e.forEach((async e=>{e.isIntersecting&&await i(r(e,t))}))},o=e=>{Object.assign(e,{observer:new IntersectionObserver(a.bind(e),{})}),e.hasAttribute("keep")||e.observer.observe(e)},u=e=>{"true"===e.dataset.eager&&(e.observer&&e.observer.disconnect(),i(r(e)))};class c extends HTMLElement{connectedCallback(){o(this),u(this)}}class d extends HTMLTableRowElement{connectedCallback(){o(this),u(this)}}class m extends HTMLLIElement{connectedCallback(){o(this),u(this)}}const l=e=>{customElements.get("futurism-element")||(customElements.define("futurism-element",c),customElements.define("futurism-table-row",d,{extends:"tr"}),customElements.define("futurism-li",m,{extends:"li"}))},f=e=>{(async function(e){const t=new TextEncoder("utf-8").encode(e),n=await crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(n)).map((e=>("00"+e.toString(16)).slice(-2))).join("")})(e.detail.element.outerHTML).then((t=>{e.detail.element.setAttribute("keep",""),sessionStorage.setItem(`futurism-${t}`,e.detail.element.outerHTML),e.target.dataset.futurismHash=t}))},b=e=>{Object.entries(sessionStorage).filter((([e,t])=>e.startsWith("futurism-"))).forEach((([e,t])=>{const n=/^futurism-(.*)/.exec(e),s=document.querySelector(`[data-futurism-hash="${n[1]}"]`);s&&(s.outerHTML=t,sessionStorage.removeItem(e))}))};e.createSubscription=e=>{e.subscriptions.create("Futurism::Channel",{connected(){window.Futurism=this,document.addEventListener("futurism:appear",((e,t=20)=>{let n,s=[];return(...r)=>{clearTimeout(n),s=[...s,...r],n=setTimeout((()=>{n=null,e(s),s=[]}),t)}})((e=>{this.send({signed_params:e.map((e=>e.target.dataset.signedParams)),sgids:e.map((e=>e.target.dataset.sgid)),signed_controllers:e.map((e=>e.target.dataset.signedController)),urls:e.map((e=>window.location.href)),broadcast_each:e.map((e=>e.target.dataset.broadcastEach))})})))},received(e){e.cableReady&&(s.default.perform(e.operations,{emitMissingElementWarnings:!1}),document.dispatchEvent(new CustomEvent("futurism:appeared",{bubbles:!0,cancelable:!0})))}})},e.initializeElements=()=>{(()=>{const e=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);if(customElements)if(e)document.write('<script src="//unpkg.com/@ungap/custom-elements-builtin"><\/script>');else try{customElements.define("built-in",document.createElement("tr").constructor,{extends:"tr"})}catch(e){document.write('<script src="//unpkg.com/@ungap/custom-elements-builtin"><\/script>')}else document.write('<script src="//unpkg.com/document-register-element"><\/script>')})(),document.addEventListener("DOMContentLoaded",l),document.addEventListener("turbo:load",l),document.addEventListener("turbo:before-cache",b),document.addEventListener("turbolinks:load",l),document.addEventListener("turbolinks:before-cache",b),document.addEventListener("cable-ready:after-outer-html",f)},Object.defineProperty(e,"__esModule",{value:!0})}));
2
+ //# sourceMappingURL=futurism.umd.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"futurism.umd.min.js","sources":["../../../javascript/futurism_channel.js","../../../javascript/elements/futurism_utils.js","../../../javascript/elements/futurism_element.js","../../../javascript/elements/futurism_table_row.js","../../../javascript/elements/futurism_li.js","../../../javascript/elements/index.js","../../../javascript/utils/crypto.js"],"sourcesContent":["/* global CustomEvent, setTimeout */\n\nimport CableReady from 'cable_ready'\n\nconst debounceEvents = (callback, delay = 20) => {\n let timeoutId\n let events = []\n return (...args) => {\n clearTimeout(timeoutId)\n events = [...events, ...args]\n timeoutId = setTimeout(() => {\n timeoutId = null\n callback(events)\n events = []\n }, delay)\n }\n}\n\nexport const createSubscription = consumer => {\n consumer.subscriptions.create('Futurism::Channel', {\n connected () {\n window.Futurism = this\n document.addEventListener(\n 'futurism:appear',\n debounceEvents(events => {\n this.send({\n signed_params: events.map(e => e.target.dataset.signedParams),\n sgids: events.map(e => e.target.dataset.sgid),\n signed_controllers: events.map(\n e => e.target.dataset.signedController\n ),\n urls: events.map(_ => window.location.href),\n broadcast_each: events.map(e => e.target.dataset.broadcastEach)\n })\n })\n )\n },\n\n received (data) {\n if (data.cableReady) {\n CableReady.perform(data.operations, {\n emitMissingElementWarnings: false\n })\n\n document.dispatchEvent(\n new CustomEvent('futurism:appeared', {\n bubbles: true,\n cancelable: true\n })\n )\n }\n }\n })\n}\n","/* global IntersectionObserver, CustomEvent, setTimeout */\n\nconst dispatchAppearEvent = (entry, observer = null) => {\n if (!window.Futurism) {\n return () => {\n setTimeout(() => dispatchAppearEvent(entry, observer)(), 1)\n }\n }\n\n const target = entry.target ? entry.target : entry\n\n const evt = new CustomEvent('futurism:appear', {\n bubbles: true,\n detail: {\n target,\n observer\n }\n })\n\n return () => {\n target.dispatchEvent(evt)\n }\n}\n\n// from https://advancedweb.hu/how-to-implement-an-exponential-backoff-retry-strategy-in-javascript/#rejection-based-retrying\nconst wait = ms => new Promise(resolve => setTimeout(resolve, ms))\n\nconst callWithRetry = async (fn, depth = 0) => {\n try {\n return await fn()\n } catch (e) {\n if (depth > 10) {\n throw e\n }\n await wait(1.15 ** depth * 2000)\n\n return callWithRetry(fn, depth + 1)\n }\n}\n\nconst observerCallback = (entries, observer) => {\n entries.forEach(async entry => {\n if (!entry.isIntersecting) return\n\n await callWithRetry(dispatchAppearEvent(entry, observer))\n })\n}\n\nexport const extendElementWithIntersectionObserver = element => {\n Object.assign(element, {\n observer: new IntersectionObserver(observerCallback.bind(element), {})\n })\n\n if (!element.hasAttribute('keep')) {\n element.observer.observe(element)\n }\n}\n\nexport const extendElementWithEagerLoading = element => {\n if (element.dataset.eager === 'true') {\n if (element.observer) element.observer.disconnect()\n callWithRetry(dispatchAppearEvent(element))\n }\n}\n","/* global HTMLElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismElement extends HTMLElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLTableRowElement */\n\nimport {\n extendElementWithIntersectionObserver,\n extendElementWithEagerLoading\n} from './futurism_utils'\n\nexport default class FuturismTableRow extends HTMLTableRowElement {\n connectedCallback () {\n extendElementWithIntersectionObserver(this)\n extendElementWithEagerLoading(this)\n }\n}\n","/* global HTMLLIElement */\r\n\r\nimport {\r\n extendElementWithIntersectionObserver,\r\n extendElementWithEagerLoading\r\n} from './futurism_utils'\r\n\r\nexport default class FuturismLI extends HTMLLIElement {\r\n connectedCallback () {\r\n extendElementWithIntersectionObserver(this)\r\n extendElementWithEagerLoading(this)\r\n }\r\n}\r\n","/* global customElements, sessionStorage */\n\nimport FuturismElement from './futurism_element'\nimport FuturismTableRow from './futurism_table_row'\nimport FuturismLI from './futurism_li'\n\nimport { sha256 } from '../utils/crypto'\n\nconst polyfillCustomElements = () => {\n const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)\n\n if (customElements) {\n if (isSafari) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n } else {\n try {\n customElements.define(\n 'built-in',\n document.createElement('tr').constructor,\n { extends: 'tr' }\n )\n } catch (_) {\n document.write(\n '<script src=\"//unpkg.com/@ungap/custom-elements-builtin\"><\\x2fscript>'\n )\n }\n }\n } else {\n document.write(\n '<script src=\"//unpkg.com/document-register-element\"><\\x2fscript>'\n )\n }\n}\n\nconst defineElements = e => {\n if (!customElements.get('futurism-element')) {\n customElements.define('futurism-element', FuturismElement)\n customElements.define('futurism-table-row', FuturismTableRow, {\n extends: 'tr'\n })\n customElements.define('futurism-li', FuturismLI, { extends: 'li' })\n }\n}\n\nconst cachePlaceholders = e => {\n sha256(e.detail.element.outerHTML).then(hashedContent => {\n e.detail.element.setAttribute('keep', '')\n sessionStorage.setItem(\n `futurism-${hashedContent}`,\n e.detail.element.outerHTML\n )\n e.target.dataset.futurismHash = hashedContent\n })\n}\n\nconst restorePlaceholders = e => {\n const inNamespace = ([key, _payload]) => key.startsWith('futurism-')\n Object.entries(sessionStorage)\n .filter(inNamespace)\n .forEach(([key, payload]) => {\n const match = /^futurism-(.*)/.exec(key)\n const targetElement = document.querySelector(\n `[data-futurism-hash=\"${match[1]}\"]`\n )\n\n if (targetElement) {\n targetElement.outerHTML = payload\n sessionStorage.removeItem(key)\n }\n })\n}\n\nexport const initializeElements = () => {\n polyfillCustomElements()\n document.addEventListener('DOMContentLoaded', defineElements)\n document.addEventListener('turbo:load', defineElements)\n document.addEventListener('turbo:before-cache', restorePlaceholders)\n document.addEventListener('turbolinks:load', defineElements)\n document.addEventListener('turbolinks:before-cache', restorePlaceholders)\n document.addEventListener('cable-ready:after-outer-html', cachePlaceholders)\n}\n","/* global crypto */\n\nexport async function sha256 (message) {\n // encode as UTF-8\n const msgBuffer = new TextEncoder('utf-8').encode(message)\n\n // hash the message\n const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer)\n\n // convert ArrayBuffer to Array\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n\n // convert bytes to hex string\n const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('')\n\n return hashHex\n}\n"],"names":["dispatchAppearEvent","entry","observer","window","Futurism","setTimeout","target","evt","CustomEvent","bubbles","detail","dispatchEvent","callWithRetry","async","fn","depth","e","ms","Promise","resolve","observerCallback","entries","forEach","isIntersecting","extendElementWithIntersectionObserver","element","Object","assign","IntersectionObserver","bind","hasAttribute","observe","extendElementWithEagerLoading","dataset","eager","disconnect","FuturismElement","HTMLElement","connectedCallback","this","FuturismTableRow","HTMLTableRowElement","FuturismLI","HTMLLIElement","defineElements","customElements","get","define","extends","cachePlaceholders","message","msgBuffer","TextEncoder","encode","hashBuffer","crypto","subtle","digest","Array","from","Uint8Array","map","b","toString","slice","join","sha256","outerHTML","then","hashedContent","setAttribute","sessionStorage","setItem","futurismHash","restorePlaceholders","filter","key","_payload","startsWith","payload","match","exec","targetElement","document","querySelector","removeItem","consumer","subscriptions","create","connected","addEventListener","callback","delay","timeoutId","events","args","clearTimeout","debounceEvents","send","signed_params","signedParams","sgids","sgid","signed_controllers","signedController","urls","_","location","href","broadcast_each","broadcastEach","received","data","cableReady","CableReady","perform","operations","emitMissingElementWarnings","cancelable","isSafari","test","navigator","userAgent","write","createElement","constructor","polyfillCustomElements"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  module Futurism
2
- class Channel < ActionCable::Channel::Base
2
+ class Channel < Futurism.configuration.parent_channel.constantize
3
3
  include CableReady::Broadcaster
4
4
 
5
5
  def stream_name
data/bin/rails ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('..', __dir__)
6
+ ENGINE_PATH = File.expand_path('../lib/futurism/engine', __dir__)
7
+ APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
8
+
9
+ # Set up gems listed in the Gemfile.
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
11
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
12
+
13
+ require "rails"
14
+ # Pick the frameworks you want:
15
+ require "active_model/railtie"
16
+ require "active_job/railtie"
17
+ # require "active_record/railtie"
18
+ # require "active_storage/engine"
19
+ require "action_controller/railtie"
20
+ # require "action_mailer/railtie"
21
+ require "action_view/railtie"
22
+ require "action_cable/engine"
23
+ # require "sprockets/railtie"
24
+ require "rails/test_unit/railtie"
25
+ require 'rails/engine/commands'
data/bin/standardize ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+
3
+ bundle exec standardrb --fix
4
+
5
+ npx prettier-standard lib/templates/**/*.js
data/bin/test ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.expand_path("../test", __dir__)
3
+
4
+ require "bundler/setup"
5
+ require "rails/plugin/test"
data/futurism.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ $:.push File.expand_path("lib", __dir__)
2
+
3
+ # Maintain your gem's version:
4
+ require "futurism/version"
5
+
6
+ # Describe your gem and declare its dependencies:
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "futurism"
9
+ spec.version = Futurism::VERSION
10
+ spec.authors = ["Julian Rubisch"]
11
+ spec.email = ["julian@julianrubisch.at"]
12
+ spec.homepage = "https://github.com/stimulusreflex/futurism"
13
+ spec.summary = "Lazy-load Rails partials via CableReady"
14
+ spec.description = "Uses custom html elements with attached IntersectionObserver to automatically lazy load partials via websockets"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = Dir[
18
+ "lib/**/*.rb",
19
+ "app/**/*.rb",
20
+ "app/assets/javascripts/*",
21
+ "bin/*",
22
+ "[A-Z]*"
23
+ ]
24
+
25
+ spec.test_files = Dir["test/**/*.rb"]
26
+
27
+ spec.add_development_dependency "appraisal"
28
+ spec.add_development_dependency "bundler", "~> 2.0"
29
+ spec.add_development_dependency "rake", "~> 13.0"
30
+ spec.add_development_dependency "pry", "~> 0.12.2"
31
+ spec.add_development_dependency "nokogiri"
32
+ spec.add_development_dependency "standardrb"
33
+ spec.add_development_dependency "sqlite3"
34
+
35
+ spec.add_dependency "rack", "~> 2.0"
36
+ spec.add_dependency "rails", ">= 5.2"
37
+ spec.add_dependency "cable_ready", "= 5.0.0.pre9"
38
+ end
data/futurism.gemspec~ ADDED
@@ -0,0 +1,30 @@
1
+ $:.push File.expand_path("lib", __dir__)
2
+
3
+ # Maintain your gem's version:
4
+ require "futurism/version"
5
+
6
+ # Describe your gem and declare its dependencies:
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "futurism"
9
+ spec.version = Futurism::VERSION
10
+ spec.authors = ["Julian Rubisch"]
11
+ spec.email = ["julian@julianrubisch.at"]
12
+ spec.homepage = "https://github.com/stimulusreflex/futurism"
13
+ spec.summary = "Lazy-load Rails partials via CableReady"
14
+ spec.description = "Uses custom html elements with attached IntersectionObserver to automatically lazy load partials via websockets"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
18
+
19
+ spec.add_development_dependency "appraisal"
20
+ spec.add_development_dependency "bundler", "~> 2.0"
21
+ spec.add_development_dependency "rake", "~> 13.0"
22
+ spec.add_development_dependency "pry", "~> 0.12.2"
23
+ spec.add_development_dependency "nokogiri"
24
+ spec.add_development_dependency "standardrb"
25
+ spec.add_development_dependency "sqlite3"
26
+
27
+ spec.add_dependency "rack", "~> 2.0"
28
+ spec.add_dependency "rails", ">= 5.2"
29
+ spec.add_dependency "cable_ready", "= 5.0.0.pre8"
30
+ end
@@ -0,0 +1,21 @@
1
+ module Futurism
2
+ class << self
3
+ def configure
4
+ yield configuration
5
+ end
6
+
7
+ def configuration
8
+ @configuration ||= Configuration.new
9
+ end
10
+
11
+ alias_method :config, :configuration
12
+ end
13
+
14
+ class Configuration
15
+ attr_accessor :parent_channel
16
+
17
+ def initialize
18
+ @parent_channel = "::ApplicationCable::Channel"
19
+ end
20
+ end
21
+ end
@@ -1,4 +1,23 @@
1
1
  module Futurism
2
2
  class Engine < ::Rails::Engine
3
+ initializer "futurism.assets" do |app|
4
+ if app.config.respond_to?(:assets)
5
+ app.config.assets.precompile += %w[
6
+ futurism.js
7
+ futurism.min.js
8
+ futurism.min.js.map
9
+ futurism.umd.js
10
+ futurism.umd.min.js
11
+ futurism.umd.min.js.map
12
+ ]
13
+ end
14
+ end
15
+
16
+ initializer "futurism.importmap", before: "importmap" do |app|
17
+ if app.config.respond_to?(:importmap)
18
+ app.config.importmap.paths << Engine.root.join("lib/futurism/importmap.rb")
19
+ app.config.importmap.cache_sweepers << Engine.root.join("app/assets/javascripts")
20
+ end
21
+ end
3
22
  end
4
23
  end
@@ -1,6 +1,6 @@
1
1
  module Futurism
2
2
  module Helpers
3
- def futurize(records_or_string = nil, extends:, **options, &block)
3
+ def futurize(records_or_string = nil, extends: :div, **options, &block)
4
4
  if (Rails.env.test? && Futurism.skip_in_test) || options[:unless]
5
5
  if records_or_string.nil?
6
6
  return render(**options)
@@ -0,0 +1,2 @@
1
+ pin "cable_ready", to: "cable_ready.min.js", preload: true
2
+ pin "futurism", to: "futurism.min.js", preload: true
@@ -1,3 +1,3 @@
1
1
  module Futurism
2
- VERSION = "1.2.0.pre9"
2
+ VERSION = "1.2.0.pre10"
3
3
  end