cubism 0.1.0.pre12 → 0.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +16 -0
  3. data/Appraisals~ +16 -0
  4. data/CHANGELOG.md +87 -0
  5. data/Gemfile +7 -0
  6. data/Gemfile.lock +219 -0
  7. data/Gemfile~ +9 -0
  8. data/README.md +32 -12
  9. data/README.md~ +191 -0
  10. data/app/assets/javascripts/cubism.js +143 -0
  11. data/app/assets/javascripts/cubism.min.js +2 -0
  12. data/app/assets/javascripts/cubism.min.js.map +1 -0
  13. data/app/assets/javascripts/cubism.umd.js +151 -0
  14. data/app/assets/javascripts/cubism.umd.min.js +2 -0
  15. data/app/assets/javascripts/cubism.umd.min.js.map +1 -0
  16. data/bin/rails +14 -0
  17. data/bin/standardize +4 -0
  18. data/bin/test +5 -0
  19. data/cubism.gemspec +36 -0
  20. data/cubism.gemspec~ +36 -0
  21. data/lib/cubism/engine.rb +20 -0
  22. data/lib/cubism/importmap.rb +2 -0
  23. data/lib/cubism/version.rb +1 -1
  24. data/package.json +43 -0
  25. data/package.json~ +43 -0
  26. data/rollup.config.js +77 -0
  27. data/test/block_container_test.rb +105 -0
  28. data/test/block_source_test.rb +102 -0
  29. data/test/broadcaster_test.rb +66 -0
  30. data/test/channels/cubism/presence_channel_test.rb +72 -0
  31. data/test/cubicle_store_test.rb +34 -0
  32. data/test/cubism_test.rb +7 -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/helpers/application_helper.rb +2 -0
  37. data/test/dummy/app/jobs/application_job.rb +7 -0
  38. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  39. data/test/dummy/app/models/application_record.rb +3 -0
  40. data/test/dummy/app/models/post.rb +3 -0
  41. data/test/dummy/app/models/user.rb +3 -0
  42. data/test/dummy/config/application.rb +22 -0
  43. data/test/dummy/config/boot.rb +5 -0
  44. data/test/dummy/config/environment.rb +5 -0
  45. data/test/dummy/config/environments/development.rb +76 -0
  46. data/test/dummy/config/environments/production.rb +120 -0
  47. data/test/dummy/config/environments/test.rb +59 -0
  48. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  49. data/test/dummy/config/initializers/assets.rb +12 -0
  50. data/test/dummy/config/initializers/backtrace_silencers.rb +8 -0
  51. data/test/dummy/config/initializers/content_security_policy.rb +28 -0
  52. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  53. data/test/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  54. data/test/dummy/config/initializers/inflections.rb +16 -0
  55. data/test/dummy/config/initializers/mime_types.rb +4 -0
  56. data/test/dummy/config/initializers/permissions_policy.rb +11 -0
  57. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/test/dummy/config/puma.rb +43 -0
  59. data/test/dummy/config/routes.rb +3 -0
  60. data/test/dummy/db/migrate/20220102072929_create_posts.rb +10 -0
  61. data/test/dummy/db/migrate/20220102073003_create_users.rb +9 -0
  62. data/test/dummy/db/schema.rb +26 -0
  63. data/test/dummy/test/models/post_test.rb +7 -0
  64. data/test/dummy/test/models/user_test.rb +7 -0
  65. data/test/helpers/cubism_helper_test.rb +39 -0
  66. data/test/integration/navigation_test.rb +7 -0
  67. data/test/models/concerns/presence_test.rb +25 -0
  68. data/test/models/concerns/user_test.rb +9 -0
  69. data/test/rendering/preprocessor_test.rb +61 -0
  70. data/test/test_helper.rb +16 -0
  71. data/yarn.lock +2814 -0
  72. metadata +117 -19
  73. data/app/channels/cubism/presence_channel.rb~ +0 -61
  74. data/app/helpers/cubism_helper.rb~ +0 -36
  75. data/app/models/concerns/cubism/presence.rb~ +0 -20
  76. data/config/routes.rb +0 -2
  77. data/lib/cubism/broadcaster.rb~ +0 -37
  78. data/lib/cubism/cubicle_block_store.rb~ +0 -56
  79. data/lib/cubism/cubicle_store.rb~ +0 -141
  80. data/lib/cubism/engine.rb~ +0 -7
  81. data/lib/cubism/parser.rb~ +0 -0
  82. data/lib/cubism/preprocessor.rb~ +0 -30
  83. data/lib/cubism/version.rb~ +0 -3
  84. data/lib/cubism.rb~ +0 -18
  85. data/lib/tasks/cubism_tasks.rake +0 -4
  86. /data/{lib/cubism/cubicle_source_store.rb~ → rollup.config.js~} +0 -0
