cable_ready 5.0.0.pre8 → 5.0.0.pre9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("morphdom")):"function"==typeof define&&define.amd?define(["exports","morphdom"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).CableReady={},e.morphdom)}(this,(function(e,t){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=n(t),r="5.0.0-pre9";const s={INPUT:!0,TEXTAREA:!0,SELECT:!0},a={INPUT:!0,TEXTAREA:!0,OPTION:!0},i={"datetime-local":!0,"select-multiple":!0,"select-one":!0,color:!0,date:!0,datetime:!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,textarea:!0,time:!0,url:!0,week:!0};let l;var c={get element(){return l},set(e){l=e}};const u=e=>s[e.tagName]&&i[e.type],d=e=>{const t=(e&&e.nodeType===Node.ELEMENT_NODE?e:document.querySelector(e))||c.element;t&&t.focus&&t.focus()},m=(e,t,n={})=>{const o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n});e.dispatchEvent(o),window.jQuery&&window.jQuery(e).trigger(t,n)},h=e=>document.evaluate(e,document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue,p=e=>Array(e).flat(),f=(e,t)=>{Array.from(e.selectAll?e.element:[e.element]).forEach(t)},b=e=>e.split("").map(((e,t)=>e.toUpperCase()===e?`${0!==t?"-":""}${e.toLowerCase()}`:e)).join(""),y=(e,t)=>!e.cancel&&(e.delay?setTimeout(t,e.delay):t(),!0),g=(e,t)=>m(e,`cable-ready:before-${b(t.operation)}`,t),w=(e,t)=>m(e,`cable-ready:after-${b(t.operation)}`,t);function E(e,t){let n;return(...o)=>{clearTimeout(n),n=setTimeout((()=>e.apply(this,o)),t)}}function v(e){if(!e.ok)throw Error(e.statusText);return e}async function T(e,t){try{const n=await fetch(e,{headers:{"X-REQUESTED-WITH":"XmlHttpRequest",...t}});if(null==n)return;return v(n),n}catch(t){console.error(`Could not fetch ${e}`)}}var S=Object.freeze({__proto__:null,isTextInput:u,assignFocus:d,dispatch:m,xpathToElement:h,getClassNames:p,processElements:f,operate:y,before:g,after:w,debounce:E,handleErrors:v,graciouslyFetch:T,kebabize:b});const A=e=>(t,n)=>!k.map((o=>"function"!=typeof o||o(e,t,n))).includes(!1),C=e=>t=>{L.forEach((n=>{"function"==typeof n&&n(e,t)}))},x=(e,t,n)=>!(!a[t.tagName]&&t.isEqualNode(n)),M=(e,t,n)=>t!==c.element||!t.isContentEditable,O=(e,t,n)=>{const{permanentAttributeName:o}=e;if(!o)return!0;const r=t.closest(`[${o}]`);if(!r&&t===c.element&&u(t)){const e={value:!0};return Array.from(n.attributes).forEach((n=>{e[n.name]||t.setAttribute(n.name,n.value)})),!1}return!r},k=[x,O,M],L=[];var R=Object.freeze({__proto__:null,shouldMorphCallbacks:k,didMorphCallbacks:L,shouldMorph:A,didMorph:C,verifyNotMutable:x,verifyNotContentEditable:M,verifyNotPermanent:O}),P={append:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{html:n,focusSelector:o}=e;t.insertAdjacentHTML("beforeend",n||""),d(o)})),w(t,e)}))},graft:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{parent:n,focusSelector:o}=e,r=document.querySelector(n);r&&(r.appendChild(t),d(o))})),w(t,e)}))},innerHtml:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{html:n,focusSelector:o}=e;t.innerHTML=n||"",d(o)})),w(t,e)}))},insertAdjacentHtml:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{html:n,position:o,focusSelector:r}=e;t.insertAdjacentHTML(o||"beforeend",n||""),d(r)})),w(t,e)}))},insertAdjacentText:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{text:n,position:o,focusSelector:r}=e;t.insertAdjacentText(o||"beforeend",n||""),d(r)})),w(t,e)}))},morph:e=>{f(e,(t=>{const{html:n}=e,r=document.createElement("template");r.innerHTML=String(n).trim(),e.content=r.content;const s=t.parentElement,a=Array.from(s.children).indexOf(t);g(t,e),y(e,(()=>{const{childrenOnly:n,focusSelector:s}=e;o.default(t,n?r.content:r.innerHTML,{childrenOnly:!!n,onBeforeElUpdated:A(e),onElUpdated:C(e)}),d(s)})),w(s.children[a],e)}))},outerHtml:e=>{f(e,(t=>{const n=t.parentElement,o=Array.from(n.children).indexOf(t);g(t,e),y(e,(()=>{const{html:n,focusSelector:o}=e;t.outerHTML=n||"",d(o)})),w(n.children[o],e)}))},prepend:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{html:n,focusSelector:o}=e;t.insertAdjacentHTML("afterbegin",n||""),d(o)})),w(t,e)}))},remove:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{focusSelector:n}=e;t.remove(),d(n)})),w(document,e)}))},replace:e=>{f(e,(t=>{const n=t.parentElement,o=Array.from(n.children).indexOf(t);g(t,e),y(e,(()=>{const{html:n,focusSelector:o}=e;t.outerHTML=n||"",d(o)})),w(n.children[o],e)}))},textContent:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{text:n,focusSelector:o}=e;t.textContent=null!=n?n:"",d(o)})),w(t,e)}))},addCssClass:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{name:n}=e;t.classList.add(...p(n||""))})),w(t,e)}))},removeAttribute:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{name:n}=e;t.removeAttribute(n)})),w(t,e)}))},removeCssClass:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{name:n}=e;t.classList.remove(...p(n))})),w(t,e)}))},setAttribute:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{name:n,value:o}=e;t.setAttribute(n,o||"")})),w(t,e)}))},setDatasetProperty:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{name:n,value:o}=e;t.dataset[n]=o||""})),w(t,e)}))},setProperty:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{name:n,value:o}=e;n in t&&(t[n]=o||"")})),w(t,e)}))},setStyle:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{name:n,value:o}=e;t.style[n]=o||""})),w(t,e)}))},setStyles:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{styles:n}=e;for(let[e,o]of Object.entries(n))t.style[e]=o||""})),w(t,e)}))},setValue:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{value:n}=e;t.value=n||""})),w(t,e)}))},dispatchEvent:e=>{f(e,(t=>{g(t,e),y(e,(()=>{const{name:n,detail:o}=e;m(t,n,o)})),w(t,e)}))},setMeta:e=>{g(document,e),y(e,(()=>{const{name:t,content:n}=e;let o=document.head.querySelector(`meta[name='${t}']`);o||(o=document.createElement("meta"),o.name=t,document.head.appendChild(o)),o.content=n})),w(document,e)},clearStorage:e=>{g(document,e),y(e,(()=>{const{type:t}=e;("session"===t?sessionStorage:localStorage).clear()})),w(document,e)},go:e=>{g(window,e),y(e,(()=>{const{delta:t}=e;history.go(t)})),w(window,e)},pushState:e=>{g(window,e),y(e,(()=>{const{state:t,title:n,url:o}=e;history.pushState(t||{},n||"",o)})),w(window,e)},redirectTo:e=>{g(window,e),y(e,(()=>{let{url:t,action:n}=e;n=n||"advance",window.Turbo&&window.Turbo.visit(t,{action:n}),window.Turbolinks&&window.Turbolinks.visit(t,{action:n}),window.Turbo||window.Turbolinks||(window.location.href=t)})),w(window,e)},reload:e=>{g(window,e),y(e,(()=>{window.location.reload()})),w(window,e)},removeStorageItem:e=>{g(document,e),y(e,(()=>{const{key:t,type:n}=e;("session"===n?sessionStorage:localStorage).removeItem(t)})),w(document,e)},replaceState:e=>{g(window,e),y(e,(()=>{const{state:t,title:n,url:o}=e;history.replaceState(t||{},n||"",o)})),w(window,e)},scrollIntoView:e=>{const{element:t}=e;g(t,e),y(e,(()=>{t.scrollIntoView(e)})),w(t,e)},setCookie:e=>{g(document,e),y(e,(()=>{const{cookie:t}=e;document.cookie=t||""})),w(document,e)},setFocus:e=>{const{element:t}=e;g(t,e),y(e,(()=>{d(t)})),w(t,e)},setStorageItem:e=>{g(document,e),y(e,(()=>{const{key:t,value:n,type:o}=e;("session"===o?sessionStorage:localStorage).setItem(t,n||"")})),w(document,e)},consoleLog:e=>{g(document,e),y(e,(()=>{const{message:t,level:n}=e;n&&["warn","info","error"].includes(n)?console[n](t||""):console.log(t||"")})),w(document,e)},consoleTable:e=>{g(document,e),y(e,(()=>{const{data:t,columns:n}=e;console.table(t,n||[])})),w(document,e)},notification:e=>{g(document,e),y(e,(()=>{const{title:t,options:n}=e;Notification.requestPermission().then((o=>{e.permission=o,"granted"===o&&new Notification(t||"",n)}))})),w(document,e)}};let q=P;const H=e=>{q={...q,...e}};var N={get all(){return q}};const U=(e,t={emitMissingElementWarnings:!0})=>{const n={};e.forEach((e=>{e.batch&&(n[e.batch]=n[e.batch]?++n[e.batch]:1)})),e.forEach((e=>{const o=e.operation;try{if(e.selector?e.element=e.xpath?h(e.selector):document[e.selectAll?"querySelectorAll":"querySelector"](e.selector):e.element=document,e.element||t.emitMissingElementWarnings){c.set(document.activeElement);const t=N.all[o];t?(t(e),e.batch&&0==--n[e.batch]&&m(document,"cable-ready:batch-complete",{batch:e.batch})):console.error(`CableReady couldn't find the "${o}" operation. Make sure you use the camelized form when calling an operation method.`)}}catch(t){e.element?(console.error(`CableReady detected an error in ${o||"operation"}: ${t.message}. If you need to support older browsers make sure you've included the corresponding polyfills. https://docs.stimulusreflex.com/setup#polyfills-for-ie11.`),console.error(t)):console.warn(`CableReady ${o||"operation"} failed due to missing DOM element for selector: '${e.selector}'`)}}))};let _;const I=[25,50,75,100,200,250,500,800,1e3,2e3],$=async(e=0)=>{if(_)return _;if(e>=I.length)throw new Error("Couldn't obtain a Action Cable consumer within 5s");var t;return await(t=I[e],new Promise((e=>setTimeout(e,t)))),await $(e+1)};var j={setConsumer(e){_=e},get consumer(){return _},getConsumer:async()=>await $()};class F extends HTMLElement{disconnectedCallback(){this.channel&&this.channel.unsubscribe()}createSubscription(e,t,n){this.channel=e.subscriptions.create({channel:t,identifier:this.identifier},{received:n})}get preview(){return document.documentElement.hasAttribute("data-turbolinks-preview")||document.documentElement.hasAttribute("data-turbo-preview")}get identifier(){return this.getAttribute("identifier")}}class D extends F{async connectedCallback(){if(this.preview)return;const e=await j.getConsumer();e?this.createSubscription(e,"CableReady::Stream",this.performOperations):console.error("The `stream_from` helper cannot connect without an ActionCable consumer.\nPlease run `rails generate cable_ready:helpers` to fix this.")}performOperations(e){e.cableReady&&U(e.operations)}}class z extends F{constructor(){super();this.attachShadow({mode:"open"}).innerHTML="\n<style>\n :host {\n display: block;\n }\n</style>\n<slot></slot>\n"}async connectedCallback(){if(this.preview)return;this.update=E(this.update.bind(this),this.debounce);const e=await j.getConsumer();e?this.createSubscription(e,"CableReady::Stream",this.update):console.error("The `updates-for` helper cannot connect without an ActionCable consumer.\nPlease run `rails generate cable_ready:helpers` to fix this.")}async update(e){const t=Array.from(document.querySelectorAll(this.query),(e=>new X(e)));if(t[0].element!==this)return;c.set(document.activeElement),this.html={};const n=[...new Set(t.map((e=>e.url)))];await Promise.all(n.map((async e=>{if(!this.html.hasOwnProperty(e)){const t=await T(e,{"X-Cable-Ready":"update"});this.html[e]=await t.text()}}))),this.index={},t.forEach((t=>{this.index.hasOwnProperty(t.url)?this.index[t.url]++:this.index[t.url]=0,t.process(e,this.html,this.index)}))}get query(){return`updates-for[identifier="${this.identifier}"]`}get identifier(){return this.getAttribute("identifier")}get debounce(){return this.hasAttribute("debounce")?parseInt(this.getAttribute("debounce")):20}}class X{constructor(e){this.element=e}async process(e,t,n){if(!this.shouldUpdate(e))return;const r=n[this.url],s=document.createElement("template");this.element.setAttribute("updating","updating"),s.innerHTML=String(t[this.url]).trim(),await this.resolveTurboFrames(s.content);const a=s.content.querySelectorAll(this.query);if(a.length<=r)return void console.warn(`Update aborted due to insufficient number of elements. The offending url is ${this.url}.`);const i={element:this.element,html:a[r],permanentAttributeName:"data-ignore-updates"};m(this.element,"cable-ready:before-update",i),o.default(this.element,a[r],{childrenOnly:!0,onBeforeElUpdated:A(i),onElUpdated:e=>{this.element.removeAttribute("updating"),m(this.element,"cable-ready:after-update",i),d(i.focusSelector)}})}async resolveTurboFrames(e){const t=[...e.querySelectorAll('turbo-frame[src]:not([loading="lazy"])')];return Promise.all(t.map((t=>new Promise((async n=>{const o=await T(t.getAttribute("src"),{"Turbo-Frame":t.id,"X-Cable-Ready":"update"}),r=document.createElement("template");r.innerHTML=await o.text(),await this.resolveTurboFrames(r.content),e.querySelector(`turbo-frame#${t.id}`).innerHTML=String(r.content.querySelector(`turbo-frame#${t.id}`).innerHTML).trim(),n()})))))}shouldUpdate(e){return!this.ignoresInnerUpdates&&this.hasChangesSelectedForUpdate(e)}hasChangesSelectedForUpdate(e){const t=this.element.getAttribute("only");return!(t&&e.changed&&!t.split(" ").some((t=>e.changed.includes(t))))}get ignoresInnerUpdates(){return this.element.hasAttribute("ignore-inner-updates")&&this.element.hasAttribute("performing-inner-update")}get url(){return this.element.hasAttribute("url")?this.element.getAttribute("url"):location.href}get identifier(){return this.element.identifier}get query(){return this.element.query}}const V=e=>{const t=e&&e.parentElement.closest("updates-for");t&&(t.setAttribute("performing-inner-update",""),V(t))},W=e=>{const t=e&&e.parentElement.closest("updates-for");t&&(t.removeAttribute("performing-inner-update"),W(t))},Q={perform:U,performAsync:(e,t={emitMissingElementWarnings:!0})=>new Promise(((n,o)=>{try{n(U(e,t))}catch(e){o(e)}})),shouldMorphCallbacks:k,didMorphCallbacks:L,initialize:(e={})=>{const{consumer:t}=e;document.addEventListener("stimulus-reflex:before",(e=>{V(e.detail.element)})),document.addEventListener("stimulus-reflex:after",(e=>{setTimeout((()=>{W(e.detail.element)}))})),document.addEventListener("turbo:submit-start",(e=>{V(e.target)})),document.addEventListener("turbo:submit-end",(e=>{setTimeout((()=>{W(e.target)}))})),t?j.setConsumer(t):console.error("CableReady requires a reference to your Action Cable `consumer` for its helpers to function.\nEnsure that you have imported the `CableReady` package as well as `consumer` from your `channels` folder, then call `CableReady.initialize({ consumer })`."),customElements.get("stream-from")||customElements.define("stream-from",D),customElements.get("updates-for")||customElements.define("updates-for",z)},addOperation:(e,t)=>{const n={};n[e]=t,H(n)},addOperations:e=>{H(e)},version:r,cable:j,get DOMOperations(){return console.warn("DEPRECATED: Please use `CableReady.operations` instead of `CableReady.DOMOperations`"),N.all},get operations(){return N.all},get consumer(){return j.consumer}};window.CableReady=Q,e.MorphCallbacks=R,e.StreamFromElement=D,e.SubscribingElement=F,e.UpdatesForElement=z,e.Utils=S,e.default=Q,Object.defineProperty(e,"__esModule",{value:!0})}));
2
+ //# sourceMappingURL=cable_ready.umd.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cable_ready.umd.min.js","sources":["../../../javascript/enums.js","../../../javascript/active_element.js","../../../javascript/utils.js","../../../javascript/morph_callbacks.js","../../../javascript/operations.js","../../../javascript/operation_store.js","../../../javascript/cable_ready.js","../../../javascript/cable_consumer.js","../../../javascript/elements/subscribing_element.js","../../../javascript/elements/stream_from_element.js","../../../javascript/elements/updates_for_element.js","../../../javascript/updatable/inner_updates_compat.js","../../../javascript/index.js","../../../javascript/elements/index.js"],"sourcesContent":["export const inputTags = {\n INPUT: true,\n TEXTAREA: true,\n SELECT: true\n}\n\nexport const mutableTags = {\n INPUT: true,\n TEXTAREA: true,\n OPTION: true\n}\n\nexport const textInputTypes = {\n 'datetime-local': true,\n 'select-multiple': true,\n 'select-one': true,\n color: true,\n date: true,\n datetime: true,\n email: true,\n month: true,\n number: true,\n password: true,\n range: true,\n search: true,\n tel: true,\n text: true,\n textarea: true,\n time: true,\n url: true,\n week: true\n}\n","let activeElement\n\nexport default {\n get element () {\n return activeElement\n },\n set (element) {\n activeElement = element\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: detail }\n const evt = new CustomEvent(name, init)\n element.dispatchEvent(evt)\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// 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(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// camelCase to kebab-case\n//\nconst kebabize = str => {\n return str\n .split('')\n .map((letter, idx) => {\n return letter.toUpperCase() === letter\n ? `${idx !== 0 ? '-' : ''}${letter.toLowerCase()}`\n : letter\n })\n .join('')\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 (func, timeout) {\n let timer\n return (...args) => {\n clearTimeout(timer)\n timer = setTimeout(() => func.apply(this, args), timeout)\n }\n}\n\nfunction handleErrors (response) {\n if (!response.ok) throw Error(response.statusText)\n return response\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 getClassNames,\n processElements,\n operate,\n before,\n after,\n debounce,\n handleErrors,\n graciouslyFetch,\n kebabize\n}\n","import { mutableTags } from './enums'\nimport { isTextInput } from './utils'\nimport ActiveElement from './active_element'\n\n// Indicates whether or not we should morph an element via onBeforeElUpdated callback\n// SEE: https://github.com/patrick-steele-idem/morphdom#morphdomfromnode-tonode-options--node\n//\nconst shouldMorph = operation => (fromEl, toEl) => {\n return !shouldMorphCallbacks\n .map(callback => {\n return typeof callback === 'function'\n ? callback(operation, fromEl, toEl)\n : true\n })\n .includes(false)\n}\n\n// Execute any pluggable functions that modify elements after morphing via onElUpdated callback\n//\nconst didMorph = operation => el => {\n didMorphCallbacks.forEach(callback => {\n if (typeof callback === 'function') callback(operation, el)\n })\n}\n\nconst verifyNotMutable = (detail, fromEl, toEl) => {\n // Skip nodes that are equal:\n // https://github.com/patrick-steele-idem/morphdom#can-i-make-morphdom-blaze-through-the-dom-tree-even-faster-yes\n if (!mutableTags[fromEl.tagName] && fromEl.isEqualNode(toEl)) return false\n return true\n}\n\nconst verifyNotContentEditable = (detail, fromEl, toEl) => {\n if (fromEl === ActiveElement.element && fromEl.isContentEditable) return false\n return true\n}\n\nconst verifyNotPermanent = (detail, fromEl, toEl) => {\n const { permanentAttributeName } = detail\n if (!permanentAttributeName) return true\n\n const permanent = fromEl.closest(`[${permanentAttributeName}]`)\n\n // only morph attributes on the active non-permanent text input\n if (!permanent && fromEl === ActiveElement.element && isTextInput(fromEl)) {\n const ignore = { value: true }\n Array.from(toEl.attributes).forEach(attribute => {\n if (!ignore[attribute.name])\n fromEl.setAttribute(attribute.name, attribute.value)\n })\n return false\n }\n\n return !permanent\n}\n\nconst shouldMorphCallbacks = [\n verifyNotMutable,\n verifyNotPermanent,\n verifyNotContentEditable\n]\nconst didMorphCallbacks = []\n\nexport {\n shouldMorphCallbacks,\n didMorphCallbacks,\n shouldMorph,\n didMorph,\n verifyNotMutable,\n verifyNotContentEditable,\n verifyNotPermanent\n}\n","import morphdom from 'morphdom'\nimport { shouldMorph, didMorph } from './morph_callbacks'\nimport {\n assignFocus,\n dispatch,\n getClassNames,\n processElements,\n before,\n after,\n operate\n} from './utils'\n\nexport default {\n // DOM Mutations\n\n append: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { html, focusSelector } = operation\n element.insertAdjacentHTML('beforeend', html || '')\n assignFocus(focusSelector)\n })\n after(element, operation)\n })\n },\n\n graft: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { parent, focusSelector } = operation\n const parentElement = document.querySelector(parent)\n if (parentElement) {\n parentElement.appendChild(element)\n assignFocus(focusSelector)\n }\n })\n after(element, operation)\n })\n },\n\n innerHtml: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { html, focusSelector } = operation\n element.innerHTML = html || ''\n assignFocus(focusSelector)\n })\n after(element, operation)\n })\n },\n\n insertAdjacentHtml: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { html, position, focusSelector } = operation\n element.insertAdjacentHTML(position || 'beforeend', html || '')\n assignFocus(focusSelector)\n })\n after(element, operation)\n })\n },\n\n insertAdjacentText: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { text, position, focusSelector } = operation\n element.insertAdjacentText(position || 'beforeend', text || '')\n assignFocus(focusSelector)\n })\n after(element, operation)\n })\n },\n\n morph: operation => {\n processElements(operation, element => {\n const { html } = operation\n const template = document.createElement('template')\n template.innerHTML = String(html).trim()\n operation.content = template.content\n const parent = element.parentElement\n const ordinal = Array.from(parent.children).indexOf(element)\n before(element, operation)\n operate(operation, () => {\n const { childrenOnly, focusSelector } = operation\n morphdom(\n element,\n childrenOnly ? template.content : template.innerHTML,\n {\n childrenOnly: !!childrenOnly,\n onBeforeElUpdated: shouldMorph(operation),\n onElUpdated: didMorph(operation)\n }\n )\n assignFocus(focusSelector)\n })\n after(parent.children[ordinal], operation)\n })\n },\n\n outerHtml: operation => {\n processElements(operation, element => {\n const parent = element.parentElement\n const ordinal = Array.from(parent.children).indexOf(element)\n before(element, operation)\n operate(operation, () => {\n const { html, focusSelector } = operation\n element.outerHTML = html || ''\n assignFocus(focusSelector)\n })\n after(parent.children[ordinal], operation)\n })\n },\n\n prepend: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { html, focusSelector } = operation\n element.insertAdjacentHTML('afterbegin', html || '')\n assignFocus(focusSelector)\n })\n after(element, operation)\n })\n },\n\n remove: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { focusSelector } = operation\n element.remove()\n assignFocus(focusSelector)\n })\n after(document, operation)\n })\n },\n\n replace: operation => {\n processElements(operation, element => {\n const parent = element.parentElement\n const ordinal = Array.from(parent.children).indexOf(element)\n before(element, operation)\n operate(operation, () => {\n const { html, focusSelector } = operation\n element.outerHTML = html || ''\n assignFocus(focusSelector)\n })\n after(parent.children[ordinal], operation)\n })\n },\n\n textContent: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { text, focusSelector } = operation\n element.textContent = (text != null) ? text : ''\n assignFocus(focusSelector)\n })\n after(element, operation)\n })\n },\n\n // Element Property Mutations\n\n addCssClass: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { name } = operation\n element.classList.add(...getClassNames(name || ''))\n })\n after(element, operation)\n })\n },\n\n removeAttribute: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { name } = operation\n element.removeAttribute(name)\n })\n after(element, operation)\n })\n },\n\n removeCssClass: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { name } = operation\n element.classList.remove(...getClassNames(name))\n })\n after(element, operation)\n })\n },\n\n setAttribute: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { name, value } = operation\n element.setAttribute(name, value || '')\n })\n after(element, operation)\n })\n },\n\n setDatasetProperty: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { name, value } = operation\n element.dataset[name] = value || ''\n })\n after(element, operation)\n })\n },\n\n setProperty: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { name, value } = operation\n if (name in element) element[name] = value || ''\n })\n after(element, operation)\n })\n },\n\n setStyle: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { name, value } = operation\n element.style[name] = value || ''\n })\n after(element, operation)\n })\n },\n\n setStyles: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { styles } = operation\n for (let [name, value] of Object.entries(styles))\n element.style[name] = value || ''\n })\n after(element, operation)\n })\n },\n\n setValue: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { value } = operation\n element.value = value || ''\n })\n after(element, operation)\n })\n },\n\n // DOM Events\n\n dispatchEvent: operation => {\n processElements(operation, element => {\n before(element, operation)\n operate(operation, () => {\n const { name, detail } = operation\n dispatch(element, name, detail)\n })\n after(element, operation)\n })\n },\n\n setMeta: operation => {\n before(document, operation)\n operate(operation, () => {\n const { name, content } = operation\n let meta = document.head.querySelector(`meta[name='${name}']`)\n if (!meta) {\n meta = document.createElement('meta')\n meta.name = name\n document.head.appendChild(meta)\n }\n meta.content = content\n })\n after(document, operation)\n },\n\n // Browser Manipulations\n\n clearStorage: operation => {\n before(document, operation)\n operate(operation, () => {\n const { type } = operation\n const storage = type === 'session' ? sessionStorage : localStorage\n storage.clear()\n })\n after(document, operation)\n },\n\n go: operation => {\n before(window, operation)\n operate(operation, () => {\n const { delta } = operation\n history.go(delta)\n })\n after(window, operation)\n },\n\n pushState: operation => {\n before(window, operation)\n operate(operation, () => {\n const { state, title, url } = operation\n history.pushState(state || {}, title || '', url)\n })\n after(window, operation)\n },\n\n redirectTo: operation => {\n before(window, operation)\n operate(operation, () => {\n let { url, action } = operation\n action = action || 'advance'\n if (window.Turbo) window.Turbo.visit(url, { action })\n if (window.Turbolinks) window.Turbolinks.visit(url, { action })\n if (!window.Turbo && !window.Turbolinks) window.location.href = url\n })\n after(window, operation)\n },\n\n reload: operation => {\n before(window, operation)\n operate(operation, () => {\n window.location.reload()\n })\n after(window, operation)\n },\n\n removeStorageItem: operation => {\n before(document, operation)\n operate(operation, () => {\n const { key, type } = operation\n const storage = type === 'session' ? sessionStorage : localStorage\n storage.removeItem(key)\n })\n after(document, operation)\n },\n\n replaceState: operation => {\n before(window, operation)\n operate(operation, () => {\n const { state, title, url } = operation\n history.replaceState(state || {}, title || '', url)\n })\n after(window, operation)\n },\n\n scrollIntoView: operation => {\n const { element } = operation\n before(element, operation)\n operate(operation, () => {\n element.scrollIntoView(operation)\n })\n after(element, operation)\n },\n\n setCookie: operation => {\n before(document, operation)\n operate(operation, () => {\n const { cookie } = operation\n document.cookie = cookie || ''\n })\n after(document, operation)\n },\n\n setFocus: operation => {\n const { element } = operation\n before(element, operation)\n operate(operation, () => {\n assignFocus(element)\n })\n after(element, operation)\n },\n\n setStorageItem: operation => {\n before(document, operation)\n operate(operation, () => {\n const { key, value, type } = operation\n const storage = type === 'session' ? sessionStorage : localStorage\n storage.setItem(key, value || '')\n })\n after(document, operation)\n },\n\n // Notifications\n\n consoleLog: operation => {\n before(document, operation)\n operate(operation, () => {\n const { message, level } = operation\n level && ['warn', 'info', 'error'].includes(level)\n ? console[level](message || '')\n : console.log(message || '')\n })\n after(document, operation)\n },\n\n consoleTable: operation => {\n before(document, operation)\n operate(operation, () => {\n const { data, columns } = operation\n console.table(data, columns || [])\n })\n after(document, operation)\n },\n\n notification: operation => {\n before(document, operation)\n operate(operation, () => {\n const { title, options } = operation\n Notification.requestPermission().then(result => {\n operation.permission = result\n if (result === 'granted') new Notification(title || '', options)\n })\n })\n after(document, operation)\n }\n}\n","import Operations from './operations'\n\nlet operations = Operations\n\nconst add = newOperations => {\n operations = { ...operations, ...newOperations }\n}\n\nconst addOperations = operations => {\n add(operations)\n}\n\nconst addOperation = (name, operation) => {\n const operations = {}\n operations[name] = operation\n\n add(operations)\n}\n\nexport { addOperation, addOperations }\n\nexport default {\n get all () {\n return operations\n }\n}\n","import { xpathToElement, dispatch } from './utils'\n\nimport ActiveElement from './active_element'\nimport OperationStore from './operation_store'\n\nconst perform = (\n operations,\n options = { emitMissingElementWarnings: true }\n) => {\n const batches = {}\n operations.forEach(operation => {\n if (!!operation.batch)\n batches[operation.batch] = batches[operation.batch]\n ? ++batches[operation.batch]\n : 1\n })\n operations.forEach(operation => {\n const name = operation.operation\n try {\n if (operation.selector) {\n operation.element = operation.xpath\n ? xpathToElement(operation.selector)\n : document[\n operation.selectAll ? 'querySelectorAll' : 'querySelector'\n ](operation.selector)\n } else {\n operation.element = document\n }\n if (operation.element || options.emitMissingElementWarnings) {\n ActiveElement.set(document.activeElement)\n const cableReadyOperation = OperationStore.all[name]\n\n if (cableReadyOperation) {\n cableReadyOperation(operation)\n if (!!operation.batch && --batches[operation.batch] === 0)\n dispatch(document, 'cable-ready:batch-complete', {\n batch: operation.batch\n })\n } else {\n console.error(\n `CableReady couldn't find the \"${name}\" operation. Make sure you use the camelized form when calling an operation method.`\n )\n }\n }\n } catch (e) {\n if (operation.element) {\n console.error(\n `CableReady detected an error in ${name || 'operation'}: ${\n e.message\n }. If you need to support older browsers make sure you've included the corresponding polyfills. https://docs.stimulusreflex.com/setup#polyfills-for-ie11.`\n )\n console.error(e)\n } else {\n console.warn(\n `CableReady ${name ||\n 'operation'} failed due to missing DOM element for selector: '${\n operation.selector\n }'`\n )\n }\n }\n })\n}\n\nconst performAsync = (\n operations,\n options = { emitMissingElementWarnings: true }\n) => {\n return new Promise((resolve, reject) => {\n try {\n resolve(perform(operations, options))\n } catch (err) {\n reject(err)\n }\n })\n}\n\nexport { perform, performAsync }\n","let consumer\n\nconst BACKOFF = [25, 50, 75, 100, 200, 250, 500, 800, 1000, 2000]\n\nconst wait = (ms) => new Promise(resolve => setTimeout(resolve, ms))\n\nconst getConsumerWithRetry = async (retry = 0) => {\n if (consumer) return consumer\n\n if (retry >= BACKOFF.length) {\n throw new Error(\"Couldn't obtain a Action Cable consumer within 5s\")\n }\n\n await wait(BACKOFF[retry])\n\n return await getConsumerWithRetry(retry + 1)\n}\n\nexport default {\n setConsumer (value) {\n consumer = value\n },\n\n get consumer () {\n return consumer\n },\n\n async getConsumer () {\n return await getConsumerWithRetry()\n }\n}\n","export default class SubscribingElement extends HTMLElement {\n disconnectedCallback () {\n if (this.channel) this.channel.unsubscribe()\n }\n\n createSubscription (consumer, channel, receivedCallback) {\n this.channel = consumer.subscriptions.create(\n {\n channel,\n identifier: this.identifier\n },\n {\n received: receivedCallback\n }\n )\n }\n\n get preview () {\n return (\n document.documentElement.hasAttribute('data-turbolinks-preview') ||\n document.documentElement.hasAttribute('data-turbo-preview')\n )\n }\n\n get identifier () {\n return this.getAttribute('identifier')\n }\n}\n","import { perform } from '../cable_ready'\nimport SubscribingElement from './subscribing_element'\nimport CableConsumer from '../cable_consumer'\n\nexport default class StreamFromElement extends SubscribingElement {\n async connectedCallback () {\n if (this.preview) return\n\n const consumer = await CableConsumer.getConsumer()\n\n if (consumer) {\n this.createSubscription(\n consumer,\n 'CableReady::Stream',\n this.performOperations\n )\n } else {\n console.error(\n 'The `stream_from` helper cannot connect without an ActionCable consumer.\\nPlease run `rails generate cable_ready:helpers` to fix this.'\n )\n }\n }\n\n performOperations (data) {\n if (data.cableReady) perform(data.operations)\n }\n}\n","import morphdom from 'morphdom'\n\nimport SubscribingElement from './subscribing_element'\n\nimport { shouldMorph } from '../morph_callbacks'\nimport { debounce, assignFocus, dispatch, graciouslyFetch } from '../utils'\n\nimport ActiveElement from '../active_element'\nimport CableConsumer from '../cable_consumer'\n\nconst template = `\n<style>\n :host {\n display: block;\n }\n</style>\n<slot></slot>\n`\n\nfunction url (element) {\n return element.hasAttribute('url')\n ? element.getAttribute('url')\n : location.href\n}\n\nexport default class UpdatesForElement extends SubscribingElement {\n constructor () {\n super()\n const shadowRoot = this.attachShadow({ mode: 'open' })\n shadowRoot.innerHTML = template\n }\n\n async connectedCallback () {\n if (this.preview) return\n this.update = debounce(this.update.bind(this), this.debounce)\n\n const consumer = await CableConsumer.getConsumer()\n\n if (consumer) {\n this.createSubscription(consumer, 'CableReady::Stream', this.update)\n } else {\n console.error(\n 'The `updates-for` helper cannot connect without an ActionCable consumer.\\nPlease run `rails generate cable_ready:helpers` to fix this.'\n )\n }\n }\n\n async update (data) {\n const blocks = Array.from(\n document.querySelectorAll(this.query),\n element => new Block(element)\n )\n\n // first updates-for element in the DOM *at any given moment* updates all of the others\n if (blocks[0].element !== this) return\n\n // hold a reference to the active element so that it can be restored after the morph\n ActiveElement.set(document.activeElement)\n\n // store all retrieved HTML in an object keyed by URL to minimize fetch calls\n this.html = {}\n\n const uniqueUrls = [...new Set(blocks.map(block => block.url))]\n\n await Promise.all(\n uniqueUrls.map(async url => {\n if (!this.html.hasOwnProperty(url)) {\n const response = await graciouslyFetch(url, {\n 'X-Cable-Ready': 'update'\n })\n this.html[url] = await response.text()\n }\n })\n )\n\n // track current block index for each URL; referred to as fragments\n this.index = {}\n\n blocks.forEach(block => {\n // if the block's URL is not in the index, initialize it to 0; otherwise, increment it\n this.index.hasOwnProperty(block.url)\n ? this.index[block.url]++\n : (this.index[block.url] = 0)\n\n block.process(data, this.html, this.index)\n })\n }\n\n get query () {\n return `updates-for[identifier=\"${this.identifier}\"]`\n }\n\n get identifier () {\n return this.getAttribute('identifier')\n }\n\n get debounce () {\n return this.hasAttribute('debounce')\n ? parseInt(this.getAttribute('debounce'))\n : 20\n }\n}\n\nclass Block {\n constructor (element) {\n this.element = element\n }\n\n async process (data, html, index) {\n // with the index incremented, we can now safely bail - before a fetch - if there's no work to be done\n if (!this.shouldUpdate(data)) return\n\n const blockIndex = index[this.url]\n const template = document.createElement('template')\n this.element.setAttribute('updating', 'updating')\n\n template.innerHTML = String(html[this.url]).trim()\n\n await this.resolveTurboFrames(template.content)\n\n const fragments = template.content.querySelectorAll(this.query)\n\n if (fragments.length <= blockIndex) {\n console.warn(\n `Update aborted due to insufficient number of elements. The offending url is ${this.url}.`\n )\n return\n }\n\n const operation = {\n element: this.element,\n html: fragments[blockIndex],\n permanentAttributeName: 'data-ignore-updates'\n }\n\n dispatch(this.element, 'cable-ready:before-update', operation)\n morphdom(this.element, fragments[blockIndex], {\n childrenOnly: true,\n onBeforeElUpdated: shouldMorph(operation),\n onElUpdated: _ => {\n this.element.removeAttribute('updating')\n dispatch(this.element, 'cable-ready:after-update', operation)\n assignFocus(operation.focusSelector)\n }\n })\n }\n\n async resolveTurboFrames (documentFragment) {\n const reloadingTurboFrames = [\n ...documentFragment.querySelectorAll(\n 'turbo-frame[src]:not([loading=\"lazy\"])'\n )\n ]\n\n return Promise.all(\n reloadingTurboFrames.map(frame => {\n return new Promise(async resolve => {\n const frameResponse = await graciouslyFetch(\n frame.getAttribute('src'),\n {\n 'Turbo-Frame': frame.id,\n 'X-Cable-Ready': 'update'\n }\n )\n\n const frameTemplate = document.createElement('template')\n frameTemplate.innerHTML = await frameResponse.text()\n\n // recurse here to get all nested eager loaded frames\n await this.resolveTurboFrames(frameTemplate.content)\n\n documentFragment.querySelector(\n `turbo-frame#${frame.id}`\n ).innerHTML = String(\n frameTemplate.content.querySelector(`turbo-frame#${frame.id}`)\n .innerHTML\n ).trim()\n\n resolve()\n })\n })\n )\n }\n\n shouldUpdate (data) {\n // if everything that could prevent an update is false, update this block\n return !this.ignoresInnerUpdates && this.hasChangesSelectedForUpdate(data)\n }\n\n hasChangesSelectedForUpdate (data) {\n // if there's an only attribute, only update if at least one of the attributes changed is in the allow list\n const only = this.element.getAttribute('only')\n\n return !(\n only &&\n data.changed &&\n !only.split(' ').some(attribute => data.changed.includes(attribute))\n )\n }\n\n get ignoresInnerUpdates () {\n // don't update during a Reflex or Turbolinks redraw\n return (\n this.element.hasAttribute('ignore-inner-updates') &&\n this.element.hasAttribute('performing-inner-update')\n )\n }\n\n get url () {\n return this.element.hasAttribute('url')\n ? this.element.getAttribute('url')\n : location.href\n }\n\n get identifier () {\n return this.element.identifier\n }\n\n get query () {\n return this.element.query\n }\n}\n","export const registerInnerUpdates = () => {\n document.addEventListener('stimulus-reflex:before', event => {\n recursiveMarkUpdatesForElements(event.detail.element)\n })\n\n document.addEventListener('stimulus-reflex:after', event => {\n setTimeout(() => {\n recursiveUnmarkUpdatesForElements(event.detail.element)\n })\n })\n\n document.addEventListener('turbo:submit-start', event => {\n recursiveMarkUpdatesForElements(event.target)\n })\n\n document.addEventListener('turbo:submit-end', event => {\n setTimeout(() => {\n recursiveUnmarkUpdatesForElements(event.target)\n })\n })\n}\n\nconst recursiveMarkUpdatesForElements = leaf => {\n const closestUpdatesFor = leaf && leaf.parentElement.closest('updates-for')\n if (closestUpdatesFor) {\n closestUpdatesFor.setAttribute('performing-inner-update', '')\n recursiveMarkUpdatesForElements(closestUpdatesFor)\n }\n}\n\nconst recursiveUnmarkUpdatesForElements = leaf => {\n const closestUpdatesFor = leaf && leaf.parentElement.closest('updates-for')\n if (closestUpdatesFor) {\n closestUpdatesFor.removeAttribute('performing-inner-update')\n recursiveUnmarkUpdatesForElements(closestUpdatesFor)\n }\n}\n","import packageInfo from '../package.json'\nimport { perform, performAsync } from './cable_ready'\nimport { initialize } from './elements'\nimport { shouldMorphCallbacks, didMorphCallbacks } from './morph_callbacks'\n\nimport * as MorphCallbacks from './morph_callbacks'\nimport * as Utils from './utils'\n\nimport OperationStore, { addOperation, addOperations } from './operation_store'\nimport StreamFromElement from './elements/stream_from_element'\nimport UpdatesForElement from './elements/updates_for_element'\nimport SubscribingElement from './elements/subscribing_element'\nimport CableConsumer from './cable_consumer'\n\nexport {\n Utils,\n MorphCallbacks,\n StreamFromElement,\n UpdatesForElement,\n SubscribingElement\n}\n\nconst global = {\n perform,\n performAsync,\n shouldMorphCallbacks,\n didMorphCallbacks,\n initialize,\n addOperation,\n addOperations,\n version: packageInfo.version,\n cable: CableConsumer,\n get DOMOperations () {\n console.warn(\n 'DEPRECATED: Please use `CableReady.operations` instead of `CableReady.DOMOperations`'\n )\n return OperationStore.all\n },\n get operations () {\n return OperationStore.all\n },\n get consumer () {\n return CableConsumer.consumer\n }\n}\n\nwindow.CableReady = global\n\nexport default global\n","import CableConsumer from '../cable_consumer'\n\nimport StreamFromElement from './stream_from_element'\nimport UpdatesForElement from './updates_for_element'\n\nimport { registerInnerUpdates } from '../updatable/inner_updates_compat'\n\nconst initialize = (initializeOptions = {}) => {\n const { consumer } = initializeOptions\n\n registerInnerUpdates()\n\n if (consumer) {\n CableConsumer.setConsumer(consumer)\n } else {\n console.error(\n 'CableReady requires a reference to your Action Cable `consumer` for its helpers to function.\\nEnsure that you have imported the `CableReady` package as well as `consumer` from your `channels` folder, then call `CableReady.initialize({ consumer })`.'\n )\n }\n\n if (!customElements.get('stream-from')) {\n customElements.define('stream-from', StreamFromElement)\n }\n\n if (!customElements.get('updates-for')) {\n customElements.define('updates-for', UpdatesForElement)\n }\n}\n\nexport { initialize }\n"],"names":["inputTags","INPUT","TEXTAREA","SELECT","mutableTags","OPTION","textInputTypes","color","date","datetime","email","month","number","password","range","search","tel","text","textarea","time","url","week","activeElement","ActiveElement","element","set","isTextInput","tagName","type","assignFocus","selector","focusElement","nodeType","Node","ELEMENT_NODE","document","querySelector","focus","dispatch","name","detail","evt","CustomEvent","bubbles","cancelable","dispatchEvent","window","jQuery","trigger","xpathToElement","xpath","evaluate","XPathResult","FIRST_ORDERED_NODE_TYPE","singleNodeValue","getClassNames","names","Array","flat","processElements","operation","callback","from","selectAll","forEach","kebabize","str","split","map","letter","idx","toUpperCase","toLowerCase","join","operate","cancel","delay","setTimeout","before","target","after","debounce","func","timeout","timer","args","clearTimeout","apply","this","handleErrors","response","ok","Error","statusText","async","graciouslyFetch","additionalHeaders","fetch","headers","undefined","e","console","error","shouldMorph","fromEl","toEl","shouldMorphCallbacks","includes","didMorph","el","didMorphCallbacks","verifyNotMutable","isEqualNode","verifyNotContentEditable","isContentEditable","verifyNotPermanent","permanentAttributeName","permanent","closest","ignore","value","attributes","attribute","setAttribute","Operations","append","html","focusSelector","insertAdjacentHTML","graft","parent","parentElement","appendChild","innerHtml","innerHTML","insertAdjacentHtml","position","insertAdjacentText","morph","template","createElement","String","trim","content","ordinal","children","indexOf","childrenOnly","morphdom","onBeforeElUpdated","onElUpdated","outerHtml","outerHTML","prepend","remove","replace","textContent","addCssClass","classList","add","removeAttribute","removeCssClass","setDatasetProperty","dataset","setProperty","setStyle","style","setStyles","styles","Object","entries","setValue","setMeta","meta","head","clearStorage","sessionStorage","localStorage","clear","go","delta","history","pushState","state","title","redirectTo","action","Turbo","visit","Turbolinks","location","href","reload","removeStorageItem","key","removeItem","replaceState","scrollIntoView","setCookie","cookie","setFocus","setStorageItem","setItem","consoleLog","message","level","log","consoleTable","data","columns","table","notification","options","Notification","requestPermission","then","result","permission","operations","newOperations","OperationStore","all","perform","emitMissingElementWarnings","batches","batch","cableReadyOperation","warn","consumer","BACKOFF","getConsumerWithRetry","retry","length","ms","Promise","resolve","CableConsumer","setConsumer","SubscribingElement","HTMLElement","disconnectedCallback","channel","unsubscribe","createSubscription","receivedCallback","subscriptions","create","identifier","received","preview","documentElement","hasAttribute","getAttribute","StreamFromElement","getConsumer","performOperations","cableReady","UpdatesForElement","constructor","super","attachShadow","mode","update","bind","blocks","querySelectorAll","query","Block","uniqueUrls","Set","block","hasOwnProperty","index","process","parseInt","shouldUpdate","blockIndex","resolveTurboFrames","fragments","_","documentFragment","reloadingTurboFrames","frame","frameResponse","id","frameTemplate","ignoresInnerUpdates","hasChangesSelectedForUpdate","only","changed","some","recursiveMarkUpdatesForElements","leaf","closestUpdatesFor","recursiveUnmarkUpdatesForElements","global","performAsync","reject","err","initialize","initializeOptions","addEventListener","event","customElements","get","define","addOperation","addOperations","version","packageInfo","cable","DOMOperations","CableReady"],"mappings":"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"}
@@ -8,11 +8,12 @@ module CableReadyHelper
8
8
  tag.stream_from(**build_options(*keys, html_options))
