app/assets/javascripts/pwn-fx.js.coffee in pwnstyles_rails-0.1.22 vs app/assets/javascripts/pwn-fx.js.coffee in pwnstyles_rails-0.1.23

- old
+ new

@@ -13,11 +13,11 @@ # After defining an effect class, call registerEffect on this instance to make # it aware of the effect. constructor: -> @effects = [] @effectsByName = {} - + # Wires JS to elements with data-pwnfx attributes. # # @param {Element} root the element whose content is wired; use document at # load time wire: (root) -> @@ -32,22 +32,22 @@ continue unless attrValue element.removeAttribute attrName element.setAttribute doneAttrName, attrValue scopeId = element.getAttribute scopeAttrName new effectClass element, attrValue, scopeId - null - + null + # Registers a PwnFx effect. # # @param {String} attrName string following data-pwnfx- in the effect's # attribute names # @param klass the class that wraps the effect's implementation registerEffect: (attrPrefix, klass) -> if @effectsByName[attrPrefix] throw new Error("PwnFx effect name {attrPrefix} already registered") @effects.push [attrPrefix, klass] - + # Finds a scoping container. # # @param {String} scopeId the scope ID to look for # @param {HTMLElement} element the element where the lookup starts # @return {HTMLElement} the closest parent of the given element whose @@ -56,59 +56,49 @@ resolveScope: (scopeId, element) -> element = null if scopeId is null while element != null && element.getAttribute('data-pwnfx-scope') != scopeId element = element.parentElement element || document - + # Performs a scoped querySelectAll. # # @param {HTMLElement} scope the DOM element serving as the search scope # @param {String} selector the CSS selector to query # @return {NodeList, Array} the elements in the scope that match the CSS # selector; the scope container can belong to the returned array queryScope: (scope, selector) -> scopeMatches = false if scope != document - # TODO: machesSelector is in a W3C spec, but only implemented using + # TODO: machesSelector is in a W3C spec, but only implemented using # prefixes; the code below should be simplified once browsers # implement it without vendor prefixes if scope.matchesSelector scopeMatches = scope.matchesSelector selector else if scope.webkitMatchesSelector scopeMatches = scope.webkitMatchesSelector selector else if scope.mozMatchesSelector - scopeMatches = scope.mozMatchesSelector - + scopeMatches = scope.mozMatchesSelector selector + if scopeMatches matches = Array.prototype.slice.call scope.querySelectorAll(selector) matches.push scope matches else scope.querySelectorAll selector - - # Executes the JavaScript inside the <script> tags in a DOM subtree. - # - # @param {HTMLElement} element the DOM element rooting the subtree that will - # be searched for <script> tags - runScripts: (element) -> - # HACK: <script>s are removed and re-inserted so the browser runs them - for scriptElement in element.querySelectorAll('script') - parent = scriptElement.parentElement - nextSibling = scriptElement.nextSibling - parent.removeChild scriptElement - parent.insertBefore scriptElement.cloneNode(true), nextSibling - null - + # Replaces an element's contents with some HTML. # - # The JavaScript inside the HTML's <script> tags will be executed. + # The JavaScript inside the HTML's <script> tags will be executed. # # @param {HTMLElement} element the element whose contents will be replaced # @param {String} replaceHtml: (element, html) -> - element.innerHTML = html - @runScripts element + range = document.createRange() + range.selectNode document.body + fragment = range.createContextualFragment html + element.innerHTML = '' + element.appendChild fragment @wire element # The closest form element wrapping a node. # # @param {HTMLElement} element the element whose parent chain will be searched @@ -117,11 +107,11 @@ parentForm: (element) -> while element return element if element.nodeName == 'FORM' element = element.parentNode null - + # Do AJAX. # # @param {String} url the request URL (e.g., "http://localhost/path/to.html") # @param {String} method the request method (e.g., "POST") # @param [HTMLFormElement] form the DOM form whose data will be submitted @@ -135,18 +125,18 @@ xhr.setRequestHeader 'X-Requested-With', 'XMLHttpRequest' if form xhr.send new FormData(form) else xhr.send null - + # Called when an XHR request issued by PwnFx.xhr works out. _xhr_onload: -> if (@status < 200 || @status >= 300) && (@status != 304) throw new Error( "XHR result ignored due to HTTP #{@status}: #{@statusText}") @pwnfxOnData @responseText - + # Singleton instance. PwnFx = new PwnFxClass # Moves an element using data-pwnfx-move. @@ -161,36 +151,36 @@ class PwnFxMove constructor: (element, identifier, scopeId) -> scope = PwnFx.resolveScope scopeId, element method = element.getAttribute('data-pwnfx-move-method') || 'append' target = document.querySelector "[data-pwnfx-move-target=\"#{identifier}\"]" - + switch method when 'append' target.appendChild element when 'replace' target.innerHTML = '' target.appendChild element else throw new Error("pwnfx-move-method #{method} not implemented") - + PwnFx.registerEffect 'move', PwnFxMove - - + + # Renders the contents of a template into a DOM element. # # Attributes: # data-pwnfx-render: identifier for the render operation # data-pwnfx-render-where: insertAdjacentHTML position argument; can be # beforebegin, afterbegin, beforeend, afterend; defaults to beforeend # data-pwnfx-render-randomize: regexp pattern whose matches will be replaced # with a random string; useful for generating unique IDs # data-pwnfx-render-target: set on the element(s) receiving the rendered HTML; -# set to the identifier in data-pwnfx-render +# set to the identifier in data-pwnfx-render # data-pwnfx-render-source: set to the identifier in data-pwnfx-render, on the -# <script> tag containing the source HTML +# <script> tag containing the source HTML class PwnFxRender constructor: (element, identifier, scopeId) -> sourceSelector = "script[data-pwnfx-render-source=\"#{identifier}\"]" targetSelector = "[data-pwnfx-render-target=\"#{identifier}\"]" insertionPoint = element.getAttribute('data-pwnfx-render-where') || @@ -198,11 +188,11 @@ randomizedPatten = element.getAttribute('data-pwnfx-render-randomize') if randomizedPatten randomizeRegExp = new RegExp(randomizedPatten, 'g') else randomizeRegExp = null - + onClick = (event) -> scope = PwnFx.resolveScope scopeId, element source = scope.querySelector sourceSelector html = source.innerHTML if randomizeRegExp @@ -216,11 +206,11 @@ element.addEventListener 'click', onClick, false PwnFx.registerEffect 'render', PwnFxRender -# Loads some content after the main page load via an AJAX request. +# Loads some content after the main page load via an AJAX request. # # The text / HTML returned by the request is placed in another element. Scripts # in <script> tags are executed. # # Element attributes: @@ -243,11 +233,11 @@ ajaxLoad = -> PwnFx.xhr xhrUrl, xhrMethod, xhrForm, (data) -> scope = PwnFx.resolveScope scopeId, element for targetElement in PwnFx.queryScope(scope, targetSelector) PwnFx.replaceHtml targetElement, data - + window.setTimeout ajaxLoad, delay PwnFx.registerEffect 'delayed', PwnFxDelayed @@ -270,35 +260,35 @@ xhrUrl = element.getAttribute('data-pwnfx-refresh-url') xhrMethod = element.getAttribute('data-pwnfx-refresh-method') || 'POST' xhrForm = PwnFx.parentForm element refreshDelay = parseInt( element.getAttribute('data-pwnfx-refresh-ms') || '200'); - + onXhrData = (data) -> scope = PwnFx.resolveScope scopeId, element for targetElement in PwnFx.queryScope(scope, targetSelector) PwnFx.replaceHtml targetElement, data - + changeTimeout = null refreshOldValue = null ajaxRefresh = -> changeTimeout = null PwnFx.xhr xhrUrl, xhrMethod, xhrForm, onXhrData - + onChange = -> value = element.value return true if value == refreshOldValue refreshOldValue = value - + window.clearTimeout changeTimeout if changeTimeout != null changeTimeout = window.setTimeout ajaxRefresh, refreshDelay true - + element.addEventListener 'change', onChange, false element.addEventListener 'keydown', onChange, false element.addEventListener 'keyup', onChange, false - + PwnFx.registerEffect 'refresh', PwnFxRefresh # Shows elements conditionally, depending on whether some inputs' values match. # @@ -336,11 +326,11 @@ targetElement.classList.remove hiddenClass for targetElement in PwnFx.queryScope(scope, hideSelector) targetElement.classList.add hiddenClass true onChange() - + element.addEventListener 'change', onChange, false element.addEventListener 'keydown', onChange, false element.addEventListener 'keyup', onChange, false PwnFx.registerEffect 'confirm', PwnFxConfirm @@ -369,22 +359,22 @@ negativeSelector = "[data-pwnfx-hide-negative=\"#{identifier}\"]" onChange = (event) -> positive = (trigger == 'click') || element.checked hideSelector = if positive then positiveSelector else negativeSelector showSelector = if positive then negativeSelector else positiveSelector - + scope = PwnFx.resolveScope scopeId, element for targetElement in PwnFx.queryScope(scope, hideSelector) targetElement.classList.add hiddenClass for targetElement in PwnFx.queryScope(scope, showSelector) targetElement.classList.remove hiddenClass if trigger == 'click' event.preventDefault() false else true - + if trigger == 'click' element.addEventListener 'click', onChange, false else if trigger == 'checked' element.addEventListener 'change', onChange, false onChange() @@ -409,25 +399,25 @@ class PwnFxShowIf constructor: (element, identifier, scopeId) -> hiddenClass = element.getAttribute('data-pwnfx-showif-class') || 'hidden' showValue = element.getAttribute 'data-pwnfx-showif-is' sourceSelector = "[data-pwnfx-showif-source=\"#{identifier}\"]" - + replacementTag = element.getAttribute 'data-pwnfx-showif-replace' if replacementTag replacement = document.createElement replacementTag replacement.setAttribute 'class', hiddenClass else replacement = null - + isHidden = false onChange = (event) -> value = event.target.value willHide = value != showValue return if isHidden == willHide isHidden = willHide - + if replacement if willHide element.parentElement.replaceChild replacement, element else replacement.parentElement.replaceChild element, replacement @@ -451,10 +441,10 @@ # Removes elements from the DOM when an element is clicked. # # Attributes: # data-pwnfx-remove: an identifier connecting the elements to be removed # data-pwnfx-remove-target: set to the same value as data-pwnfx-remove on -# elements that will be removed when the element is clicked +# elements that will be removed when the element is clicked class PwnFxRemove constructor: (element, identifier, scopeId) -> targetSelector = "[data-pwnfx-remove-target=\"#{identifier}\"]" onClick = (event) ->