[
  {
    "path": ".gitignore",
    "content": "/.bundle/\n/.yardoc\n/Gemfile.lock\n/_yardoc/\n/coverage/\n/doc/\n/pkg/\n/spec/reports/\n/tmp/\n/node_modules\n/vendor/bundle\n/*.gem\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js: node\n\nbefore_script:\n  - npm install grunt-cli\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Code of Conduct\n\nAs contributors and maintainers of this project, and in the interest of\nfostering an open and welcoming community, we pledge to respect all people who\ncontribute through reporting issues, posting feature requests, updating\ndocumentation, submitting pull requests or patches, and other activities.\n\nWe are committed to making participation in this project a harassment-free\nexperience for everyone, regardless of level of experience, gender, gender\nidentity and expression, sexual orientation, disability, personal appearance,\nbody size, race, ethnicity, age, religion, or nationality.\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery\n* Personal attacks\n* Trolling or insulting/derogatory comments\n* Public or private harassment\n* Publishing other's private information, such as physical or electronic\n  addresses, without explicit permission\n* Other unethical or unprofessional conduct\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\nBy adopting this Code of Conduct, project maintainers commit themselves to\nfairly and consistently applying these principles to every aspect of managing\nthis project. Project maintainers who do not follow or enforce the Code of\nConduct may be permanently removed from the project team.\n\nThis code of conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community.\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting a project maintainer at lukasz@niemier.pl. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. Maintainers are\nobligated to maintain confidentiality with regard to the reporter of an\nincident.\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 1.3.0, available at\n[http://contributor-covenant.org/version/1/3/0/][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/3/0/"
  },
  {
    "path": "Gemfile",
    "content": "source 'https://rubygems.org'\n\n# Specify your gem's dependencies in vanilla-ujs.gemspec\ngemspec\n"
  },
  {
    "path": "Gruntfile.js",
    "content": "module.exports = function (grunt) {\n  grunt.initConfig({\n    pkg: grunt.file.readJSON('package.json'),\n    mocha: {\n      all: {\n        options: {\n          reporter: 'Spec'\n        }\n      },\n      ci: {\n        options: {\n        }\n      },\n      options: {\n        run: true,\n        urls: ['http://localhost:8000/index.html'],\n      }\n    },\n    concat: {\n      options: {\n        stripBanners: true,\n        banner: \"(function (window, document) {\\n'use strict';\\n\",\n        footer: '}).call(void(0), window, document);'\n      },\n      dist: {\n        src: [\n          'lib/assets/javascripts/vanilla-ujs/polyfills.js',\n          'lib/assets/javascripts/vanilla-ujs/liteajax.js',\n          'lib/assets/javascripts/vanilla-ujs/confirm.js',\n          'lib/assets/javascripts/vanilla-ujs/method.js',\n          'lib/assets/javascripts/vanilla-ujs/disable.js',\n          'lib/assets/javascripts/vanilla-ujs/csrf.js',\n          'lib/assets/javascripts/vanilla-ujs/form.js',\n        ],\n        dest: 'lib/assets/javascripts/vanilla-ujs.js'\n      }\n    },\n    express: {\n      test: {\n        options: {\n          port: 8000,\n          hostname: 'localhost',\n          server: 'test/helpers/serv.js',\n          bases: ['./lib/', './test/']\n        }\n      }\n    },\n    watch: {\n      tests: {\n        files: [\"lib/**/*.js\", \"test/**/*.spec.js\"],\n        tasks: [\"test\"],\n      },\n    }\n  });\n\n  grunt.registerTask('test', ['express:test', 'mocha:all']);\n  grunt.registerTask('webtest', ['express:test', 'express-keepalive']);\n  grunt.registerTask('ci', ['express:test', 'mocha:ci']);\n\n  grunt.registerTask('dist', ['concat']);\n  grunt.registerTask('default', ['test']);\n\n  grunt.loadNpmTasks('grunt-contrib-jshint');\n  grunt.loadNpmTasks('grunt-contrib-concat');\n  grunt.loadNpmTasks('grunt-contrib-watch');\n  grunt.loadNpmTasks('grunt-express');\n  grunt.loadNpmTasks('grunt-mocha');\n};\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "Copyright (c) 2013 Łukasz Niemier\n\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "MAINTAINERS",
    "content": "Łukasz Jan Niemier <lukasz@niemier.pl> (@hauleth)\nAlex Tsokurov <me@ximik.net> (@Ximik)\n"
  },
  {
    "path": "README.md",
    "content": "# Vanilla UJS\n[![Build Status][travis-img]][travis-link][![Dependency Status](https://gemnasium.com/hauleth/vanilla-ujs.svg)](https://gemnasium.com/hauleth/vanilla-ujs)\n\nIt is implementation of Rails [jQuery UJS][jq-ujs] in pure JavaScript.\nNo extra dependencies.\n\n## Installation using the vanilla-ujs gem\n\nFor automated installation in Rails, use the `vanilla-ujs` gem.\nPlace this in your Gemfile:\n\n```ruby\ngem 'vanilla-ujs'\n```\n\nAnd run:\n\n```shell\n$ bundle install\n```\n\nRequire `vanilla-ujs` into your application.js manifest.\n\n```javascript\n//= require vanilla-ujs\n```\n\n## Does it mean that I shouldn't use jQuery\n\nNo. You should if you want. This library is created to make your Rails code\nindependent from front-end library.\n\n## Contribute\n\n1. Clone repo\n\n        $ git clone git://github.com/hauleth/vanilla-ujs.git\n        $ cd vanilla-js/\n\n2. Install dependencies\n\n        $ npm install\n\n3. Run tests\n\n        $ grunt test\n\n## Thanks\n\n- Alex Tsokurov ([@ximik](https://github.com/ximik))\n- Matt Huggins ([@mhuggins](https://github.com/mhuggins))\n- Tasveer Singh ([@tazsingh](https://github.com/tazsingh))\n- Tim O'Sulg ([@timgluz](https://github.com/timgluz))\n- Walter Lee Davis ([@walterdavis](https://github.com/walterdavis))\n\n# License\n\nSee [`LICENSE`](LICENSE.txt) file.\n\n[travis-img]:  https://travis-ci.org/hauleth/vanilla-ujs.svg?branch=master\n[travis-link]: https://travis-ci.org/hauleth/vanilla-ujs\n[jq-ujs]:      https://github.com/rails/jquery-ujs\n"
  },
  {
    "path": "lib/assets/javascripts/vanilla-ujs/confirm.js",
    "content": "document.addEventListener('click', function (event) {\n  var message, element;\n\n  if (element = matchesSelfOrParent(event.target, 'a[data-confirm], button[data-confirm], input[data-confirm]')) {\n    message = element.getAttribute('data-confirm');\n    if (!confirm(message)) {\n      event.stopPropagation();\n      event.stopImmediatePropagation();\n      event.preventDefault();\n      return false;\n    }\n\n    return;\n  }\n}, false);\n"
  },
  {
    "path": "lib/assets/javascripts/vanilla-ujs/csrf.js",
    "content": "var CSRF = {\n  token: function () {\n    var token = document.querySelector('meta[name=\"csrf-token\"]');\n    return token && token.getAttribute('content');\n  },\n  param: function () {\n    var param = document.querySelector('meta[name=\"csrf-param\"]');\n    return param && param.getAttribute('content');\n  }\n};\n\nvar sameOrigin = function (url) {\n  var a = document.createElement('a'), origin;\n  a.href = url;\n  origin = a.href.split('/', 3).join('/');\n\n  return window.location.href.indexOf(origin) === 0;\n};\n\nwindow.CSRF = CSRF;\n\ndocument.addEventListener('ajax:before', function (e) {\n  var token = CSRF.token(), xhr = e.detail;\n  if (token)\n    xhr.setRequestHeader('X-CSRF-Token', token);\n});\n\ndocument.addEventListener('submit', function (e) {\n  var token = CSRF.token(),\n      param = CSRF.param(),\n      form  = e.target;\n\n  if (matches.call(form, 'form')) {\n    if (matches.call(form, 'form[data-remote]'))\n      return true;\n    if (!form.method || form.method.toUpperCase() == 'GET')\n      return true;\n    if (!sameOrigin(form.action))\n      return true;\n\n    if (param && token && !form.querySelector('input[name='+param+']')) {\n      var input = document.createElement('input');\n      input.setAttribute('type', 'hidden');\n      input.setAttribute('name', param);\n      input.setAttribute('value', token);\n\n      form.appendChild(input);\n    }\n\n    return true;\n  }\n});\n"
  },
  {
    "path": "lib/assets/javascripts/vanilla-ujs/disable.js",
    "content": "document.addEventListener('click', function (event) {\n  var message, element;\n\n  // do not disable on right click. Work on left and middle click\n  if (event.which == 3) {\n    return;\n  }\n\n  if (element = matchesSelfOrParent(event.target, 'a[data-disable-with], button[data-disable-with], input[data-disable-with]')) {\n\n    // do not disable if the element is a submit button and its form has invalid input elements.\n    // since failed validations prevent the form from being submitted, we would lock the form permanently\n    // by disabling the submit button even though the form was never submitted\n    if (element.getAttribute(\"type\") === \"submit\" && element.form.querySelector(\":invalid\") !== null) {\n      return;\n    }\n\n    message = element.getAttribute('data-disable-with');\n    if (!!element.value) {\n      element.value = message;\n    } else {\n      element.innerHTML = message;\n    }\n    // timeout is needed because Safari stops the submit if the button is immediately disabled\n    setTimeout(function() {\n      element.setAttribute('disabled', 'disabled');\n    }, 0);\n    return;\n  }\n\n  if (element = matchesSelfOrParent(event.target, 'a[data-disable], button[data-disable], input[data-disable]')) {\n    if (element.getAttribute(\"type\") === \"submit\" && element.form.querySelector(\":invalid\") !== null) {\n      return;\n    }\n\n    setTimeout(function() {\n      element.setAttribute('disabled', 'disabled');\n    }, 0);\n  }\n}, false);\n"
  },
  {
    "path": "lib/assets/javascripts/vanilla-ujs/form.js",
    "content": "var VanillaUJS = {\n  formHasNoInputs: function (form) {\n    var element,\n        fieldType;\n\n    for (var i = 0, elements = form.elements, count = elements.length; i < count; i++) {\n      element = elements[i];\n      fieldType = element.nodeName.toUpperCase();\n\n      if (!element.hasAttribute('name') || element.disabled) {\n        continue;\n      }\n\n      if ((fieldType == 'RADIO' || fieldType == 'CHECKBOX') && !element.checked) {\n        continue;\n      }\n\n      return false;\n    }\n\n    return true;\n  }\n};\n\ndocument.addEventListener('submit', function(event) {\n\n  var form = event.target;\n\n  if (matches.call(form, 'form[data-remote]')) {\n    var url = form.action;\n    var method = (form.method || form.getAttribute('data-method') || 'POST').toUpperCase();\n    var data = new FormData(form);\n    var formHasNoInputs = VanillaUJS.formHasNoInputs(form);\n\n    if (CSRF.param() && CSRF.token()) {\n      data[CSRF.param()] = CSRF.token();\n    } else if (formHasNoInputs) {\n      data = null;\n    }\n\n    if (LiteAjax.ajax({ url: url, method: method, data: data, target: form })){\n      event.preventDefault();\n    } else {\n      return true;\n    }\n  }\n});\n"
  },
  {
    "path": "lib/assets/javascripts/vanilla-ujs/liteajax.js",
    "content": "var LiteAjax = (function () {\n  var LiteAjax = {};\n\n  LiteAjax.options = {\n    method: 'GET',\n    url: window.location.href\n  };\n\n  LiteAjax.ajax = function (url, options) {\n    if (typeof url == 'object') {\n      options = url;\n      url = undefined;\n    }\n\n    options = options || {};\n\n    if(!options.accepts) {\n      options.accepts = 'text/javascript, application/javascript, ' +\n                        'application/ecmascript, application/x-ecmascript';\n    }\n\n    url = url || options.url || location.href || '';\n    var data = options.data;\n    var target = options.target || document;\n    var xhr = new XMLHttpRequest();\n\n    xhr.addEventListener('load', function () {\n      var responseType = xhr.getResponseHeader('content-type');\n      if(responseType === 'text/javascript; charset=utf-8') {\n        eval(xhr.response);\n      }\n\n      var event = new CustomEvent('ajax:complete', {detail: xhr, bubbles: true});\n      target.dispatchEvent(event);\n    });\n\n    if (typeof options.success == 'function')\n      xhr.addEventListener('load', function (event) {\n        if (xhr.status >= 200 && xhr.status < 300)\n          options.success(xhr);\n      });\n\n    if (typeof options.error == 'function') {\n      xhr.addEventListener('load', function (event) {\n        if (xhr.status < 200 || xhr.status >= 300)\n          options.error(xhr);\n      });\n      xhr.addEventListener('error', function (event) {\n        options.error(xhr);\n      });\n    }\n\n    xhr.open(options.method || 'GET', url);\n    xhr.setRequestHeader('X-Requested-With', 'XmlHttpRequest');\n    xhr.setRequestHeader('Accept', '*/*;q=0.5, ' + options.accepts);\n\n    if(options.json) {\n      xhr.setRequestHeader('Content-type', 'application/json');\n      data = JSON.stringify(data);\n    }\n\n    var beforeSend = new CustomEvent('ajax:before', {detail: xhr, bubbles: true});\n    target.dispatchEvent(beforeSend);\n    xhr.send(data);\n\n    return xhr;\n  };\n\n  return LiteAjax;\n})();\n"
  },
  {
    "path": "lib/assets/javascripts/vanilla-ujs/method.js",
    "content": "document.addEventListener('click', function(event) {\n  var element, url, method, data, handler;\n\n  // Only left click allowed. Firefox triggers click event on right click/contextmenu.\n  if (event.button !== 0) {\n    return;\n  }\n\n  if (element = matchesSelfOrParent(event.target, 'a[data-method]')) {\n    url = element.getAttribute('href');\n    method = element.getAttribute('data-method').toUpperCase();\n    data = {};\n\n    if (CSRF.param() && CSRF.token()) {\n      data[CSRF.param()] = CSRF.token();\n    }\n\n    if (matches.call(element, 'a[data-remote]')) {\n      handler = xhr;\n    } else {\n      handler = submit;\n    }\n\n    if (handler({ url: url, method: method, data: data, target: element })) {\n      event.preventDefault();\n    } else {\n      return true;\n    }\n  }\n\n  function submit(options) {\n    var form, input, param;\n\n    if (options.method == 'GET') {\n      return false;\n    }\n\n    form = document.createElement('form');\n    form.method = 'POST';\n    form.action = options.url;\n    form.style.display = 'none';\n\n    for (param in options.data) {\n      if (Object.prototype.hasOwnProperty.call(options.data, param)) {\n        input = document.createElement('input');\n        input.setAttribute('type', 'hidden');\n        input.setAttribute('name', param);\n        input.setAttribute('value', options.data[param]);\n        form.appendChild(input);\n      }\n    }\n\n    if (options.method != 'POST') {\n      input = document.createElement('input');\n      input.setAttribute('type', 'hidden');\n      input.setAttribute('name', '_method');\n      input.setAttribute('value', options.method);\n      form.appendChild(input);\n    }\n\n    document.body.appendChild(form);\n    form.submit();\n    return true;\n  }\n\n  function xhr(options) {\n    LiteAjax.ajax(options);\n    return true;\n  }\n}, false);\n"
  },
  {
    "path": "lib/assets/javascripts/vanilla-ujs/polyfills.js",
    "content": "var matches = (function(doc) {\n  return doc.matchesSelector ||\n    doc.webkitMatchesSelector ||\n    doc.mozMatchesSelector ||\n    doc.oMatchesSelector ||\n    doc.msMatchesSelector;\n})(document.documentElement);\n\nvar matchesSelfOrParent = function (element, selector) {\n  while (!matches.call(element, selector)) {\n    element = element.parentNode;\n    if (element instanceof HTMLDocument) {\n      return null;\n    }\n  }\n  return element;\n};\n\nvar CustomEvent = function (event, params) {\n  params = params || {bubbles: false, cancelable: false, detail: undefined};\n  var evt = document.createEvent('CustomEvent');\n  evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);\n  return evt;\n};\n\nCustomEvent.prototype = window.CustomEvent.prototype;\n\nwindow.CustomEvent = CustomEvent;\n"
  },
  {
    "path": "lib/assets/javascripts/vanilla-ujs.js",
    "content": "//= require_tree ./vanilla-ujs\n"
  },
  {
    "path": "lib/vanilla/ujs/rails.rb",
    "content": "module Vanilla\n  module Rails\n    class Engine < ::Rails::Engine\n    end\n  end\nend\n"
  },
  {
    "path": "lib/vanilla/ujs/version.rb",
    "content": "module Vanilla\n  module Ujs\n    VERSION = '1.3.0'.freeze\n  end\nend\n"
  },
  {
    "path": "lib/vanilla/ujs.rb",
    "content": "require 'vanilla/ujs/version'\nrequire 'vanilla/ujs/rails'\n\nmodule Vanilla\n  module Rails\n    # Your code goes here...\n  end\nend\n"
  },
  {
    "path": "lib/vanilla-ujs.rb",
    "content": "require 'vanilla/ujs'\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"vanilla-ujs\",\n  \"version\": \"1.3.0\",\n  \"description\": \"Rails UJS in VanillaJS\",\n  \"main\": \"lib/assets/javascripts/vanilla-ujs.js\",\n  \"scripts\": {\n    \"build\": \"npm install && grunt dist\",\n    \"test\": \"grunt test\",\n    \"webtest\": \"grunt webtest\"\n  },\n  \"devDependencies\": {\n    \"body-parser\": \"~1.18.2\",\n    \"chai\": \"^4.1.2\",\n    \"express\": \"~4.16.2\",\n    \"grunt\": \"^1.0.1\",\n    \"grunt-contrib-concat\": \"*\",\n    \"grunt-contrib-jshint\": \"*\",\n    \"grunt-contrib-uglify\": \"~3.2.1\",\n    \"grunt-contrib-watch\": \"~1.0.0\",\n    \"grunt-express\": \"*\",\n    \"grunt-mocha\": \"~1.0\",\n    \"grunt-parallel\": \"^0.5.1\",\n    \"sinon\": \"^4.1.2\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/hauleth/vanilla-ujs.git\"\n  },\n  \"keywords\": [\n    \"ujs\",\n    \"rails\",\n    \"vanilla\"\n  ],\n  \"author\": \"Łukasz Niemier\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/hauleth/vanilla-ujs/issues\"\n  },\n  \"homepage\": \"https://github.com/hauleth/vanilla-ujs\"\n}\n"
  },
  {
    "path": "test/config/globals.js",
    "content": "(function () {\n  var global = typeof global !== 'undefined' ?\n               global :\n               typeof self !== 'undefined' ?\n                 self :\n                 typeof window !== 'undefined' ?\n                 window :\n                 {};\n\n  global.expect = chai.expect;\n  global.click  = function (element) {\n    var evt = document.createEvent(\"MouseEvents\");\n    evt.initMouseEvent(\"click\", true, true, window,\n    0, 0, 0, 80, 20, false, false, false, false, 0, null);\n\n    element.dispatchEvent(evt);\n  };\n  mocha.suite.beforeEach(function(done) {\n    global.iframe = document.createElement('iframe');\n    global.iframe.src   = '/fixture';\n    global.iframe.onload = function () {\n      global.iframe.onload = function () {};\n\n      global.win = function () {\n        return global.iframe.contentWindow;\n      };\n      global.doc = function () {\n        return global.iframe.contentDocument;\n      };\n\n      done();\n    };\n\n    var fixture = document.getElementById('fixture');\n    fixture.appendChild(global.iframe);\n  });\n\n  mocha.suite.afterEach(function() {\n    global.iframe.parentNode.removeChild(global.iframe);\n  });\n}).call(this);\n"
  },
  {
    "path": "test/confirm.spec.js",
    "content": "describe('Link confirmation', function () {\n  describe('<a> link', function () {\n    var a, confirm, clickLink;\n\n    beforeEach(function () {\n      win().confirm   = confirm   = sinon.stub();\n      win().clickLink = clickLink = sinon.spy();\n\n      a = document.createElement('a');\n      a.setAttribute('data-confirm', 'Lolcontent');\n      a.href = '#clicked';\n      doc().body.appendChild(a);\n    });\n\n    it('call confirm', function () {\n      click(a);\n\n      expect(confirm.called).to.be.true;\n    });\n\n    it('fire default action if confirm() returns true', function () {\n      win.confirm = confirm = confirm.returns(true);\n\n      click(a);\n\n      expect(win().location.hash).to.equal('#clicked');\n    });\n\n    it('do not fire default action if confirm() returns false', function () {\n      win.confirm = confirm = confirm.returns(false);\n\n      click(a);\n\n      expect(win().location.hash).to.not.equal('#clicked');\n    });\n\n    it('call confirm woth elements inside link', function () {\n      var i = document.createElement('i');\n      a.appendChild(i);\n\n      click(i);\n\n      expect(confirm.called).to.be.true;\n    });\n  });\n\n  // TODO: Write tests for <button>\n});\n"
  },
  {
    "path": "test/csrf.spec.js",
    "content": "describe('CSRF', function () {\n  beforeEach(function () {\n    var token, param;\n\n    token = doc().createElement('meta');\n    token.setAttribute('name', 'csrf-token');\n    token.setAttribute('content', 'CSRFToken');\n    doc().head.appendChild(token);\n\n    param = doc().createElement('meta');\n    param.setAttribute('name', 'csrf-param');\n    param.setAttribute('content', 'CSRFParam');\n    doc().head.appendChild(param);\n  });\n\n  describe('getters', function () {\n\n    it('return token value', function () {\n      expect(win().CSRF.token()).to.equal('CSRFToken');\n    });\n\n    it('return param value', function () {\n      expect(win().CSRF.param()).to.equal('CSRFParam');\n    });\n  });\n\n  describe('LiteAjax request', function () {\n    it('send valid CSRF header value', function (done) {\n      var xhr = win().LiteAjax.ajax('/xhr', {\n        success: function (req) {\n          expect(JSON.parse(req.response).csrf).to.equal('CSRFToken');\n          done();\n        }\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/disable.spec.js",
    "content": "describe('Should disable links and buttons with [data-disable-with]', function () {\n  describe('<a> link', function () {\n    var a;\n\n    beforeEach(function () {\n      a = document.createElement('a');\n      a.setAttribute('data-disable-with', 'Lolcontent');\n      a.href = '#clicked';\n      doc().body.appendChild(a);\n    });\n\n    it('when click a link', function (done) {\n      click(a);\n\n      expect(win().location.hash).to.equal('#clicked');\n      expect(a.innerText).to.equal('Lolcontent');\n\n      setTimeout(function () {\n        expect(win().location.hash).to.equal('#clicked');\n        expect(a.getAttribute('disabled')).to.equal('disabled');\n        done();\n      }, 5); // because original code has timeout too\n    });\n\n    it('when click element inside link', function (done) {\n      var i = document.createElement('i');\n      a.appendChild(i);\n\n      click(i);\n\n      expect(win().location.hash).to.equal('#clicked');\n      expect(a.innerText).to.equal('Lolcontent');\n\n      setTimeout(function () {\n        expect(a.getAttribute('disabled')).to.equal('disabled');\n        done();\n      }, 5); // because original code has timeout too\n    });\n  });\n\n  // TODO: Write tests for <button>\n});\n\ndescribe('Should disable links and buttons with [data-disable]', function () {\n  var a;\n\n  beforeEach(function () {\n    a = document.createElement('a');\n    a.innerText = 'Original'\n    a.setAttribute('data-disable', '');\n    a.href = '#clicked';\n    doc().body.appendChild(a);\n  });\n\n  it('when click a link', function (done) {\n    click(a);\n\n    expect(win().location.hash).to.equal('#clicked');\n    expect(a.innerText).to.equal('Original');\n\n    setTimeout(function () {\n      expect(a.getAttribute('disabled')).to.equal('disabled');\n      done();\n    }, 5); // because original code has timeout too\n  });\n\n  it('when click element inside link', function (done) {\n    var i = document.createElement('i');\n    a.appendChild(i);\n\n    click(i);\n\n    expect(win().location.hash).to.equal('#clicked');\n    expect(a.innerText).to.equal('Original');\n\n    setTimeout(function () {\n      expect(a.getAttribute('disabled')).to.equal('disabled');\n      done();\n    }, 5); // because original code has timeout too\n  });\n})"
  },
  {
    "path": "test/form.spec.js",
    "content": "describe('Form methods', function () {\n  var form, submit, submitForm;\n\n  beforeEach(function () {\n    form = document.createElement('form');\n    submit = document.createElement('input')\n    doc().body.appendChild(form);\n    submit.setAttribute('type', 'submit');\n    submit.setAttribute('name', 'submit');\n    submit.setAttribute('value', 'submit');\n    form.appendChild(submit)\n    win().submitForm = submitForm = sinon.spy();\n  });\n\n  describe('[method=post]', function () {\n    beforeEach(function () {\n      form.onsubmit = submitForm;\n      form.setAttribute('method', 'post');\n    });\n\n    describe('no [data-remote]', function () {\n      beforeEach(function () {\n        form.setAttribute('action', '/echo?callback=parse');\n      });\n\n      it('is submitted as a form', function (done) {\n        var url = win().location.href;\n\n        window.parse = function (json) {\n          expect(url).to.not.equal(win().location.href);\n          expect(json).to.deep.equal({\n            method: 'post',\n            path: '/echo'\n          });\n          done();\n        };\n\n        click(submit);\n\n        expect(submitForm.called).to.be.true;\n      });\n\n    });\n\n    describe('[data-remote]', function () {\n      beforeEach(function () {\n        form.setAttribute('action', '/xhr');\n        form.setAttribute('data-remote', 'true');\n      });\n\n      it('calls ajax:before event on form with bubbling enabled', function (done) {\n        var handler = function (event) {\n          expect(event.target).to.equal(form);\n          expect(event.bubbles).to.equal(true);\n\n          doc().removeEventListener('ajax:before', handler);\n          done();\n        };\n\n        doc().addEventListener('ajax:before', handler);\n\n        click(submit);\n\n        expect(submitForm.called).to.be.true;\n      });\n\n      it('calls ajax:complete event on form with bubbling enabled', function (done) {\n        var handler = function (event) {\n          expect(event.target).to.equal(form);\n          expect(event.bubbles).to.equal(true);\n\n          doc().removeEventListener('ajax:complete', handler);\n          done();\n        };\n\n        doc().addEventListener('ajax:complete', handler);\n\n        click(submit);\n\n        expect(submitForm.called).to.be.true;\n      });\n\n      it('is sent as XHR request', function (done) {\n        var url = win().location.href;\n\n        var handler = function (event) {\n          expect(url).to.equal(win().location.href);\n          expect(JSON.parse(event.detail.response)).to.deep.equal({\n            method: 'post',\n            path: '/xhr'\n          });\n\n          doc().removeEventListener('ajax:complete', handler);\n          done();\n        };\n\n        doc().addEventListener('ajax:complete', handler);\n\n        click(submit);\n\n        expect(submitForm.called).to.be.true;\n      });\n\n      it('does not pass formdata when there are no form inputs', function (done) {\n        // Inputs without name are usually not included in a form POST, we use that to simulate\n        // a request without inputs\n        submit.removeAttribute('name');\n\n        win().LiteAjax.ajax = sinon.spy();\n        click(submit);\n\n        var options = win().LiteAjax.ajax.getCall(0).args[0];\n        expect(options.data).to.equal(null);\n\n        expect(submitForm.called).to.be.true;\n        done();\n      });\n    });\n  });\n\n});\n"
  },
  {
    "path": "test/helpers/serv.js",
    "content": "var express = require('express'),\n    bodyParser = require('body-parser'),\n    fs = require('fs'),\n    path = require('path'),\n    app = express();\n\napp.use(bodyParser.urlencoded({ extended: true }));\n\nvar scripts = [\n  'polyfills',\n  'confirm',\n  'method',\n  'disable',\n  'liteajax',\n  'csrf',\n  'form'\n].map(function (s) { return ['<script src=\"/assets/javascripts/vanilla-ujs/', s, '.js\"></script>'].join(''); });\n\nvar template = function(body) {\n  return [\n    '<html><head><title>Testing…</title></head><body>',\n    body,\n    '</body></html>'\n  ].join(\"\\n\");\n};\n\napp.get('/fixture', function (req, res) {\n  res.send(template(scripts.join('')));\n});\n\napp.all('/echo', function (req, res) {\n  res.send(template([\n    '<script>window.top.',\n    req.query.callback,\n    '(',\n    JSON.stringify({\n      method: (req.body._method || req.method).toLowerCase(),\n      csrf: req.get('X-CSRF-Token'),\n      path: req.path\n    }),\n    ');</script>'\n  ].join('')));\n});\n\napp.all('/xhr', function (req, res) {\n  res.send({\n    method: (req.body._method || req.method).toLowerCase(),\n    csrf: req.get('X-CSRF-Token') || req.body[req.params['param']],\n    path: req.path\n  });\n});\n\napp.all('/assets/mocha.js', function (req, res) {\n  res.sendFile(path.resolve(__dirname, \"../../node_modules/mocha/mocha.js\"));\n});\n\napp.all('/assets/mocha.css', function (req, res) {\n  res.sendFile(path.resolve(__dirname, \"../../node_modules/mocha/mocha.css\"));\n});\n\napp.all('/assets/sinon.js', function (req, res) {\n  res.sendFile(path.resolve(__dirname, \"../../node_modules/sinon/pkg/sinon.js\"));\n});\n\napp.all('/assets/chai.js', function (req, res) {\n  res.sendFile(path.resolve(__dirname, \"../../node_modules/chai/chai.js\"));\n});\n\nmodule.exports = app;\n"
  },
  {
    "path": "test/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Mocha</title>\n    <meta charset=\"UTF-8\">\n    <script src=\"assets/mocha.js\"></script>\n    <script src=\"assets/chai.js\"></script>\n    <script src=\"assets/sinon.js\"></script>\n    <script src=\"config/globals.js\"></script>\n    <script>mocha.setup('bdd')</script>\n    <link rel=\"stylesheet\" href=\"assets/mocha.css\" />\n  </head>\n  <body>\n    <div id=\"mocha\"></div>\n    <div id=\"fixture\"></div>\n    <script src=\"liteajax.spec.js\"></script>\n    <script src=\"method.spec.js\"></script>\n    <script src=\"confirm.spec.js\"></script>\n    <script src=\"csrf.spec.js\"></script>\n    <script src=\"form.spec.js\"></script>\n    <script src=\"disable.spec.js\"></script>\n    <script>\n      window.onload = function () {\n        if (navigator.userAgent.indexOf('PhantomJS') < 0) {\n          mocha.run();\n        }\n      };\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "test/liteajax.spec.js",
    "content": "describe('LiteAjax', function () {\n  describe('success callback', function () {\n    var xhr, callbackSpy;\n\n    beforeEach(function (done) {\n      callbackSpy = sinon.spy();\n      xhr = win().LiteAjax.ajax('/echo', {\n        success: function () {\n          callbackSpy();\n          done();\n        }\n      });\n    });\n\n    it('is called on valid request', function () {\n      expect(callbackSpy.called).to.be.true;\n    });\n  });\n\n  describe('error callback', function () {\n    var xhr, callbackSpy;\n\n    beforeEach(function (done) {\n      callbackSpy = sinon.spy();\n      xhr = win().LiteAjax.ajax('/404', {\n        success: function () { done(); },\n        error: function () {\n          callbackSpy();\n          done();\n        }\n      });\n    });\n\n    it('is called on invalid request', function () {\n      expect(callbackSpy.called).to.be.true;\n    });\n  });\n});\n"
  },
  {
    "path": "test/method.spec.js",
    "content": "describe('Link methods', function () {\n  var a, clickLink;\n\n  beforeEach(function () {\n    a = document.createElement('a');\n    doc().body.appendChild(a);\n    win().clickLink = clickLink = sinon.spy();\n  });\n\n  describe('no [data-method]', function () {\n    beforeEach(function () {\n      a.onclick = clickLink;\n    });\n\n    it('is sent normally', function () {\n      click(a);\n\n      expect(clickLink.called).to.be.true;\n    });\n  });\n\n  describe('[data-method=get]', function () {\n    beforeEach(function () {\n      a.onclick = clickLink;\n      a.setAttribute('data-method', 'get');\n    });\n\n    it('is sent normally', function () {\n      click(a);\n\n      expect(clickLink.called).to.be.true;\n    });\n  });\n\n  describe('[data-method=post]', function () {\n    beforeEach(function () {\n      a.setAttribute('data-method', 'post');\n    });\n\n    describe('no [data-remote]', function () {\n      beforeEach(function () {\n        a.setAttribute('href', '/echo?callback=parse');\n      });\n\n      it('is sent as POST form', function (done) {\n        var url = win().location.href;\n\n        window.parse = function (json) {\n          expect(url).to.not.equal(win().location.href);\n          expect(json).to.deep.equal({\n            method: 'post',\n            path: '/echo'\n          });\n          done();\n        };\n\n        click(a);\n      });\n\n      it('is sent when clicking child element inside link', function (done) {\n        var url = win().location.href;\n        var i = document.createElement('i');\n        a.appendChild(i);\n        window.parse = function (json) {\n          expect(url).to.not.equal(win().location.href);\n          expect(json).to.deep.equal({\n            method: 'post',\n            path: '/echo'\n          });\n          done();\n        };\n\n        click(i);\n      });\n    });\n\n    describe('[data-remote]', function () {\n      beforeEach(function () {\n        a.setAttribute('href', '/xhr');\n        a.setAttribute('data-remote', 'true');\n      });\n\n      it('calls ajax:before event on a element with bubbling enabled', function (done) {\n        var handler = function (event) {\n          expect(event.target).to.equal(a);\n          expect(event.bubbles).to.equal(true);\n\n          doc().removeEventListener('ajax:before', handler);\n          done();\n        };\n\n        doc().addEventListener('ajax:before', handler);\n\n        click(a);\n      });\n\n      it('calls ajax:complete event on a element with bubbling enabled', function (done) {\n        var handler = function (event) {\n          expect(event.target).to.equal(a);\n          expect(event.bubbles).to.equal(true);\n\n          doc().removeEventListener('ajax:complete', handler);\n          done();\n        };\n\n        doc().addEventListener('ajax:complete', handler);\n\n        click(a);\n      });\n\n      it('is sent as XHR request', function (done) {\n        var url = win().location.href;\n\n        var handler = function (event) {\n          expect(url).to.equal(win().location.href);\n          expect(JSON.parse(event.detail.response)).to.deep.equal({\n            method: 'post',\n            path: '/xhr'\n          });\n\n          doc().removeEventListener('ajax:complete', handler);\n          done();\n        };\n\n        doc().addEventListener('ajax:complete', handler);\n\n        click(a);\n      });\n    });\n  });\n\n  describe('[data-method=delete]', function () {\n    beforeEach(function () {\n      a.setAttribute('href', '/echo?callback=parse');\n      a.setAttribute('data-method', 'delete');\n    });\n\n    it('is sent with DELETE method', function (done) {\n      window.parse = function (json) {\n        expect(json).to.deep.equal({\n          method: 'delete',\n          path: '/echo'\n        });\n        done();\n      };\n\n      click(a);\n    });\n  });\n\n  describe('[data-method=put]', function () {\n    beforeEach(function () {\n      a.setAttribute('href', '/echo?callback=parse');\n      a.setAttribute('data-method', 'put');\n    });\n\n    it('is sent with PUT method', function (done) {\n      window.parse = function (json) {\n        expect(json).to.deep.equal({\n          method: 'put',\n          path: '/echo'\n        });\n        done();\n      };\n\n      click(a);\n    });\n  });\n});\n"
  },
  {
    "path": "vanilla-ujs.gemspec",
    "content": "# coding: utf-8\nlib = File.expand_path('../lib', __FILE__)\n$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)\nrequire 'vanilla/ujs/version'\n\nGem::Specification.new do |spec|\n  spec.name          = 'vanilla-ujs'\n  spec.version       = Vanilla::Ujs::VERSION\n  spec.authors       = ['Łukasz Jan Niemier', 'Alex Tsukurov', 'Kirill Pimenov']\n  spec.email         = ['lukasz@niemier.pl', 'me@ximik.net']\n\n  spec.summary       = 'UJS without jQuery dependency'\n  spec.description   = 'This gem provides Rails UJS features without jQuery library.'\n  spec.homepage      = 'https://github.com/hauleth/vanilla-ujs'\n  spec.license       = 'MIT'\n\n  spec.files         = `git ls-files -z`.split(\"\\x0\").reject { |f| f.match(%r{^(test)/}) }\n  spec.require_paths = ['lib']\n\n  spec.required_rubygems_version = '>= 1.3.6'\n\n  spec.add_dependency 'railties', '>= 4.2.0'\n\n  spec.add_development_dependency 'bundler', '~> 1.11'\nend\n"
  }
]