9
9
  end
10
10
 
11
- def updates_for(*keys, url: nil, debounce: nil, only: nil, html_options: {}, &block)
11
+ def updates_for(*keys, url: nil, debounce: nil, only: nil, ignore_inner_updates: false, html_options: {}, &block)
12
12
  options = build_options(*keys, html_options)
13
13
  options[:url] = url if url
14
14
  options[:debounce] = debounce if debounce
15
15
  options[:only] = only if only
16
+ options[:"ignore-inner-updates"] = "" if ignore_inner_updates
16
17
  tag.updates_for(**options) { capture(&block) }
17
18
  end
18
19
 
@@ -15,13 +15,13 @@ module CableReady
15
15
  private
16
16
 
17
17
  def broadcast_create(model)
18
- ActionCable.server.broadcast(model.class, {})
18
+ model.class.send(:broadcast_updates, model.class, {})
19
19
  end
20
20
  alias_method :broadcast_destroy, :broadcast_create
21
21
 
22
22
  def broadcast_update(model)
23
- ActionCable.server.broadcast(model.class, {})
24
- ActionCable.server.broadcast(model.to_global_id, model.respond_to?(:previous_changes) ? {changed: model.previous_changes.keys} : {})
23
+ model.class.send(:broadcast_updates, model.class, {})
24
+ model.class.send(:broadcast_updates, model.to_global_id, model.respond_to?(:previous_changes) ? {changed: model.previous_changes.keys} : {})
25
25
  end