@@ -0,0 +1,143 @@
1
+ import CableReady, { SubscribingElement } from "cable_ready";
2
+
3
+ function debounce(fn, delay = 250) {
4
+ let timer;
5
+ return (...args) => {
6
+ const callback = () => fn.apply(this, args);
7
+ if (timer) clearTimeout(timer);
8
+ timer = setTimeout(callback, delay);
9
+ };
10
+ }
11
+
12
+ class Cubicle extends SubscribingElement {
13
+ constructor() {
14
+ super();
15
+ const shadowRoot = this.attachShadow({
16
+ mode: "open"
17
+ });
18
+ shadowRoot.innerHTML = `\n<style>\n :host {\n display: block;\n }\n</style>\n<slot></slot>\n`;
19
+ this.triggerRoot = this;
20
+ this.present = false;
21
+ }
22
+ async connectedCallback() {
23
+ if (this.preview) return;
24
+ this.appear = debounce(this.appear.bind(this), 50);
25
+ this.appearTriggers = this.getAttribute("appear-trigger") ? this.getAttribute("appear-trigger").split(",") : [];
26
+ this.disappearTriggers = this.getAttribute("disappear-trigger") ? this.getAttribute("disappear-trigger").split(",") : [];
27
+ this.triggerRootSelector = this.getAttribute("trigger-root");
28
+ this.consumer = await CableReady.consumer;
29
+ console.log(this.consumer);
30
+ this.channel = this.createSubscription();
31
+ this.appearanceIntersectionObserver = new IntersectionObserver(((entries, observer) => {
32
+ entries.forEach((entry => {
33
+ if (entry.target !== this.triggerRoot) return;
34
+ if (entry.isIntersecting) {
35
+ if (!this.present) {
36
+ this.appear();
37
+ }
38
+ } else {
39
+ if (this.present) {
40
+ this.disappear();
41
+ }
42
+ }
43
+ }));
44
+ }), {
45
+ threshold: 0
46
+ });
47
+ this.mutationObserver = new MutationObserver(((mutationsList, observer) => {
48
+ if (this.triggerRootSelector) {
49
+ for (const mutation of mutationsList) {
50
+ const root = document.querySelector(this.triggerRootSelector);
51
+ if (root) {
52
+ this.uninstall();
53
+ this.triggerRoot = root;
54
+ this.install();
55
+ }
56
+ }
57
+ }
58
+ this.mutationObserver.disconnect();
59
+ }));
60
+ this.mutationObserver.observe(document, {
61
+ subtree: true,
62
+ childList: true
63
+ });
64
+ }
65
+ disconnectedCallback() {
66
+ this.disappear();
67
+ super.disconnectedCallback();
68
+ }
69
+ install() {
70
+ if (this.appearTriggers.includes("connect")) {
71
+ this.appear();
72
+ }
73
+ this.appearTriggers.filter((eventName => eventName === "intersect")).forEach((() => {
74
+ this.appearanceIntersectionObserver.observe(this.triggerRoot);
75
+ }));
76
+ this.appearTriggers.filter((eventName => eventName !== "connect" && eventName !== "intersect")).forEach((eventName => {
77
+ this.triggerRoot.addEventListener(eventName, this.appear.bind(this));
78
+ }));
79
+ this.disappearTriggers.filter((eventName => eventName !== "disconnect" && eventName !== "intersect")).forEach((eventName => {
80
+ this.triggerRoot.addEventListener(eventName, this.disappear.bind(this));
81
+ }));
82
+ this.disappearTriggers.filter((eventName => eventName === "intersect")).forEach((() => {}));
83
+ }
84
+ uninstall() {
85
+ this.appearTriggers.filter((eventName => eventName === "intersect")).forEach((() => {
86
+ this.appearanceIntersectionObserver.unobserve(this.triggerRoot);
87
+ }));
88
+ this.appearTriggers.filter((eventName => eventName !== "connect" && eventName !== "intersect")).forEach((eventName => {
89
+ this.triggerRoot.removeEventListener(eventName, this.appear.bind(this));
90
+ }));
91
+ this.disappearTriggers.filter((eventName => eventName === "intersect")).forEach((() => {}));
92
+ this.disappearTriggers.filter((eventName => eventName !== "disconnect" && eventName !== "intersect")).forEach((eventName => {
93
+ this.triggerRoot.removeEventListener(eventName, this.disappear.bind(this));
94
+ }));
95
+ }
96
+ appear() {
97
+ if (this.channel) {
98
+ this.present = true;
99
+ this.channel.perform("appear");
100
+ }
101
+ }
102
+ disappear() {
103
+ if (this.channel) {
104
+ this.present = false;
105
+ this.channel.perform("disappear");
106
+ }
107
+ }
108
+ performOperations(data) {
109
+ if (data.cableReady) {
110
+ CableReady.perform(data.operations);
111
+ }
112
+ }
113
+ createSubscription() {
114
+ if (!this.consumer) {
115
+ console.error("The `cubicle-element` helper cannot connect without an ActionCable consumer.");
116
+ return;
117
+ }
118
+ return this.consumer.subscriptions.create({
119
+ channel: this.channelName,
120
+ identifier: this.getAttribute("identifier"),
121
+ element_id: this.id,
122
+ scope: this.getAttribute("scope"),
123
+ exclude_current_user: this.getAttribute("exclude-current-user") === "true"
124
+ }, {
125
+ connected: () => {
126
+ this.install();
127
+ },
128
+ disconnected: () => {
129
+ this.disappear();
130
+ this.uninstall();
131
+ },
132
+ rejected: () => {
133
+ this.uninstall();
134
+ },
135
+ received: this.performOperations.bind(this)
136
+ });
137
+ }
138
+ get channelName() {
139
+ return "Cubism::PresenceChannel";
140
+ }
141
+ }
142
+
143
+ customElements.define("cubicle-element", Cubicle);
@@ -0,0 +1,2 @@
1
+ import e,{SubscribingElement as t}from"cable_ready";customElements.define("cubicle-element",class extends t{constructor(){super();this.attachShadow({mode:"open"}).innerHTML="\n<style>\n :host {\n display: block;\n }\n</style>\n<slot></slot>\n",this.triggerRoot=this,this.present=!1}async connectedCallback(){this.preview||(this.appear=function(e,t=250){let r;return(...i)=>{r&&clearTimeout(r),r=setTimeout((()=>e.apply(this,i)),t)}}(this.appear.bind(this),50),this.appearTriggers=this.getAttribute("appear-trigger")?this.getAttribute("appear-trigger").split(","):[],this.disappearTriggers=this.getAttribute("disappear-trigger")?this.getAttribute("disappear-trigger").split(","):[],this.triggerRootSelector=this.getAttribute("trigger-root"),this.consumer=await e.consumer,console.log(this.consumer),this.channel=this.createSubscription(),this.appearanceIntersectionObserver=new IntersectionObserver(((e,t)=>{e.forEach((e=>{e.target===this.triggerRoot&&(e.isIntersecting?this.present||this.appear():this.present&&this.disappear())}))}),{threshold:0}),this.mutationObserver=new MutationObserver(((e,t)=>{if(this.triggerRootSelector)for(const t of e){const e=document.querySelector(this.triggerRootSelector);e&&(this.uninstall(),this.triggerRoot=e,this.install())}this.mutationObserver.disconnect()})),this.mutationObserver.observe(document,{subtree:!0,childList:!0}))}disconnectedCallback(){this.disappear(),super.disconnectedCallback()}install(){this.appearTriggers.includes("connect")&&this.appear(),this.appearTriggers.filter((e=>"intersect"===e)).forEach((()=>{this.appearanceIntersectionObserver.observe(this.triggerRoot)})),this.appearTriggers.filter((e=>"connect"!==e&&"intersect"!==e)).forEach((e=>{this.triggerRoot.addEventListener(e,this.appear.bind(this))})),this.disappearTriggers.filter((e=>"disconnect"!==e&&"intersect"!==e)).forEach((e=>{this.triggerRoot.addEventListener(e,this.disappear.bind(this))})),this.disappearTriggers.filter((e=>"intersect"===e)).forEach((()=>{}))}uninstall(){this.appearTriggers.filter((e=>"intersect"===e)).forEach((()=>{this.appearanceIntersectionObserver.unobserve(this.triggerRoot)})),this.appearTriggers.filter((e=>"connect"!==e&&"intersect"!==e)).forEach((e=>{this.triggerRoot.removeEventListener(e,this.appear.bind(this))})),this.disappearTriggers.filter((e=>"intersect"===e)).forEach((()=>{})),this.disappearTriggers.filter((e=>"disconnect"!==e&&"intersect"!==e)).forEach((e=>{this.triggerRoot.removeEventListener(e,this.disappear.bind(this))}))}appear(){this.channel&&(this.present=!0,this.channel.perform("appear"))}disappear(){this.channel&&(this.present=!1,this.channel.perform("disappear"))}performOperations(t){t.cableReady&&e.perform(t.operations)}createSubscription(){if(this.consumer)return this.consumer.subscriptions.create({channel:this.channelName,identifier:this.getAttribute("identifier"),element_id:this.id,scope:this.getAttribute("scope"),exclude_current_user:"true"===this.getAttribute("exclude-current-user")},{connected:()=>{this.install()},disconnected:()=>{this.disappear(),this.uninstall()},rejected:()=>{this.uninstall()},received:this.performOperations.bind(this)});console.error("The `cubicle-element` helper cannot connect without an ActionCable consumer.")}get channelName(){return"Cubism::PresenceChannel"}});
2
+ //# sourceMappingURL=cubism.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cubism.min.js","sources":["../../../javascript/elements/index.js","../../../javascript/elements/cubicle.js","../../../../cable_ready/javascript/utils.js"],"sourcesContent":["/* global customElements */\n\nimport { Cubicle } from './cubicle'\n\nexport * from './cubicle'\n\ncustomElements.define('cubicle-element', Cubicle)\n","/* eslint-disable no-undef */\nimport CableReady, { SubscribingElement } from 'cable_ready'\nimport { debounce } from 'cable_ready/javascript/utils'\n\nexport class Cubicle extends SubscribingElement {\n constructor () {\n super()\n const shadowRoot = this.attachShadow({ mode: 'open' })\n shadowRoot.innerHTML = `\n<style>\n :host {\n display: block;\n }\n</style>\n<slot></slot>\n`\n\n this.triggerRoot = this\n this.present = false\n }\n\n async connectedCallback () {\n if (this.preview) return\n\n this.appear = debounce(this.appear.bind(this), 50)\n\n this.appearTriggers = this.getAttribute('appear-trigger')\n ? this.getAttribute('appear-trigger').split(',')\n : []\n this.disappearTriggers = this.getAttribute('disappear-trigger')\n ? this.getAttribute('disappear-trigger').split(',')\n : []\n this.triggerRootSelector = this.getAttribute('trigger-root')\n\n this.consumer = await CableReady.consumer\n console.log(this.consumer)\n\n this.channel = this.createSubscription()\n\n this.appearanceIntersectionObserver = new IntersectionObserver(\n (entries, observer) => {\n entries.forEach(entry => {\n if (entry.target !== this.triggerRoot) return\n if (entry.isIntersecting) {\n if (!this.present) {\n this.appear()\n }\n } else {\n if (this.present) {\n this.disappear()\n }\n }\n })\n },\n { threshold: 0 }\n )\n\n this.mutationObserver = new MutationObserver((mutationsList, observer) => {\n if (this.triggerRootSelector) {\n // eslint-disable-next-line no-unused-vars\n for (const mutation of mutationsList) {\n const root = document.querySelector(this.triggerRootSelector)\n if (root) {\n this.uninstall()\n this.triggerRoot = root\n this.install()\n }\n }\n }\n this.mutationObserver.disconnect()\n })\n\n this.mutationObserver.observe(document, {\n subtree: true,\n childList: true\n })\n }\n\n disconnectedCallback () {\n this.disappear()\n super.disconnectedCallback()\n }\n\n install () {\n if (this.appearTriggers.includes('connect')) {\n this.appear()\n }\n\n this.appearTriggers\n .filter(eventName => eventName === 'intersect')\n .forEach(() => {\n this.appearanceIntersectionObserver.observe(this.triggerRoot)\n })\n\n this.appearTriggers\n .filter(eventName => eventName !== 'connect' && eventName !== 'intersect')\n .forEach(eventName => {\n this.triggerRoot.addEventListener(eventName, this.appear.bind(this))\n })\n\n this.disappearTriggers\n .filter(\n eventName => eventName !== 'disconnect' && eventName !== 'intersect'\n )\n .forEach(eventName => {\n this.triggerRoot.addEventListener(eventName, this.disappear.bind(this))\n })\n\n this.disappearTriggers\n .filter(eventName => eventName === 'intersect')\n .forEach(() => {})\n }\n\n uninstall () {\n this.appearTriggers\n .filter(eventName => eventName === 'intersect')\n .forEach(() => {\n this.appearanceIntersectionObserver.unobserve(this.triggerRoot)\n })\n\n this.appearTriggers\n .filter(eventName => eventName !== 'connect' && eventName !== 'intersect')\n .forEach(eventName => {\n this.triggerRoot.removeEventListener(eventName, this.appear.bind(this))\n })\n\n this.disappearTriggers\n .filter(eventName => eventName === 'intersect')\n .forEach(() => {})\n\n this.disappearTriggers\n .filter(\n eventName => eventName !== 'disconnect' && eventName !== 'intersect'\n )\n .forEach(eventName => {\n this.triggerRoot.removeEventListener(\n eventName,\n this.disappear.bind(this)\n )\n })\n }\n\n appear () {\n if (this.channel) {\n this.present = true\n this.channel.perform('appear')\n }\n }\n\n disappear () {\n if (this.channel) {\n this.present = false\n this.channel.perform('disappear')\n }\n }\n\n performOperations (data) {\n if (data.cableReady) {\n CableReady.perform(data.operations)\n }\n }\n\n createSubscription () {\n if (!this.consumer) {\n console.error(\n 'The `cubicle-element` helper cannot connect without an ActionCable consumer.'\n )\n return\n }\n\n return this.consumer.subscriptions.create(\n {\n channel: this.channelName,\n identifier: this.getAttribute('identifier'),\n element_id: this.id,\n scope: this.getAttribute('scope'),\n exclude_current_user:\n this.getAttribute('exclude-current-user') === 'true'\n },\n {\n connected: () => {\n this.install()\n },\n disconnected: () => {\n this.disappear()\n this.uninstall()\n },\n rejected: () => {\n this.uninstall()\n },\n received: this.performOperations.bind(this)\n }\n )\n }\n\n get channelName () {\n return 'Cubism::PresenceChannel'\n }\n}\n","import { inputTags, textInputTypes } from './enums'\nimport ActiveElement from './active_element'\n\n// Indicates if the passed element is considered a text input.\n//\nconst isTextInput = element => {\n return inputTags[element.tagName] && textInputTypes[element.type]\n}\n\n// Assigns focus to the appropriate element... preferring the explicitly passed selector\n//\n// * selector - a CSS selector for the element that should have focus\n//\nconst assignFocus = selector => {\n const element =\n selector && selector.nodeType === Node.ELEMENT_NODE\n ? selector\n : document.querySelector(selector)\n const focusElement = element || ActiveElement.element\n if (focusElement && focusElement.focus) focusElement.focus()\n}\n\n// Dispatches an event on the passed element\n//\n// * element - the element\n// * name - the name of the event\n// * detail - the event detail\n//\nconst dispatch = (element, name, detail = {}) => {\n const init = { bubbles: true, cancelable: true, detail }\n const event = new CustomEvent(name, init)\n element.dispatchEvent(event)\n if (window.jQuery) window.jQuery(element).trigger(name, detail)\n}\n\n// Accepts an xPath query and returns the element found at that position in the DOM\n//\nconst xpathToElement = xpath => {\n return document.evaluate(\n xpath,\n document,\n null,\n XPathResult.FIRST_ORDERED_NODE_TYPE,\n null\n ).singleNodeValue\n}\n\n// Accepts an xPath query and returns all matching elements in the DOM\n//\nconst xpathToElementArray = (xpath, reverse = false) => {\n const snapshotList = document.evaluate(\n xpath,\n document,\n null,\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\n null\n )\n const snapshots = []\n\n for (let i = 0; i < snapshotList.snapshotLength; i++) {\n snapshots.push(snapshotList.snapshotItem(i))\n }\n\n return reverse ? snapshots.reverse() : snapshots\n}\n\n// Return an array with the class names to be used\n//\n// * names - could be a string or an array of strings for multiple classes.\n//\nconst getClassNames = names => Array.from(names).flat()\n\n// Perform operation for either the first or all of the elements returned by CSS selector\n//\n// * operation - the instruction payload from perform\n// * callback - the operation function to run for each element\n//\nconst processElements = (operation, callback) => {\n Array.from(\n operation.selectAll ? operation.element : [operation.element]\n ).forEach(callback)\n}\n\n// convert string to kebab-case\n// most other implementations (lodash) are focused on camelCase to kebab-case\n// instead, this uses word token boundaries to produce readable URL slugs and keys\n// this implementation will not support Emoji or other non-ASCII characters\n//\nconst kebabize = createCompounder(function (result, word, index) {\n return result + (index ? '-' : '') + word.toLowerCase()\n})\n\nfunction createCompounder (callback) {\n return function (str) {\n return words(str).reduce(callback, '')\n }\n}\n\nconst words = str => {\n str = str == null ? '' : str\n return str.match(/([A-Z]{2,}|[0-9]+|[A-Z]?[a-z]+|[A-Z])/g) || []\n}\n\n// Provide a standardized pipeline of checks and modifications to all operations based on provided options\n// Currently skips execution if cancelled and implements an optional delay\n//\nconst operate = (operation, callback) => {\n if (!operation.cancel) {\n operation.delay ? setTimeout(callback, operation.delay) : callback()\n return true\n }\n return false\n}\n\n// Dispatch life-cycle events with standardized naming\nconst before = (target, operation) =>\n dispatch(\n target,\n `cable-ready:before-${kebabize(operation.operation)}`,\n operation\n )\n\nconst after = (target, operation) =>\n dispatch(\n target,\n `cable-ready:after-${kebabize(operation.operation)}`,\n operation\n )\n\nfunction debounce (fn, delay = 250) {\n let timer\n return (...args) => {\n const callback = () => fn.apply(this, args)\n if (timer) clearTimeout(timer)\n timer = setTimeout(callback, delay)\n }\n}\n\nfunction handleErrors (response) {\n if (!response.ok) throw Error(response.statusText)\n return response\n}\n\nfunction safeScalar (val) {\n if (\n val !== undefined &&\n !['string', 'number', 'boolean'].includes(typeof val)\n )\n console.warn(\n `Operation expects a string, number or boolean, but got ${val} (${typeof val})`\n )\n return val != null ? val : ''\n}\n\nfunction safeString (str) {\n if (str !== undefined && typeof str !== 'string')\n console.warn(`Operation expects a string, but got ${str} (${typeof str})`)\n\n return str != null ? String(str) : ''\n}\n\nfunction safeArray (arr) {\n if (arr !== undefined && !Array.isArray(arr))\n console.warn(`Operation expects an array, but got ${arr} (${typeof arr})`)\n return arr != null ? Array.from(arr) : []\n}\n\nfunction safeObject (obj) {\n if (obj !== undefined && typeof obj !== 'object')\n console.warn(`Operation expects an object, but got ${obj} (${typeof obj})`)\n return obj != null ? Object(obj) : {}\n}\n\nfunction safeStringOrArray (elem) {\n if (elem !== undefined && !Array.isArray(elem) && typeof elem !== 'string')\n console.warn(`Operation expects an Array or a String, but got ${elem} (${typeof elem})`)\n\n return elem == null ? '' : Array.isArray(elem) ? Array.from(elem) : String(elem)\n}\n\nfunction fragmentToString (fragment) {\n return new XMLSerializer().serializeToString(fragment)\n}\n\n// A proxy method to wrap a fetch call in error handling\n//\n// * url - the URL to fetch\n// * additionalHeaders - an object of additional headers passed to fetch\n//\nasync function graciouslyFetch (url, additionalHeaders) {\n try {\n const response = await fetch(url, {\n headers: {\n 'X-REQUESTED-WITH': 'XmlHttpRequest',\n ...additionalHeaders\n }\n })\n if (response == undefined) return\n\n handleErrors(response)\n\n return response\n } catch (e) {\n console.error(`Could not fetch ${url}`)\n }\n}\n\nexport {\n isTextInput,\n assignFocus,\n dispatch,\n xpathToElement,\n xpathToElementArray,\n getClassNames,\n processElements,\n operate,\n before,\n after,\n debounce,\n handleErrors,\n graciouslyFetch,\n kebabize,\n safeScalar,\n safeString,\n safeArray,\n safeObject,\n safeStringOrArray,\n fragmentToString\n}\n"],"names":["customElements","define","SubscribingElement","constructor","super","this","attachShadow","mode","innerHTML","triggerRoot","present","async","preview","appear","fn","delay","timer","args","clearTimeout","setTimeout","apply","debounce","bind","appearTriggers","getAttribute","split","disappearTriggers","triggerRootSelector","consumer","CableReady","console","log","channel","createSubscription","appearanceIntersectionObserver","IntersectionObserver","entries","observer","forEach","entry","target","isIntersecting","disappear","threshold","mutationObserver","MutationObserver","mutationsList","mutation","root","document","querySelector","uninstall","install","disconnect","observe","subtree","childList","disconnectedCallback","includes","filter","eventName","addEventListener","unobserve","removeEventListener","perform","performOperations","data","cableReady","operations","subscriptions","create","channelName","identifier","element_id","id","scope","exclude_current_user","connected","disconnected","rejected","received","error"],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(require("cable_ready")) : typeof define === "function" && define.amd ? define([ "cable_ready" ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self,
3
+ factory(global.CableReady));
4
+ })(this, (function(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
+ function debounce(fn, delay = 250) {
13
+ let timer;
14
+ return (...args) => {
15
+ const callback = () => fn.apply(this, args);
16
+ if (timer) clearTimeout(timer);
17
+ timer = setTimeout(callback, delay);
18
+ };
19
+ }
20
+ class Cubicle extends CableReady.SubscribingElement {
21
+ constructor() {
22
+ super();
23
+ const shadowRoot = this.attachShadow({
24
+ mode: "open"
25
+ });
26
+ shadowRoot.innerHTML = `\n<style>\n :host {\n display: block;\n }\n</style>\n<slot></slot>\n`;
27
+ this.triggerRoot = this;
28
+ this.present = false;
29
+ }
30
+ async connectedCallback() {
31
+ if (this.preview) return;
32
+ this.appear = debounce(this.appear.bind(this), 50);
33
+ this.appearTriggers = this.getAttribute("appear-trigger") ? this.getAttribute("appear-trigger").split(",") : [];
34
+ this.disappearTriggers = this.getAttribute("disappear-trigger") ? this.getAttribute("disappear-trigger").split(",") : [];
35
+ this.triggerRootSelector = this.getAttribute("trigger-root");
36
+ this.consumer = await CableReady__default["default"].consumer;
37
+ console.log(this.consumer);
38
+ this.channel = this.createSubscription();
39
+ this.appearanceIntersectionObserver = new IntersectionObserver(((entries, observer) => {
40
+ entries.forEach((entry => {
41
+ if (entry.target !== this.triggerRoot) return;
42
+ if (entry.isIntersecting) {
43
+ if (!this.present) {
44
+ this.appear();
45
+ }
46
+ } else {
47
+ if (this.present) {
48
+ this.disappear();
49
+ }
50
+ }
51
+ }));
52
+ }), {
53
+ threshold: 0
54
+ });
55
+ this.mutationObserver = new MutationObserver(((mutationsList, observer) => {
56
+ if (this.triggerRootSelector) {
57
+ for (const mutation of mutationsList) {
58
+ const root = document.querySelector(this.triggerRootSelector);
59
+ if (root) {
60
+ this.uninstall();
61
+ this.triggerRoot = root;
62
+ this.install();
63
+ }
64
+ }
65
+ }
66
+ this.mutationObserver.disconnect();
67
+ }));
68
+ this.mutationObserver.observe(document, {
69
+ subtree: true,
70
+ childList: true
71
+ });
72
+ }
73
+ disconnectedCallback() {
74
+ this.disappear();
75
+ super.disconnectedCallback();
76
+ }
77
+ install() {
78
+ if (this.appearTriggers.includes("connect")) {
79
+ this.appear();
80
+ }
81
+ this.appearTriggers.filter((eventName => eventName === "intersect")).forEach((() => {
82
+ this.appearanceIntersectionObserver.observe(this.triggerRoot);
83
+ }));
84
+ this.appearTriggers.filter((eventName => eventName !== "connect" && eventName !== "intersect")).forEach((eventName => {
85
+ this.triggerRoot.addEventListener(eventName, this.appear.bind(this));
86
+ }));
87
+ this.disappearTriggers.filter((eventName => eventName !== "disconnect" && eventName !== "intersect")).forEach((eventName => {
88
+ this.triggerRoot.addEventListener(eventName, this.disappear.bind(this));
89
+ }));
90
+ this.disappearTriggers.filter((eventName => eventName === "intersect")).forEach((() => {}));
91
+ }
92
+ uninstall() {
93
+ this.appearTriggers.filter((eventName => eventName === "intersect")).forEach((() => {
94
+ this.appearanceIntersectionObserver.unobserve(this.triggerRoot);
95
+ }));
96
+ this.appearTriggers.filter((eventName => eventName !== "connect" && eventName !== "intersect")).forEach((eventName => {
97
+ this.triggerRoot.removeEventListener(eventName, this.appear.bind(this));
98
+ }));
99
+ this.disappearTriggers.filter((eventName => eventName === "intersect")).forEach((() => {}));
100
+ this.disappearTriggers.filter((eventName => eventName !== "disconnect" && eventName !== "intersect")).forEach((eventName => {
101
+ this.triggerRoot.removeEventListener(eventName, this.disappear.bind(this));
102
+ }));
103
+ }
104
+ appear() {
105
+ if (this.channel) {
106
+ this.present = true;
107
+ this.channel.perform("appear");
108
+ }
109
+ }
110
+ disappear() {
111
+ if (this.channel) {
112
+ this.present = false;
113
+ this.channel.perform("disappear");
114
+ }
115
+ }
116
+ performOperations(data) {
117
+ if (data.cableReady) {
118
+ CableReady__default["default"].perform(data.operations);
119
+ }
120
+ }
121
+ createSubscription() {
122
+ if (!this.consumer) {
123
+ console.error("The `cubicle-element` helper cannot connect without an ActionCable consumer.");
124
+ return;
125
+ }
126
+ return this.consumer.subscriptions.create({
127
+ channel: this.channelName,
128
+ identifier: this.getAttribute("identifier"),
129
+ element_id: this.id,
130
+ scope: this.getAttribute("scope"),
131
+ exclude_current_user: this.getAttribute("exclude-current-user") === "true"
132
+ }, {
133
+ connected: () => {
134
+ this.install();
135
+ },
136
+ disconnected: () => {
137
+ this.disappear();
138
+ this.uninstall();
139
+ },
140
+ rejected: () => {
141
+ this.uninstall();
142
+ },
143
+ received: this.performOperations.bind(this)
144
+ });
145
+ }
146
+ get channelName() {
147
+ return "Cubism::PresenceChannel";
148
+ }
149
+ }
150
+ customElements.define("cubicle-element", Cubicle);
151
+ }));
@@ -0,0 +1,2 @@
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("cable_ready")):"function"==typeof define&&define.amd?define(["cable_ready"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).CableReady)}(this,(function(e){"use strict";function t(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var i=t(e);class r extends e.SubscribingElement{constructor(){super();this.attachShadow({mode:"open"}).innerHTML="\n<style>\n :host {\n display: block;\n }\n</style>\n<slot></slot>\n",this.triggerRoot=this,this.present=!1}async connectedCallback(){this.preview||(this.appear=function(e,t=250){let i;return(...r)=>{i&&clearTimeout(i),i=setTimeout((()=>e.apply(this,r)),t)}}(this.appear.bind(this),50),this.appearTriggers=this.getAttribute("appear-trigger")?this.getAttribute("appear-trigger").split(","):[],this.disappearTriggers=this.getAttribute("disappear-trigger")?this.getAttribute("disappear-trigger").split(","):[],this.triggerRootSelector=this.getAttribute("trigger-root"),this.consumer=await i.default.consumer,console.log(this.consumer),this.channel=this.createSubscription(),this.appearanceIntersectionObserver=new IntersectionObserver(((e,t)=>{e.forEach((e=>{e.target===this.triggerRoot&&(e.isIntersecting?this.present||this.appear():this.present&&this.disappear())}))}),{threshold:0}),this.mutationObserver=new MutationObserver(((e,t)=>{if(this.triggerRootSelector)for(const t of e){const e=document.querySelector(this.triggerRootSelector);e&&(this.uninstall(),this.triggerRoot=e,this.install())}this.mutationObserver.disconnect()})),this.mutationObserver.observe(document,{subtree:!0,childList:!0}))}disconnectedCallback(){this.disappear(),super.disconnectedCallback()}install(){this.appearTriggers.includes("connect")&&this.appear(),this.appearTriggers.filter((e=>"intersect"===e)).forEach((()=>{this.appearanceIntersectionObserver.observe(this.triggerRoot)})),this.appearTriggers.filter((e=>"connect"!==e&&"intersect"!==e)).forEach((e=>{this.triggerRoot.addEventListener(e,this.appear.bind(this))})),this.disappearTriggers.filter((e=>"disconnect"!==e&&"intersect"!==e)).forEach((e=>{this.triggerRoot.addEventListener(e,this.disappear.bind(this))})),this.disappearTriggers.filter((e=>"intersect"===e)).forEach((()=>{}))}uninstall(){this.appearTriggers.filter((e=>"intersect"===e)).forEach((()=>{this.appearanceIntersectionObserver.unobserve(this.triggerRoot)})),this.appearTriggers.filter((e=>"connect"!==e&&"intersect"!==e)).forEach((e=>{this.triggerRoot.removeEventListener(e,this.appear.bind(this))})),this.disappearTriggers.filter((e=>"intersect"===e)).forEach((()=>{})),this.disappearTriggers.filter((e=>"disconnect"!==e&&"intersect"!==e)).forEach((e=>{this.triggerRoot.removeEventListener(e,this.disappear.bind(this))}))}appear(){this.channel&&(this.present=!0,this.channel.perform("appear"))}disappear(){this.channel&&(this.present=!1,this.channel.perform("disappear"))}performOperations(e){e.cableReady&&i.default.perform(e.operations)}createSubscription(){if(this.consumer)return this.consumer.subscriptions.create({channel:this.channelName,identifier:this.getAttribute("identifier"),element_id:this.id,scope:this.getAttribute("scope"),exclude_current_user:"true"===this.getAttribute("exclude-current-user")},{connected:()=>{this.install()},disconnected:()=>{this.disappear(),this.uninstall()},rejected:()=>{this.uninstall()},received:this.performOperations.bind(this)});console.error("The `cubicle-element` helper cannot connect without an ActionCable consumer.")}get channelName(){return"Cubism::PresenceChannel"}}customElements.define("cubicle-element",r)}));
2
+ //# sourceMappingURL=cubism.umd.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cubism.umd.min.js","sources":["../../../javascript/elements/cubicle.js","../../../../cable_ready/javascript/utils.js","../../../javascript/elements/index.js"],"sourcesContent":["/* eslint-disable no-undef */\nimport CableReady, { SubscribingElement } from 'cable_ready'\nimport { debounce } from 'cable_ready/javascript/utils'\n\nexport class Cubicle extends SubscribingElement {\n constructor () {\n super()\n const shadowRoot = this.attachShadow({ mode: 'open' })\n shadowRoot.innerHTML = `\n<style>\n :host {\n display: block;\n }\n</style>\n<slot></slot>\n`\n\n this.triggerRoot = this\n this.present = false\n }\n\n async connectedCallback () {\n if (this.preview) return\n\n this.appear = debounce(this.appear.bind(this), 50)\n\n this.appearTriggers = this.getAttribute('appear-trigger')\n ? this.getAttribute('appear-trigger').split(',')\n : []\n this.disappearTriggers = this.getAttribute('disappear-trigger')\n ? this.getAttribute('disappear-trigger').split(',')\n : []\n this.triggerRootSelector = this.getAttribute('trigger-root')\n\n this.consumer = await CableReady.consumer\n console.log(this.consumer)\n\n this.channel = this.createSubscription()\n\n this.appearanceIntersectionObserver = new IntersectionObserver(\n (entries, observer) => {\n entries.forEach(entry => {\n if (entry.target !== this.triggerRoot) return\n if (entry.isIntersecting) {\n if (!this.present) {\n this.appear()\n }\n } else {\n if (this.present) {\n this.disappear()\n }\n }\n })\n },\n { threshold: 0 }\n )\n\n this.mutationObserver = new MutationObserver((mutationsList, observer) => {\n if (this.triggerRootSelector) {\n // eslint-disable-next-line no-unused-vars\n for (const mutation of mutationsList) {\n const root = document.querySelector(this.triggerRootSelector)\n if (root) {\n this.uninstall()\n this.triggerRoot = root\n this.install()\n }\n }\n }\n this.mutationObserver.disconnect()\n })\n\n this.mutationObserver.observe(document, {\n subtree: true,\n childList: true\n })\n }\n\n disconnectedCallback () {\n this.disappear()\n super.disconnectedCallback()\n }\n\n install () {\n if (this.appearTriggers.includes('connect')) {\n this.appear()\n }\n\n this.appearTriggers\n .filter(eventName => eventName === 'intersect')\n .forEach(() => {\n this.appearanceIntersectionObserver.observe(this.triggerRoot)\n })\n\n this.appearTriggers\n .filter(eventName => eventName !== 'connect' && eventName !== 'intersect')\n .forEach(eventName => {\n this.triggerRoot.addEventListener(eventName, this.appear.bind(this))\n })\n\n this.disappearTriggers\n .filter(\n eventName => eventName !== 'disconnect' && eventName !== 'intersect'\n )\n .forEach(eventName => {\n this.triggerRoot.addEventListener(eventName, this.disappear.bind(this))\n })\n\n this.disappearTriggers\n .filter(eventName => eventName === 'intersect')\n .forEach(() => {})\n }\n\n uninstall () {\n this.appearTriggers\n .filter(eventName => eventName === 'intersect')\n .forEach(() => {\n this.appearanceIntersectionObserver.unobserve(this.triggerRoot)\n })\n\n this.appearTriggers\n .filter(eventName => eventName !== 'connect' && eventName !== 'intersect')\n .forEach(eventName => {\n this.triggerRoot.removeEventListener(eventName, this.appear.bind(this))\n })\n\n this.disappearTriggers\n .filter(eventName => eventName === 'intersect')\n .forEach(() => {})\n\n this.disappearTriggers\n .filter(\n eventName => eventName !== 'disconnect' && eventName !== 'intersect'\n )\n .forEach(eventName => {\n this.triggerRoot.removeEventListener(\n eventName,\n this.disappear.bind(this)\n )\n })\n }\n\n appear () {\n if (this.channel) {\n this.present = true\n this.channel.perform('appear')\n }\n }\n\n disappear () {\n if (this.channel) {\n this.present = false\n this.channel.perform('disappear')\n }\n }\n\n performOperations (data) {\n if (data.cableReady) {\n CableReady.perform(data.operations)\n }\n }\n\n createSubscription () {\n if (!this.consumer) {\n console.error(\n 'The `cubicle-element` helper cannot connect without an ActionCable consumer.'\n )\n return\n }\n\n return this.consumer.subscriptions.create(\n {\n channel: this.channelName,\n identifier: this.getAttribute('identifier'),\n element_id: this.id,\n scope: this.getAttribute('scope'),\n exclude_current_user:\n this.getAttribute('exclude-current-user') === 'true'\n },\n {\n connected: () => {\n this.install()\n },\n disconnected: () => {\n this.disappear()\n this.uninstall()\n },\n rejected: () => {\n this.uninstall()\n },\n received: this.performOperations.bind(this)\n }\n )\n }\n\n get channelName () {\n return 'Cubism::PresenceChannel'\n }\n}\n","import { inputTags, textInputTypes } from './enums'\nimport ActiveElement from './active_element'\n\n// Indicates if the passed element is considered a text input.\n//\nconst isTextInput = element => {\n return inputTags[element.tagName] && textInputTypes[element.type]\n}\n\n// Assigns focus to the appropriate element... preferring the explicitly passed selector\n//\n// * selector - a CSS selector for the element that should have focus\n//\nconst assignFocus = selector => {\n const element =\n selector && selector.nodeType === Node.ELEMENT_NODE\n ? selector\n : document.querySelector(selector)\n const focusElement = element || ActiveElement.element\n if (focusElement && focusElement.focus) focusElement.focus()\n}\n\n// Dispatches an event on the passed element\n//\n// * element - the element\n// * name - the name of the event\n// * detail - the event detail\n//\nconst dispatch = (element, name, detail = {}) => {\n const init = { bubbles: true, cancelable: true, detail }\n const event = new CustomEvent(name, init)\n element.dispatchEvent(event)\n if (window.jQuery) window.jQuery(element).trigger(name, detail)\n}\n\n// Accepts an xPath query and returns the element found at that position in the DOM\n//\nconst xpathToElement = xpath => {\n return document.evaluate(\n xpath,\n document,\n null,\n XPathResult.FIRST_ORDERED_NODE_TYPE,\n null\n ).singleNodeValue\n}\n\n// Accepts an xPath query and returns all matching elements in the DOM\n//\nconst xpathToElementArray = (xpath, reverse = false) => {\n const snapshotList = document.evaluate(\n xpath,\n document,\n null,\n XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,\n null\n )\n const snapshots = []\n\n for (let i = 0; i < snapshotList.snapshotLength; i++) {\n snapshots.push(snapshotList.snapshotItem(i))\n }\n\n return reverse ? snapshots.reverse() : snapshots\n}\n\n// Return an array with the class names to be used\n//\n// * names - could be a string or an array of strings for multiple classes.\n//\nconst getClassNames = names => Array.from(names).flat()\n\n// Perform operation for either the first or all of the elements returned by CSS selector\n//\n// * operation - the instruction payload from perform\n// * callback - the operation function to run for each element\n//\nconst processElements = (operation, callback) => {\n Array.from(\n operation.selectAll ? operation.element : [operation.element]\n ).forEach(callback)\n}\n\n// convert string to kebab-case\n// most other implementations (lodash) are focused on camelCase to kebab-case\n// instead, this uses word token boundaries to produce readable URL slugs and keys\n// this implementation will not support Emoji or other non-ASCII characters\n//\nconst kebabize = createCompounder(function (result, word, index) {\n return result + (index ? '-' : '') + word.toLowerCase()\n})\n\nfunction createCompounder (callback) {\n return function (str) {\n return words(str).reduce(callback, '')\n }\n}\n\nconst words = str => {\n str = str == null ? '' : str\n return str.match(/([A-Z]{2,}|[0-9]+|[A-Z]?[a-z]+|[A-Z])/g) || []\n}\n\n// Provide a standardized pipeline of checks and modifications to all operations based on provided options\n// Currently skips execution if cancelled and implements an optional delay\n//\nconst operate = (operation, callback) => {\n if (!operation.cancel) {\n operation.delay ? setTimeout(callback, operation.delay) : callback()\n return true\n }\n return false\n}\n\n// Dispatch life-cycle events with standardized naming\nconst before = (target, operation) =>\n dispatch(\n target,\n `cable-ready:before-${kebabize(operation.operation)}`,\n operation\n )\n\nconst after = (target, operation) =>\n dispatch(\n target,\n `cable-ready:after-${kebabize(operation.operation)}`,\n operation\n )\n\nfunction debounce (fn, delay = 250) {\n let timer\n return (...args) => {\n const callback = () => fn.apply(this, args)\n if (timer) clearTimeout(timer)\n timer = setTimeout(callback, delay)\n }\n}\n\nfunction handleErrors (response) {\n if (!response.ok) throw Error(response.statusText)\n return response\n}\n\nfunction safeScalar (val) {\n if (\n val !== undefined &&\n !['string', 'number', 'boolean'].includes(typeof val)\n )\n console.warn(\n `Operation expects a string, number or boolean, but got ${val} (${typeof val})`\n )\n return val != null ? val : ''\n}\n\nfunction safeString (str) {\n if (str !== undefined && typeof str !== 'string')\n console.warn(`Operation expects a string, but got ${str} (${typeof str})`)\n\n return str != null ? String(str) : ''\n}\n\nfunction safeArray (arr) {\n if (arr !== undefined && !Array.isArray(arr))\n console.warn(`Operation expects an array, but got ${arr} (${typeof arr})`)\n return arr != null ? Array.from(arr) : []\n}\n\nfunction safeObject (obj) {\n if (obj !== undefined && typeof obj !== 'object')\n console.warn(`Operation expects an object, but got ${obj} (${typeof obj})`)\n return obj != null ? Object(obj) : {}\n}\n\nfunction safeStringOrArray (elem) {\n if (elem !== undefined && !Array.isArray(elem) && typeof elem !== 'string')\n console.warn(`Operation expects an Array or a String, but got ${elem} (${typeof elem})`)\n\n return elem == null ? '' : Array.isArray(elem) ? Array.from(elem) : String(elem)\n}\n\nfunction fragmentToString (fragment) {\n return new XMLSerializer().serializeToString(fragment)\n}\n\n// A proxy method to wrap a fetch call in error handling\n//\n// * url - the URL to fetch\n// * additionalHeaders - an object of additional headers passed to fetch\n//\nasync function graciouslyFetch (url, additionalHeaders) {\n try {\n const response = await fetch(url, {\n headers: {\n 'X-REQUESTED-WITH': 'XmlHttpRequest',\n ...additionalHeaders\n }\n })\n if (response == undefined) return\n\n handleErrors(response)\n\n return response\n } catch (e) {\n console.error(`Could not fetch ${url}`)\n }\n}\n\nexport {\n isTextInput,\n assignFocus,\n dispatch,\n xpathToElement,\n xpathToElementArray,\n getClassNames,\n processElements,\n operate,\n before,\n after,\n debounce,\n handleErrors,\n graciouslyFetch,\n kebabize,\n safeScalar,\n safeString,\n safeArray,\n safeObject,\n safeStringOrArray,\n fragmentToString\n}\n","/* global customElements */\n\nimport { Cubicle } from './cubicle'\n\nexport * from './cubicle'\n\ncustomElements.define('cubicle-element', Cubicle)\n"],"names":["Cubicle","SubscribingElement","constructor","super","this","attachShadow","mode","innerHTML","triggerRoot","present","async","preview","appear","fn","delay","timer","args","clearTimeout","setTimeout","apply","debounce","bind","appearTriggers","getAttribute","split","disappearTriggers","triggerRootSelector","consumer","CableReady","console","log","channel","createSubscription","appearanceIntersectionObserver","IntersectionObserver","entries","observer","forEach","entry","target","isIntersecting","disappear","threshold","mutationObserver","MutationObserver","mutationsList","mutation","root","document","querySelector","uninstall","install","disconnect","observe","subtree","childList","disconnectedCallback","includes","filter","eventName","addEventListener","unobserve","removeEventListener","perform","performOperations","data","cableReady","operations","subscriptions","create","channelName","identifier","element_id","id","scope","exclude_current_user","connected","disconnected","rejected","received","error","customElements","define"],"mappings":"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"}
data/bin/rails ADDED
@@ -0,0 +1,14 @@
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/cubism/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/all"
14
+ require "rails/engine/commands"
data/bin/standardize ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+
3
+ bundle exec standardrb --fix
4
+ yarn format
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/cubism.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ require_relative "lib/cubism/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "cubism"
5
+ spec.version = Cubism::VERSION
6
+ spec.authors = ["Julian Rubisch"]
7
+ spec.email = ["julian@julianrubisch.at"]
8
+ spec.homepage = "https://github.com/julianrubisch/cubism"
9
+ spec.summary = "Lightweight Resource-Based Presence Solution with CableReady"
10
+ spec.description = "Lightweight Resource-Based Presence Solution with CableReady"
11
+ spec.license = "MIT"
12
+
13
+ spec.metadata["homepage_uri"] = spec.homepage
14
+ spec.metadata["source_code_uri"] = "https://github.com/julianrubisch/cubism.git"
15
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
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_dependency "rails", ">= 6.0"
28
+ spec.add_dependency "kredis", ">= 0.4"
29
+ spec.add_dependency "cable_ready", "= 5.0.0.rc1"
30
+
31
+ spec.add_development_dependency "standard"
32
+ spec.add_development_dependency "nokogiri"
33
+ spec.add_development_dependency "mocha"
34
+ spec.add_development_dependency "appraisal"
35
+ spec.add_development_dependency "sqlite3"
36
+ end
data/cubism.gemspec~ ADDED
@@ -0,0 +1,36 @@
1
+ require_relative "lib/cubism/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "cubism"
5
+ spec.version = Cubism::VERSION
6
+ spec.authors = ["Julian Rubisch"]
7
+ spec.email = ["julian@julianrubisch.at"]
8
+ spec.homepage = "https://github.com/julianrubisch/cubism"
9
+ spec.summary = "Lightweight Resource-Based Presence Solution with CableReady"
10
+ spec.description = "Lightweight Resource-Based Presence Solution with CableReady"
11
+ spec.license = "MIT"
12
+
13
+ spec.metadata["homepage_uri"] = spec.homepage
14
+ spec.metadata["source_code_uri"] = "https://github.com/julianrubisch/cubism.git"
15
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
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_dependency "rails", ">= 6.0"
28
+ spec.add_dependency "kredis", ">= 0.4"
29
+ spec.add_dependency "cable_ready", "= 5.0.0.pre9"
30
+
31
+ spec.add_development_dependency "standard"
32
+ spec.add_development_dependency "nokogiri"
33
+ spec.add_development_dependency "mocha"
34
+ spec.add_development_dependency "appraisal"
35
+ spec.add_development_dependency "sqlite3"
36
+ end
data/lib/cubism/engine.rb CHANGED
@@ -4,5 +4,25 @@ module Cubism
4
4
  Cubism.block_store = Cubism::CubicleStore.new("cubism-blocks")
5
5
  Cubism.source_store = Cubism::CubicleStore.new("cubism-source")
6
6
  end
7
+
8
+ initializer "cubism.assets" do |app|
9
+ if app.config.respond_to?(:assets)
10
+ app.config.assets.precompile += %w[
11
+ cubism.js
12
+ cubism.min.js
13
+ cubism.min.js.map
14
+ cubism.umd.js
15
+ cubism.umd.min.js
16
+ cubism.umd.min.js.map
17
+ ]
18
+ end
19
+ end
20
+
21
+ initializer "cubism.importmap", before: "importmap" do |app|
22
+ if app.config.respond_to?(:importmap)
23
+ app.config.importmap.paths << Engine.root.join("lib/cubism/importmap.rb")
24
+ app.config.importmap.cache_sweepers << Engine.root.join("app/assets/javascripts")
25
+ end
26
+ end
7
27
  end
8
28
  end
@@ -0,0 +1,2 @@
1
+ pin "cable_ready", to: "cable_ready.min.js", preload: true
2
+ pin "cubism", to: "cubism.min.js", preload: true
@@ -1,3 +1,3 @@
1
1
  module Cubism
2
- VERSION = "0.1.0.pre12"
2
+ VERSION = "0.1.0.rc1"
3
3
  end
data/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@minthesize/cubism",
3
+ "version": "0.1.0-rc1",
4
+ "description": "Lightweight Resource-Based Presence Solution with CableReady.",
5
+ "main": "./dist/cubism.umd.min.js",
6
+ "module": "./dist/cubism.min.js",
7
+ "files": [
8
+ "dist/*",
9
+ "javascript/*"
10
+ ],
11
+ "scripts": {
12
+ "lint": "yarn run prettier-standard:check",
13
+ "format": "yarn run prettier-standard:format",
14
+ "prettier-standard:check": "yarn run prettier-standard --check ./javascript/**/*.js rollup.config.js",
15
+ "prettier-standard:format": "yarn run prettier-standard ./javascript/**/*.js rollup.config.js",
16
+ "build": "yarn rollup -c",
17
+ "watch": "yarn rollup -wc"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/julianrubisch/cubism.git"
22
+ },
23
+ "keywords": [
24
+ "presence"
25
+ ],
26
+ "author": "Julian Rubisch",
27
+ "license": "MIT",
28
+ "bugs": {
29
+ "url": "https://github.com/julianrubisch/cubism/issues"
30
+ },
31
+ "homepage": "https://github.com/julianrubisch/cubism#readme",
32
+ "devDependencies": {
33
+ "@rollup/plugin-commonjs": "^21.0.3",
34
+ "@rollup/plugin-json": "^4.1.0",
35
+ "@rollup/plugin-node-resolve": "^13.1.3",
36
+ "prettier-standard": "^16.4.1",
37
+ "rollup": "^2.70.1",
38
+ "rollup-plugin-terser": "^7.0.2"
39
+ },
40
+ "dependencies": {
41
+ "cable_ready": "^5.0.0-rc1"
42
+ }
43
+ }
data/package.json~ ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@minthesize/cubism",
3
+ "version": "0.1.0-pre13",
4
+ "description": "Lightweight Resource-Based Presence Solution with CableReady.",
5
+ "main": "./dist/cubism.umd.min.js",
6
+ "module": "./dist/cubism.min.js",
7
+ "files": [
8
+ "dist/*",
9
+ "javascript/*"
10
+ ],
11
+ "scripts": {
12
+ "lint": "yarn run prettier-standard:check",
13
+ "format": "yarn run prettier-standard:format",
14
+ "prettier-standard:check": "yarn run prettier-standard --check ./javascript/**/*.js rollup.config.js",
15
+ "prettier-standard:format": "yarn run prettier-standard ./javascript/**/*.js rollup.config.js",
16
+ "build": "yarn rollup -c",
17
+ "watch": "yarn rollup -wc"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/julianrubisch/cubism.git"
22
+ },
23
+ "keywords": [
24
+ "presence"
25
+ ],
26
+ "author": "Julian Rubisch",
27
+ "license": "MIT",
28
+ "bugs": {
29
+ "url": "https://github.com/julianrubisch/cubism/issues"
30
+ },
31
+ "homepage": "https://github.com/julianrubisch/cubism#readme",
32
+ "devDependencies": {
33
+ "@rollup/plugin-commonjs": "^21.0.3",
34
+ "@rollup/plugin-json": "^4.1.0",
35
+ "@rollup/plugin-node-resolve": "^13.1.3",
36
+ "prettier-standard": "^16.4.1",
37
+ "rollup": "^2.70.1",
38
+ "rollup-plugin-terser": "^7.0.2"
39
+ },
40
+ "dependencies": {
41
+ "cable_ready": "^5.0.0-pre9"
42
+ }
43
+ }