<commit_before>xhr = null

installDocumentReadyPageEventTriggers = ->
  document.addEventListener 'DOMContentLoaded', ( ->
    triggerEvent 'page:change'
    triggerEvent 'page:update'
  ), true

installJqueryAjaxSuccessPageUpdateTrigger = ->
  if typeof jQuery isnt 'undefined'
    jQuery(document).on 'ajaxSuccess', (event, xhr, settings) ->
      return unless jQuery.trim xhr.responseText
      triggerEvent 'page:update'

# Handle bug in Firefox 26/27 where history.state is initially undefined
historyStateIsDefined =
  window.history.state != undefined or navigator.userAgent.match /Firefox\/2[6|7]/

browserSupportsPushState =
  window.history and window.history.pushState and window.history.replaceState and historyStateIsDefined

browserIsntBuggy =
  !navigator.userAgent.match /CriOS\//

window.triggerEvent = (name, data) ->
  event = document.createEvent 'Events'
  event.data = data if data
  event.initEvent name, true, true
  document.dispatchEvent event

window.triggerEventFor = (name, node, data) ->
  event = document.createEvent 'Events'
  event.data = data if data
  event.initEvent name, true, true
  node.dispatchEvent event

popCookie = (name) ->
  value = document.cookie.match(new RegExp(name+"=(\\w+)"))?[1].toUpperCase() or ''
  document.cookie = name + '=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/'
  value

requestMethodIsSafe =
  popCookie('request_method') in ['GET','']

browserSupportsTurbolinks = browserSupportsPushState and browserIsntBuggy and requestMethodIsSafe

browserSupportsCustomEvents =
  document.addEventListener and document.createEvent

if browserSupportsCustomEvents
  installDocumentReadyPageEventTriggers()
  installJqueryAjaxSuccessPageUpdateTrigger()

# TODO: triggerEvent should be accessible to all these guys
# on some kind of eventbus
# TODO: clean up everything above me ^
# TODO: decide on the public API
class window.Turbolinks
  createDocument = null
  currentState = null
  loadedAssets = null
  referer = null

  fetch = (url, partialReplace = false, replaceContents = [], callback) ->
    return if pageChangePrevented(url)
    url = new ComponentUrl url

    rememberReferer()

    fetchReplacement url, partialReplace, ->
      resetScrollPosition() unless replaceContents.length
      callback?()
    , replaceContents

  @pushState: (state, title, url) ->
    window.history.pushState(state, title, url)

  @replaceState: (state, title, url) ->
    window.history.replaceState(state, title, url)

  fetchReplacement = (url, partialReplace, onLoadFunction, replaceContents) ->
    triggerEvent 'page:fetch', url: url.absolute

    xhr?.abort()
    xhr = new XMLHttpRequest
    xhr.open 'GET', url.withoutHashForIE10compatibility(), true
    xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
    xhr.setRequestHeader 'X-XHR-Referer', referer

    xhr.onload = ->
      if xhr.status >= 500
        document.location.href = url.absolute
      else
        Turbolinks.loadPage(url, xhr, partialReplace, onLoadFunction, replaceContents)

    xhr.onloadend = -> xhr = null
    xhr.onerror   = ->
      document.location.href = url.absolute

    xhr.send()

    return

  @loadPage: (url, xhr, partialReplace = false, onLoadFunction = (->), replaceContents = [], replaceAllExcept = []) ->
    triggerEvent 'page:receive'


    if doc = processResponse(xhr, partialReplace)
      reflectNewUrl url
      nodes = changePage(extractTitleAndBody(doc)..., partialReplace, replaceContents, replaceAllExcept)
      reflectRedirectedUrl(xhr)
      triggerEvent 'page:load', nodes
      onLoadFunction?()
    else
      document.location.href = url.absolute

    return

  changePage = (title, body, csrfToken, runScripts, partialReplace, onlyKeys = [], exceptKeys = []) ->
    document.title = title if title

    refreshedNodes = []
    refreshedNodes = refreshRefreshAlwaysNodes(body)
    if onlyKeys.length
      refreshedNodes = refreshedNodes.concat refreshNodesWithKeys(onlyKeys, body)
      return refreshedNodes
    else
      persistStaticElements(body)
      if exceptKeys.length
        refreshAllExceptWithKeys(exceptKeys, body)
      else
        deleteRefreshNeverNodes(body)

      triggerEvent 'page:before-replace'
      document.documentElement.replaceChild body, document.body
      CSRFToken.update csrfToken if csrfToken?
      executeScriptTags() if runScripts
      currentState = window.history.state
      triggerEvent 'page:change'
      triggerEvent 'page:update'

    return

  deleteRefreshNeverNodes = (body) ->
    for node in body.querySelectorAll('[refresh-never]')
      node.parentNode.removeChild(node)

    return

  refreshNodes = (allNodesToBeRefreshed, body) ->
    triggerEvent 'page:before-partial-replace', allNodesToBeRefreshed

    parentIsRefreshing = (node) ->
      for potentialParent in allNodesToBeRefreshed when node != potentialParent
        return true if potentialParent.contains(node)
      false

    refreshedNodes = []
    for existingNode in allNodesToBeRefreshed
      continue if parentIsRefreshing(existingNode)

      unless nodeId = existingNode.getAttribute('id')
        throw new Error "Turbolinks refresh: Refresh key elements must have an id."

      if newNode = body.querySelector("##{ nodeId }")
        newNode = newNode.cloneNode(true)
        existingNode.parentNode.replaceChild(newNode, existingNode)

        if newNode.nodeName == 'SCRIPT' && newNode.getAttribute("data-turbolinks-eval") != "false"
          executeScriptTag(newNode)
        else
          refreshedNodes.push(newNode)

      else if existingNode.getAttribute("refresh-always") == null
        existingNode.parentNode.removeChild(existingNode)

    refreshedNodes

  refreshRefreshAlwaysNodes = (body) ->
    allNodesToBeRefreshed = []
    for node in document.querySelectorAll("[refresh-always]")
      allNodesToBeRefreshed.push(node)

    return refreshNodes(allNodesToBeRefreshed, body)

  refreshNodesWithKeys = (keys, body) ->
    allNodesToBeRefreshed = []
    for key in keys
      for node in document.querySelectorAll("[refresh=#{key}]")
        allNodesToBeRefreshed.push(node)

    return refreshNodes(allNodesToBeRefreshed, body)

  keepNodes = (body, allNodesToKeep) ->
    for existingNode in allNodesToKeep
      unless nodeId = existingNode.getAttribute('id')
        throw new Error("TurboGraft refresh: Kept nodes must have an id.")