26
26
  end
27
27
  end
@@ -25,6 +25,13 @@ module CableReady
25
25
  after_commit(ModelUpdatableCallbacks.new(:update, enabled_operations), {on: :update, if: options[:if]})
26
26
  after_commit(ModelUpdatableCallbacks.new(:destroy, enabled_operations), {on: :destroy, if: options[:if]})
27
27
  end
28
+
29
+ def self.skip_updates
30
+ skip_updates_classes.push(self)
31
+ yield
32
+ ensure
33
+ skip_updates_classes.pop
34
+ end
28
35
  end
29
36
  end
30
37
 
@@ -39,13 +46,21 @@ module CableReady
39
46
  result
40
47
  end
41
48
 
49
+ def has_many_attached(name, **options)
50
+ option = options.delete(:enable_updates)
51
+ broadcast = option.present?
52
+ result = super
53
+ enrich_attachments_with_updates(name, option) if broadcast
54
+ result
55
+ end
56
+
42
57
  def cable_ready_collections
43
58
  @cable_ready_collections ||= CollectionsRegistry.new
44
59
  end
45
60
 
46
61
  def cable_ready_update_collection(resource, name, model)
47
62
  identifier = resource.to_global_id.to_s + ":" + name.to_s
48
- ActionCable.server.broadcast(identifier, model.respond_to?(:previous_changes) ? {changed: model.previous_changes.keys} : {})
63
+ broadcast_updates(identifier, model.respond_to?(:previous_changes) ? {changed: model.previous_changes.keys} : {})
49
64
  end
