<commit_before># PwnFx: AJAX sprinkles via unobtrusive JavaScript.
# @author <NAME>

# The author sorely misses Rails' AJAX helpers such as observe_field. This
# library provides a replacement that adheres to the new philosophy of
# unobtrusive JavaScript triggered by HTML5 data- attributes.


# The class of the singleton instance that tracks all the effects on the page.
class PwnFxClass
  # Creates an instance that isn't aware of any effects.
  #
  # 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) ->
    for effect in @effects
      attrName = "data-pwnfx-#{effect[0]}"
      effectClass = effect[1]
      scopeAttrName = "#{attrName}-scope"
      doneAttrName = "#{attrName}-done"
      attrSelector = "[#{attrName}]"
      for element in document.querySelectorAll(attrSelector)
        attrValue = element.getAttribute attrName
        continue unless attrValue
        element.removeAttribute attrName
        element.setAttribute doneAttrName, attrValue
        scopeId = element.getAttribute scopeAttrName
        new effectClass element, attrValue, scopeId
    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("Effect name {attrPrefix} already registered")
    @effects.push [attrPrefix, klass]
  
  # Finds a scoping container.
  #
  # @param [String] scopeId the scope ID to look for
  # @param [Element] element the element where the lookup starts
  # @return [Element] the closest parent of the given element whose
  #     data-pwnfx-scope matches the scopeId argument; window.document is
  #     returned if no such element exists or if scope is null
  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 [Element] 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 
      #       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
    
    if scopeMatches
      matches = Array.prototype.slice.call scope.querySelectorAll(selector)
      matches.push scope
      matches
    else
      scope.querySelectorAll selector
  
  
# Singleton instance.
PwnFx = new PwnFxClass


# Moves an element using data-pwnfx-move.
#
# Attributes:
#   data-pwnfx-move: an identifier connecting the move's target element
#   data-pwnfx-move-target: set to the same value as data-pwnfx-move on the
#       element that will receive the moved element as its last child
class PwnFxMove
  constructor: (element, identifier, scopeId) ->
    scope = PwnFx.resolveScope scopeId, element
    target = document.querySelector "[data-pwnfx-move-target=\"#{identifier}\"]"
    target.appendChild element

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 
#   data-pwnfx-render-source: set on the <script> tag containing the source HTML
#       to be rendered; set to the identifier in data-pwnfx-render
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') ||
                     'beforeend'
    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
        randomId = 'r' + Date.now() + '_' + Math.random()
        html = html.replace randomizeRegExp, randomId
      for targetElement in PwnFx.queryScope(scope, targetSelector)
        targetElement.insertAdjacentHTML insertionPoint, html
        PwnFx.wire targetElement
      event.preventDefault()
      false
    element.addEventListener 'click', onClick, false

PwnFx.registerEffect 'render', PwnFxRender


# Fires off an AJAX request (almost) every time when an element changes.
#
# The text / HTML returned by the request is placed in another element.
#
# Element attributes:
#   data-pwnfx-refresh: URL to perform an AJAX request to
#   data-pwnfx-refresh-method: the HTTP method of AJAX request (default: POST)
#   data-pwnfx-refresh-ms: interval between a change on the source element and
#                          AJAX refresh requests (default: 200ms)
#   data-pwnfx-target: the element populated with the AJAX response
class PwnFxRefresh
  constructor: (element, xhrUrl, scopeId) ->
    targetSelector = '#' + element.getAttribute('data-pwnfx-refresh-target') 
    refreshInterval = parseInt(
        element.getAttribute('data-pwnfx-refresh-ms') || '200');
    xhrMethod = element.getAttribute('data-pwnfx-refresh-method') || 'POST'
    xhrForm = @parentForm element
    
    onXhrSuccess = ->
      data = @responseText
      scope = PwnFx.resolveScope scopeId, element
      for targetElement in PwnFx.queryScope(scope, targetSelector)
        targetElement.innerHTML = data
        # HACK: <script>s are removed and re-inserted so the browser runs them
        for scriptElement in targetElement.querySelectorAll('script')
          parent = scriptElement.parentElement
          nextSibling = scriptElement.nextSibling
          parent.removeChild scriptElement
          parent.insertBefore scriptElement.cloneNode(true), nextSibling        
        PwnFx.wire targetElement
            
    refreshPending = false
    refreshOldValue = null<commit_msg>pwnfx.<commit_after># PwnFx: AJAX sprinkles via unobtrusive JavaScript.
