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) ->