<commit_msg>avoid returning stale nodes by doing every replace in one go, as it was performed previously<commit_after>xhr = null

installDocumentReadyPageEventTriggers = ->
  document.addEventListener 'DOMContentLoaded', ( ->
    triggerEvent 'page:change'
    triggerEvent 'page:update'
  ), true

installJqueryAjaxSuccessPageUpdateTrigger = ->
  if typeof jQuery isnt 'undefined'
    jQuery(document).on 'ajaxSuccess', (event, xhr, settings) ->
      return unless jQuery.trim xhr.responseText
      triggerEvent 'page:update'

# Handle bug in Firefox 26/27 where history.state is initially undefined
historyStateIsDefined =
  window.history.state != undefined or navigator.userAgent.match /Firefox\/2[6|7]/

browserSupportsPushState =
  window.history and window.history.pushState and window.history.replaceState and historyStateIsDefined

browserIsntBuggy =
  !navigator.userAgent.match /CriOS\//

window.triggerEvent = (name, data) ->
  event = document.createEvent 'Events'
  event.data = data if data
  event.initEvent name, true, true
  document.dispatchEvent event

window.triggerEventFor = (name, node, data) ->
  event = document.createEvent 'Events'
  event.data = data if data
  event.initEvent name, true, true
  node.dispatchEvent event

popCookie = (name) ->
  value = document.cookie.match(new RegExp(name+"=(\\w+)"))?[1].toUpperCase() or ''
  document.cookie = name + '=; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/'
  value

requestMethodIsSafe =
  popCookie('request_method') in ['GET','']

browserSupportsTurbolinks = browserSupportsPushState and browserIsntBuggy and requestMethodIsSafe

browserSupportsCustomEvents =
  document.addEventListener and document.createEvent

if browserSupportsCustomEvents
  installDocumentReadyPageEventTriggers()
  installJqueryAjaxSuccessPageUpdateTrigger()