50
65
 
51
66
  def enrich_association_with_updates(name, option)
@@ -59,6 +74,36 @@ module CableReady
59
74
  through_association = reflection.through_reflection.name.to_s.singularize
60
75
  end
61
76
 
77
+ options = build_options(option)
78
+
79
+ reflection.klass.send(:include, CableReady::Updatable) unless reflection.klass.respond_to?(:cable_ready_collections)
80
+
81
+ reflection.klass.cable_ready_collections.register({
82
+ klass: self,
83
+ foreign_key: reflection.foreign_key,
84
+ name: name,
85
+ inverse_association: inverse_of,
86
+ through_association: through_association,
87
+ options: options
88
+ })
89
+ end
90
+
91
+ def enrich_attachments_with_updates(name, option)
92
+ options = build_options(option)
93
+
94
+ ActiveStorage::Attachment.send(:include, CableReady::Updatable) unless ActiveStorage::Attachment.respond_to?(:cable_ready_collections)
95
+
96
+ ActiveStorage::Attachment.cable_ready_collections.register({
97
+ klass: self,
98
+ foreign_key: "record_id",
99
+ name: name,
100
+ inverse_association: "record",
101
+ through_association: nil,
102
+ options: options
103
+ })
104
+ end
105
+
106
+ def build_options(option)
62
107
  options = {
63
108
  on: [:create, :update, :destroy],
64
109
  if: ->(resource) { true }
@@ -82,16 +127,16 @@ module CableReady
82
127
  raise ArgumentError, "Invalid enable_updates option #{option}"
83
128
  end
84
129
 
85
- reflection.klass.send(:include, CableReady::Updatable) unless reflection.klass.respond_to?(:cable_ready_collections)
130
+ options
131
+ end
86
132
 
87
- reflection.klass.cable_ready_collections.register({
88
- klass: self,
89
- foreign_key: reflection.foreign_key,
90
- name: name,
91
- inverse_association: inverse_of,
92
- through_association: through_association,
93
- options: options
94
- })
133
+ def broadcast_updates(model_class, options)
134
+ return if skip_updates_classes.any? { |klass| klass >= self }
135
+ ActionCable.server.broadcast(model_class, options)
136
+ end
137
+
138
+ def skip_updates_classes
139
+ Thread.current[:skip_updates_classes] ||= []
95
140
  end
96
141
  end
97
142
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path("lib/cable_ready/version", __dir__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "cable_ready"
7
+ gem.license = "MIT"
8
+ gem.version = CableReady::VERSION
9
+ gem.authors = ["Nathan Hopkins"]
10
+ gem.email = ["natehop@gmail.com"]
11
+ gem.homepage = "https://github.com/stimulusreflex/cable_ready"
12
+ gem.summary = "Out-of-Band Server Triggered DOM Operations"
13
+
14
+ gem.files = Dir[
15
+ "lib/**/*.rb",
16
+ "app/**/*.rb",
17
+ "app/assets/javascripts/*",
18
+ "bin/*",
19
+ "[A-Z]*"
20
+ ]
21
+
22
+ gem.test_files = Dir["test/**/*.rb"]
23
+
24
+ rails_version = ">= 5.2"
25
+ gem.add_dependency "actioncable", rails_version
26
+ gem.add_dependency "actionpack", rails_version
27
+ gem.add_dependency "actionview", rails_version
28
+ gem.add_dependency "activerecord", rails_version
29
+ gem.add_dependency "activesupport", rails_version
30
+ gem.add_dependency "railties", rails_version
31
+
32
+ gem.add_dependency "thread-local", ">= 1.1.0"
33
+
34
+ gem.add_development_dependency "magic_frozen_string_literal"
35
+ gem.add_development_dependency "mocha"
36
+ gem.add_development_dependency "pry"
37
+ gem.add_development_dependency "pry-nav"
38
+ gem.add_development_dependency "rails", rails_version
39
+ gem.add_development_dependency "rake"
40
+ gem.add_development_dependency "sqlite3"
41
+ gem.add_development_dependency "standardrb"
42
+ end
@@ -0,0 +1,40 @@
1
+ require "rails/engine"
2
+
3
+ module CableReady
4
+ class Engine < Rails::Engine
5
+ initializer "cable_ready.sanity_check" do
6
+ SanityChecker.check! unless Rails.env.production?
7
+ end
8
+
9
+ initializer "cable_ready.renderer" do
10
+ ActiveSupport.on_load(:action_controller) do
11
+ ActionController::Renderers.add :operations do |operations, options|
12
+ response.content_type ||= Mime[:cable_ready]
13
+ render json: operations.dispatch
14
+ end
15
+
16
+ Mime::Type.register "application/vnd.cable-ready.json", :cable_ready
17
+ end
18
+ end
19
+
20
+ initializer "cable_ready.assets" do |app|
21
+ if app.config.respond_to?(:assets)
22
+ app.config.assets.precompile += %w[
23
+ cable_ready.js
24
+ cable_ready.min.js
25
+ cable_ready.min.js.map
26
+ cable_ready.umd.js
27
+ cable_ready.umd.min.js
28
+ cable_ready.umd.min.js.map
29
+ ]
30
+ end
31
+ end
32
+
33
+ initializer "cable_ready.importmap", before: "importmap" do |app|
34
+ if app.config.respond_to?(:importmap)
35
+ app.config.importmap.paths << Engine.root.join("lib/cable_ready/importmap.rb")
36
+ app.config.importmap.cache_sweepers << Engine.root.join("app/assets/javascripts")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,2 @@
1
+ pin "morphdom", to: "https://ga.jspm.io/npm:morphdom@2.6.1/dist/morphdom.js", preload: true
2
+ pin "cable_ready", to: "cable_ready.min.js", preload: true
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CableReady
4
- VERSION = "5.0.0.pre8"
4
+ VERSION = "5.0.0.pre9"
5
5
  end
data/lib/cable_ready.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rails/engine"
4
3
  require "open-uri"
5
4
  require "active_record"
6
5
  require "action_view"
@@ -14,6 +13,7 @@ require "cable_ready/identifiable"
14
13
  require "cable_ready/operation_builder"
15
14
  require "cable_ready/config"
16
15
  require "cable_ready/broadcaster"
16
+ require "cable_ready/engine"
17
17
  require "cable_ready/sanity_checker"
18
18
  require "cable_ready/compoundable"
19
19
  require "cable_ready/channel"
@@ -22,23 +22,6 @@ require "cable_ready/cable_car"
22
22
  require "cable_ready/stream_identifier"
23
23
 
24
24
  module CableReady
25
- class Engine < Rails::Engine
26
- initializer "cable_ready.sanity_check" do
27
- SanityChecker.check! unless Rails.env.production?
28
- end
29
-
30
- initializer "renderer" do
31
- ActiveSupport.on_load(:action_controller) do
32
- ActionController::Renderers.add :operations do |operations, options|
33
- response.content_type ||= Mime[:cable_ready]
34
- render json: operations.dispatch
35
- end
36
-
37
- Mime::Type.register "application/vnd.cable-ready.json", :cable_ready
38
- end
39
- end
40
- end
41
-
42
25
  class << self
43
26
  def config
44
27
  CableReady::Config.instance
data/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "cable_ready",
3
+ "version": "5.0.0-pre9",
4
+ "description": "CableReady helps you create great real-time user experiences by making it simple to trigger client-side DOM changes from server-side Ruby.",
5
+ "keywords": [
6
+ "ruby",
7
+ "rails",
8
+ "websockets",
9
+ "actioncable",
10
+ "cable",
11
+ "ssr",
12
+ "stimulus_reflex",
13
+ "client-side",
14
+ "dom"
15
+ ],
16
+ "homepage": "https://cableready.stimulusreflex.com/",
17
+ "bugs": {
18
+ "url": "https://github.com/stimulusreflex/cable_ready/issues"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com:stimulusreflex/cable_ready.git"
23
+ },
24
+ "license": "MIT",
25
+ "author": "Nathan Hopkins <natehop@gmail.com>",
26
+ "main": "./dist/cable_ready.umd.min.js",
27
+ "module": "./dist/cable_ready.min.js",
28
+ "files": [
29
+ "dist/*",
30
+ "javascript/*"
31
+ ],
32
+ "scripts": {
33
+ "lint": "yarn run prettier-standard:check",
34
+ "format": "yarn run prettier-standard:format",
35
+ "prettier-standard:check": "yarn run prettier-standard --check ./javascript/**/*.js rollup.config.js",
36
+ "prettier-standard:format": "yarn run prettier-standard ./javascript/**/*.js rollup.config.js",
37
+ "build": "yarn rollup -c",
38
+ "watch": "yarn rollup -wc"
39
+ },
40
+ "dependencies": {
41
+ "morphdom": "^2.6.1"
42
+ },
43
+ "devDependencies": {
44
+ "@rollup/plugin-commonjs": "^21.0.3",
45
+ "@rollup/plugin-json": "^4.1.0",
46
+ "@rollup/plugin-node-resolve": "^13.1.3",
47
+ "prettier-standard": "^16.4.1",
48
+ "rollup": "^2.70.1",
49
+ "rollup-plugin-terser": "^7.0.2"
50
+ }
51
+ }
data/rollup.config.js ADDED
@@ -0,0 +1,75 @@
1
+ import resolve from '@rollup/plugin-node-resolve'
2
+ import commonjs from '@rollup/plugin-commonjs'
3
+ import json from '@rollup/plugin-json'
4
+ import { terser } from 'rollup-plugin-terser'
5
+
6
+ const pretty = () => {
7
+ return terser({
8
+ mangle: false,
9
+ compress: false,
10
+ format: {
11
+ beautify: true,
12
+ indent_level: 2
13
+ }
14
+ })
15
+ }
16
+
17
+ const minify = () => {
18
+ return terser({
19
+ mangle: true,
20
+ compress: true
21
+ })
22
+ }
23
+
24
+ const esConfig = {
25
+ format: 'es',
26
+ inlineDynamicImports: true
27
+ }
28
+
29
+ const umdConfig = {
30
+ name: 'CableReady',
31
+ format: 'umd',
32
+ exports: 'named',
33
+ globals: { morphdom: 'morphdom' }
34
+ }
35
+
36
+ const distFolders = ['dist/', 'app/assets/javascripts/']
37
+
38
+ const output = distFolders
39
+ .map(distFolder => [
40
+ {
41
+ ...esConfig,
42
+ file: `${distFolder}/cable_ready.js`,
43
+ plugins: [pretty()]
44
+ },
45
+ {
46
+ ...esConfig,
47
+ file: `${distFolder}/cable_ready.min.js`,
48
+ sourcemap: true,
49
+ plugins: [minify()]
50
+ },
51
+ {
52
+ ...umdConfig,
53
+ file: `${distFolder}/cable_ready.umd.js`,
54
+ plugins: [pretty()]
55
+ },
56
+ {
57
+ ...umdConfig,
58
+ file: `${distFolder}/cable_ready.umd.min.js`,
59
+ sourcemap: true,
60
+ plugins: [minify()]
61
+ }
62
+ ])
63
+ .flat()
64
+
65
+ export default [
66
+ {
67
+ external: ['morphdom'],
68
+ input: 'javascript/index.js',
69
+ output,
70
+ plugins: [commonjs(), resolve(), json()],
71
+ watch: {
72
+ include: 'javascript/**'
73
+ }
74
+ }
75
+ ]
@@ -0,0 +1,4 @@
1
+ class Dugong < ApplicationRecord
2
+ include CableReady::Updatable
3
+ has_many_attached :images, enable_updates: true
4
+ end
@@ -0,0 +1,8 @@
1
+ class CreateDugongs < ActiveRecord::Migration[6.1]
2
+ def change
3
+ create_table :dugongs do |t|
4
+
5
+ t.timestamps
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,36 @@
1
+ # This migration comes from active_storage (originally 20170806125915)
2
+ class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
3
+ def change
4
+ create_table :active_storage_blobs do |t|
5
+ t.string :key, null: false
6
+ t.string :filename, null: false
7
+ t.string :content_type
8
+ t.text :metadata
9
+ t.string :service_name, null: false
10
+ t.bigint :byte_size, null: false
11
+ t.string :checksum, null: false
12
+ t.datetime :created_at, null: false
13
+
14
+ t.index [ :key ], unique: true
15
+ end
16
+
17
+ create_table :active_storage_attachments do |t|
18
+ t.string :name, null: false
19
+ t.references :record, null: false, polymorphic: true, index: false
20
+ t.references :blob, null: false
21
+
22
+ t.datetime :created_at, null: false
23
+
24
+ t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
25
+ t.foreign_key :active_storage_blobs, column: :blob_id
26
+ end
27
+
28
+ create_table :active_storage_variant_records do |t|
29
+ t.belongs_to :blob, null: false, index: false
30
+ t.string :variation_digest, null: false
31
+
32
+ t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
33
+ t.foreign_key :active_storage_blobs, column: :blob_id
34
+ end
35
+ end
36
+ end