# @author <NAME>

# The author sorely misses Rails' AJAX helpers such as observe_field. This
# library provides a replacement that adheres to the new philosophy of
# unobtrusive JavaScript triggered by HTML5 data- attributes.


# The class of the singleton instance that tracks all the effects on the page.
class PwnFxClass
  # Creates an instance that isn't aware of any effects.
  #
  # 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) ->
    for effect in @effects
      attrName = "data-pwnfx-#{effect[0]}"
      effectClass = effect[1]
      scopeAttrName = "#{attrName}-scope"
      doneAttrName = "#{attrName}-done"
      attrSelector = "[#{attrName}]"
      for element in document.querySelectorAll(attrSelector)
        attrValue = element.getAttribute attrName
        continue unless attrValue
        element.removeAttribute attrName
        element.setAttribute doneAttrName, attrValue
        scopeId = element.getAttribute scopeAttrName
        new effectClass element, attrValue, scopeId
    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("Effect name {attrPrefix} already registered")
    @effects.push [attrPrefix, klass]
  
  # Finds a scoping container.
  #
  # @param [String] scopeId the scope ID to look for
  # @param [Element] element the element where the lookup starts
  # @return [Element] the closest parent of the given element whose
  #     data-pwnfx-scope matches the scopeId argument; window.document is
  #     returned if no such element exists or if scope is null
  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 [Element] 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 
      #       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
    
    if scopeMatches
      matches = Array.prototype.slice.call scope.querySelectorAll(selector)
      matches.push scope
      matches
    else
      scope.querySelectorAll selector
  
  # Executes the JavaScript inside <script>s in a DOM subtree.
  #
  # @param [Element] element the DOM element rooting the subtree that will be
  #                          searched for <script>
  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
  
# Singleton instance.
PwnFx = new PwnFxClass


# Moves an element using data-pwnfx-move.
#
# Attributes:
#   data-pwnfx-move: an identifier connecting the move's target element
#   data-pwnfx-move-target: set to the same value as data-pwnfx-move on the
#       element that will receive the moved element as its last child
class PwnFxMove
  constructor: (element, identifier, scopeId) ->
    scope = PwnFx.resolveScope scopeId, element
    target = document.querySelector "[data-pwnfx-move-target=\"#{identifier}\"]"
    target.appendChild element

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 
#   data-pwnfx-render-source: set on the <script> tag containing the source HTML
#       to be rendered; set to the identifier in data-pwnfx-render
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') ||
                     'beforeend'
    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
        randomId = 'r' + Date.now() + '_' + Math.random()
        html = html.replace randomizeRegExp, randomId
      for targetElement in PwnFx.queryScope(scope, targetSelector)
        targetElement.insertAdjacentHTML insertionPoint, html
        PwnFx.wire targetElement
      event.preventDefault()
      false
    element.addEventListener 'click', onClick, false

PwnFx.registerEffect 'render', PwnFxRender


# Fires off an AJAX request (almost) every time when an element changes.
#
# The text / HTML returned by the request is placed in another element.
#
# Element attributes:
#   data-pwnfx-refresh: URL to perform an AJAX request to
#   data-pwnfx-refresh-method: the HTTP method of AJAX request (default: POST)
#   data-pwnfx-refresh-ms: interval between a change on the source element and
#                          AJAX refresh requests (default: 200ms)
#   data-pwnfx-target: the element populated with the AJAX response
class PwnFxRefresh
  constructor: (element, xhrUrl, scopeId) ->
    targetSelector = '#' + element.getAttribute('data-pwnfx-refresh-target') 
    refreshInterval = parseInt(
        element.getAttribute('data-pwnfx-refresh-ms') || '200');
    xhrMethod = element.getAttribute('data-pwnfx-refresh-method') || 'POST'
    xhrForm = @parentForm element
    
    onXhrSuccess = ->
      data = @responseText
      scope = PwnFx.resolveScope scopeId, element
      for targetElement in PwnFx.queryScope(scope, targetSelector)
        targetElement.innerHTML = data
        PwnFx.runScripts targetElement
        PwnFx.wire targetElement
            
    refreshPending = false
    refreshOldValue = null
{'fraction_non_alphanumeric': 0.07764346439957492, 'fraction_numerical': 0.001328374070138151, 'mean_word_length': 3.1570955273329653, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6256733', 'n_tokens_mistral': 4185, 'n_tokens_neox': 4166, 'n_words': 1402}