# TODO: triggerEvent should be accessible to all these guys
# on some kind of eventbus
# TODO: clean up everything above me ^
# TODO: decide on the public API
class window.Turbolinks
  createDocument = null
  currentState = null
  loadedAssets = null
  referer = null

  fetch = (url, partialReplace = false, replaceContents = [], callback) ->
    return if pageChangePrevented(url)
    url = new ComponentUrl url

    rememberReferer()

    fetchReplacement url, partialReplace, ->
      resetScrollPosition() unless replaceContents.length
      callback?()
    , replaceContents

  @pushState: (state, title, url) ->
    window.history.pushState(state, title, url)

  @replaceState: (state, title, url) ->
    window.history.replaceState(state, title, url)

  fetchReplacement = (url, partialReplace, onLoadFunction, replaceContents) ->
    triggerEvent 'page:fetch', url: url.absolute

    xhr?.abort()
    xhr = new XMLHttpRequest
    xhr.open 'GET', url.withoutHashForIE10compatibility(), true
    xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
    xhr.setRequestHeader 'X-XHR-Referer', referer

    xhr.onload = ->
      if xhr.status >= 500
        document.location.href = url.absolute
      else
        Turbolinks.loadPage(url, xhr, partialReplace, onLoadFunction, replaceContents)

    xhr.onloadend = -> xhr = null
    xhr.onerror   = ->
      document.location.href = url.absolute

    xhr.send()

    return

  @loadPage: (url, xhr, partialReplace = false, onLoadFunction = (->), replaceContents = [], replaceAllExcept = []) ->
    triggerEvent 'page:receive'


    if doc = processResponse(xhr, partialReplace)
      reflectNewUrl url
      nodes = changePage(extractTitleAndBody(doc)..., partialReplace, replaceContents, replaceAllExcept)
      reflectRedirectedUrl(xhr)
      triggerEvent 'page:load', nodes
      onLoadFunction?()
    else
      document.location.href = url.absolute

    return

  changePage = (title, body, csrfToken, runScripts, partialReplace, onlyKeys = [], exceptKeys = []) ->
    document.title = title if title

    if onlyKeys.length
      nodesToRefresh = [].concat(getNodesWithRefreshAlways(), getNodesMatchingRefreshKeys(onlyKeys))
      return refreshNodes(nodesToRefresh, body)
    else
      refreshNodes(getNodesWithRefreshAlways(), body)
      persistStaticElements(body)
      if exceptKeys.length
        refreshAllExceptWithKeys(exceptKeys, body)
      else
        deleteRefreshNeverNodes(body)

      triggerEvent 'page:before-replace'
      document.documentElement.replaceChild body, document.body
      CSRFToken.update csrfToken if csrfToken?
      executeScriptTags() if runScripts
      currentState = window.history.state
      triggerEvent 'page:change'
      triggerEvent 'page:update'

    return

  getNodesMatchingRefreshKeys = (keys) ->
    matchingNodes = []
    for key in keys
      for node in document.querySelectorAll("[refresh=#{key}]")
        matchingNodes.push(node)

    return matchingNodes

  getNodesWithRefreshAlways = ->
    matchingNodes = []
    for node in document.querySelectorAll("[refresh-always]")
      matchingNodes.push(node)

    return matchingNodes

  deleteRefreshNeverNodes = (body) ->
    for node in body.querySelectorAll('[refresh-never]')
      node.parentNode.removeChild(node)

    return

  refreshNodes = (allNodesToBeRefreshed, body) ->
    triggerEvent 'page:before-partial-replace', allNodesToBeRefreshed

    parentIsRefreshing = (node) ->
      for potentialParent in allNodesToBeRefreshed when node != potentialParent
        return true if potentialParent.contains(node)
      false

    refreshedNodes = []
    for existingNode in allNodesToBeRefreshed
      continue if parentIsRefreshing(existingNode)

      unless nodeId = existingNode.getAttribute('id')
        throw new Error "Turbolinks refresh: Refresh key elements must have an id."

      if newNode = body.querySelector("##{ nodeId }")
        newNode = newNode.cloneNode(true)
        existingNode.parentNode.replaceChild(newNode, existingNode)

        if newNode.nodeName == 'SCRIPT' && newNode.getAttribute("data-turbolinks-eval") != "false"
          executeScriptTag(newNode)
        else
          refreshedNodes.push(newNode)

      else if existingNode.getAttribute("refresh-always") == null
        existingNode.parentNode.removeChild(existingNode)

    refreshedNodes

  keepNodes = (body, allNodesToKeep) ->
    for existingNode in allNodesToKeep
      unless nodeId = existingNode.getAttribute('id')
        throw new Error("TurboGraft refresh: Kept nodes must have an id.")

{'fraction_non_alphanumeric': 0.09538677698852549, 'fraction_numerical': 0.003590664272890485, 'mean_word_length': 3.8020989505247376, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, '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': '5930816', 'n_tokens_mistral': 3714, 'n_tokens_neox': 3639, 'n_words': 882}