unpoly-rails 0.20.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of unpoly-rails might be problematic. Click here for more details.

Files changed (339) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.ruby-version +2 -0
  4. data/.yardopts +1 -0
  5. data/CHANGELOG.md +577 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +42 -0
  8. data/LICENSE +22 -0
  9. data/README.md +75 -0
  10. data/README_RAILS.md +116 -0
  11. data/Rakefile +45 -0
  12. data/bower.json +26 -0
  13. data/design/animation-ghosting.txt +72 -0
  14. data/design/design.txt +34 -0
  15. data/design/draft.html.erb +48 -0
  16. data/design/draft.rb +9 -0
  17. data/design/ghost-debugging.txt +118 -0
  18. data/design/homepage.txt +236 -0
  19. data/design/rename.txt +0 -0
  20. data/dist/unpoly-bootstrap3.css +5 -0
  21. data/dist/unpoly-bootstrap3.js +28 -0
  22. data/dist/unpoly-bootstrap3.min.css +1 -0
  23. data/dist/unpoly-bootstrap3.min.js +1 -0
  24. data/dist/unpoly.css +98 -0
  25. data/dist/unpoly.js +9041 -0
  26. data/dist/unpoly.min.css +1 -0
  27. data/dist/unpoly.min.js +3 -0
  28. data/lib/assets/javascripts/unpoly-bootstrap3.js.coffee +2 -0
  29. data/lib/assets/javascripts/unpoly-bootstrap3/form-ext.js.coffee +1 -0
  30. data/lib/assets/javascripts/unpoly-bootstrap3/layout-ext.js.coffee +5 -0
  31. data/lib/assets/javascripts/unpoly-bootstrap3/modal-ext.js.coffee +11 -0
  32. data/lib/assets/javascripts/unpoly-bootstrap3/navigation-ext.js.coffee +3 -0
  33. data/lib/assets/javascripts/unpoly.js.coffee +20 -0
  34. data/lib/assets/javascripts/unpoly/browser.js.coffee +252 -0
  35. data/lib/assets/javascripts/unpoly/bus.js.coffee +393 -0
  36. data/lib/assets/javascripts/unpoly/flow.js.coffee +809 -0
  37. data/lib/assets/javascripts/unpoly/form.js.coffee +864 -0
  38. data/lib/assets/javascripts/unpoly/history.js.coffee +204 -0
  39. data/lib/assets/javascripts/unpoly/layout.js.coffee +529 -0
  40. data/lib/assets/javascripts/unpoly/link.js.coffee +496 -0
  41. data/lib/assets/javascripts/unpoly/log.js.coffee +73 -0
  42. data/lib/assets/javascripts/unpoly/modal.js.coffee +509 -0
  43. data/lib/assets/javascripts/unpoly/module.js.coffee +4 -0
  44. data/lib/assets/javascripts/unpoly/motion.js.coffee +690 -0
  45. data/lib/assets/javascripts/unpoly/navigation.js.coffee +219 -0
  46. data/lib/assets/javascripts/unpoly/popup.js.coffee +409 -0
  47. data/lib/assets/javascripts/unpoly/proxy.js.coffee +521 -0
  48. data/lib/assets/javascripts/unpoly/rails.js.coffee +36 -0
  49. data/lib/assets/javascripts/unpoly/syntax.js.coffee +337 -0
  50. data/lib/assets/javascripts/unpoly/tooltip.js.coffee +183 -0
  51. data/lib/assets/javascripts/unpoly/util.js.coffee +1593 -0
  52. data/lib/assets/stylesheets/unpoly-bootstrap3.css.sass +1 -0
  53. data/lib/assets/stylesheets/unpoly-bootstrap3/modal-ext.css.sass +9 -0
  54. data/lib/assets/stylesheets/unpoly.css.sass +1 -0
  55. data/lib/assets/stylesheets/unpoly/close.css.sass +2 -0
  56. data/lib/assets/stylesheets/unpoly/error.css.sass +15 -0
  57. data/lib/assets/stylesheets/unpoly/link.css.sass +2 -0
  58. data/lib/assets/stylesheets/unpoly/modal.css.sass +69 -0
  59. data/lib/assets/stylesheets/unpoly/popup.css.sass +8 -0
  60. data/lib/assets/stylesheets/unpoly/tooltip.css.sass +42 -0
  61. data/lib/unpoly-rails.rb +7 -0
  62. data/lib/unpoly/rails/engine.rb +6 -0
  63. data/lib/unpoly/rails/inspector.rb +78 -0
  64. data/lib/unpoly/rails/inspector_accessor.rb +30 -0
  65. data/lib/unpoly/rails/request_echo_headers.rb +27 -0
  66. data/lib/unpoly/rails/request_method_cookie.rb +36 -0
  67. data/lib/unpoly/rails/version.rb +9 -0
  68. data/spec_app/.firefox-version +1 -0
  69. data/spec_app/.gitignore +17 -0
  70. data/spec_app/.rspec +2 -0
  71. data/spec_app/Bowerfile +3 -0
  72. data/spec_app/Gemfile +29 -0
  73. data/spec_app/Gemfile.lock +212 -0
  74. data/spec_app/README.rdoc +28 -0
  75. data/spec_app/Rakefile +6 -0
  76. data/spec_app/app/assets/images/.keep +0 -0
  77. data/spec_app/app/assets/javascripts/application.js +20 -0
  78. data/spec_app/app/assets/stylesheets/application.css +17 -0
  79. data/spec_app/app/assets/stylesheets/blocks/card.css.sass +11 -0
  80. data/spec_app/app/assets/stylesheets/blocks/controls.css.sass +7 -0
  81. data/spec_app/app/assets/stylesheets/blocks/menu.css.sass +13 -0
  82. data/spec_app/app/assets/stylesheets/blocks/panel.css.sass +8 -0
  83. data/spec_app/app/assets/stylesheets/jasmine_specs.css +5 -0
  84. data/spec_app/app/controllers/application_controller.rb +6 -0
  85. data/spec_app/app/controllers/concerns/.keep +0 -0
  86. data/spec_app/app/controllers/test_controller.rb +28 -0
  87. data/spec_app/app/helpers/application_helper.rb +2 -0
  88. data/spec_app/app/mailers/.keep +0 -0
  89. data/spec_app/app/models/concerns/.keep +0 -0
  90. data/spec_app/app/views/layouts/application.html.erb +12 -0
  91. data/spec_app/bin/bundle +3 -0
  92. data/spec_app/bin/rails +8 -0
  93. data/spec_app/bin/rake +8 -0
  94. data/spec_app/bin/setup +29 -0
  95. data/spec_app/bin/spring +18 -0
  96. data/spec_app/config.ru +4 -0
  97. data/spec_app/config/application.rb +26 -0
  98. data/spec_app/config/boot.rb +3 -0
  99. data/spec_app/config/database.yml +25 -0
  100. data/spec_app/config/environment.rb +5 -0
  101. data/spec_app/config/environments/development.rb +41 -0
  102. data/spec_app/config/environments/production.rb +79 -0
  103. data/spec_app/config/environments/test.rb +42 -0
  104. data/spec_app/config/initializers/assets.rb +11 -0
  105. data/spec_app/config/initializers/backtrace_silencers.rb +7 -0
  106. data/spec_app/config/initializers/bower_rails.rb +16 -0
  107. data/spec_app/config/initializers/cookies_serializer.rb +3 -0
  108. data/spec_app/config/initializers/filter_parameter_logging.rb +4 -0
  109. data/spec_app/config/initializers/inflections.rb +16 -0
  110. data/spec_app/config/initializers/mime_types.rb +4 -0
  111. data/spec_app/config/initializers/session_store.rb +3 -0
  112. data/spec_app/config/initializers/wrap_parameters.rb +14 -0
  113. data/spec_app/config/locales/en.yml +23 -0
  114. data/spec_app/config/routes.rb +8 -0
  115. data/spec_app/config/secrets.yml +22 -0
  116. data/spec_app/db/schema.rb +23 -0
  117. data/spec_app/db/seeds.rb +7 -0
  118. data/spec_app/lib/assets/.keep +0 -0
  119. data/spec_app/lib/tasks/.keep +0 -0
  120. data/spec_app/lib/tasks/cucumber.rake +65 -0
  121. data/spec_app/log/.keep +0 -0
  122. data/spec_app/public/404.html +67 -0
  123. data/spec_app/public/422.html +67 -0
  124. data/spec_app/public/500.html +66 -0
  125. data/spec_app/public/favicon.ico +0 -0
  126. data/spec_app/public/robots.txt +5 -0
  127. data/spec_app/script/cucumber +10 -0
  128. data/spec_app/spec/controllers/test_controller_spec.rb +76 -0
  129. data/spec_app/spec/javascripts/helpers/append_fixture.js.coffee +8 -0
  130. data/spec_app/spec/javascripts/helpers/browser_switches.js.coffee +9 -0
  131. data/spec_app/spec/javascripts/helpers/index.js.coffee +1 -0
  132. data/spec_app/spec/javascripts/helpers/knife.js.coffee +69 -0
  133. data/spec_app/spec/javascripts/helpers/last_request.js.coffee +22 -0
  134. data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +5 -0
  135. data/spec_app/spec/javascripts/helpers/mock_clock.js.coffee +2 -0
  136. data/spec_app/spec/javascripts/helpers/remove_body_margin.js.coffee +5 -0
  137. data/spec_app/spec/javascripts/helpers/reset_knife.js.coffee +2 -0
  138. data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +8 -0
  139. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +4 -0
  140. data/spec_app/spec/javascripts/helpers/restore_body_scroll.js.coffee +2 -0
  141. data/spec_app/spec/javascripts/helpers/set_timer.js.coffee +3 -0
  142. data/spec_app/spec/javascripts/helpers/to_be_around.js.coffee +5 -0
  143. data/spec_app/spec/javascripts/helpers/to_be_blank.js.coffee +5 -0
  144. data/spec_app/spec/javascripts/helpers/to_be_given.js.coffee +5 -0
  145. data/spec_app/spec/javascripts/helpers/to_be_missing.js.coffee +5 -0
  146. data/spec_app/spec/javascripts/helpers/to_be_present.js.coffee +5 -0
  147. data/spec_app/spec/javascripts/helpers/to_end_with.js.coffee +8 -0
  148. data/spec_app/spec/javascripts/helpers/to_equal_jquery.js.coffee +9 -0
  149. data/spec_app/spec/javascripts/helpers/to_have_request_method.js.coffee +8 -0
  150. data/spec_app/spec/javascripts/helpers/trigger.js.coffee +68 -0
  151. data/spec_app/spec/javascripts/support/jasmine.yml +51 -0
  152. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +122 -0
  153. data/spec_app/spec/javascripts/up/flow_spec.js.coffee +755 -0
  154. data/spec_app/spec/javascripts/up/form_spec.js.coffee +471 -0
  155. data/spec_app/spec/javascripts/up/history_spec.js.coffee +96 -0
  156. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +318 -0
  157. data/spec_app/spec/javascripts/up/link_spec.js.coffee +340 -0
  158. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +265 -0
  159. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +306 -0
  160. data/spec_app/spec/javascripts/up/navigation_spec.js.coffee +132 -0
  161. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +220 -0
  162. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +371 -0
  163. data/spec_app/spec/javascripts/up/rails_spec.js.coffee +101 -0
  164. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +99 -0
  165. data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +60 -0
  166. data/spec_app/spec/javascripts/up/util_spec.js.coffee +325 -0
  167. data/spec_app/spec/spec_helper.rb +62 -0
  168. data/spec_app/test/controllers/.keep +0 -0
  169. data/spec_app/test/fixtures/.keep +0 -0
  170. data/spec_app/test/helpers/.keep +0 -0
  171. data/spec_app/test/integration/.keep +0 -0
  172. data/spec_app/test/mailers/.keep +0 -0
  173. data/spec_app/test/models/.keep +0 -0
  174. data/spec_app/test/test_helper.rb +10 -0
  175. data/spec_app/vendor/assets/.bowerrc +3 -0
  176. data/spec_app/vendor/assets/bower.json +8 -0
  177. data/spec_app/vendor/assets/bower_components/jasmine-ajax/.bower.json +43 -0
  178. data/spec_app/vendor/assets/bower_components/jasmine-ajax/MIT.LICENSE +20 -0
  179. data/spec_app/vendor/assets/bower_components/jasmine-ajax/README.markdown +289 -0
  180. data/spec_app/vendor/assets/bower_components/jasmine-ajax/bower.json +35 -0
  181. data/spec_app/vendor/assets/bower_components/jasmine-ajax/lib/mock-ajax.js +733 -0
  182. data/spec_app/vendor/assets/bower_components/jasmine-ajax/package.json +26 -0
  183. data/spec_app/vendor/assets/bower_components/jasmine-ajax/release_notes/2.0.2.md +50 -0
  184. data/spec_app/vendor/assets/bower_components/jasmine-ajax/release_notes/2.99.md +14 -0
  185. data/spec_app/vendor/assets/bower_components/jasmine-ajax/release_notes/3.0.md +28 -0
  186. data/spec_app/vendor/assets/bower_components/jasmine-ajax/release_notes/3.1.0.md +24 -0
  187. data/spec_app/vendor/assets/bower_components/jasmine-ajax/release_notes/3.1.1.md +23 -0
  188. data/spec_app/vendor/assets/bower_components/jasmine-ajax/release_notes/3.2.0.md +20 -0
  189. data/spec_app/vendor/assets/bower_components/jasmine-fixture/.bower.json +27 -0
  190. data/spec_app/vendor/assets/bower_components/jasmine-fixture/.gitignore +8 -0
  191. data/spec_app/vendor/assets/bower_components/jasmine-fixture/.npmignore +8 -0
  192. data/spec_app/vendor/assets/bower_components/jasmine-fixture/LICENSE.txt +24 -0
  193. data/spec_app/vendor/assets/bower_components/jasmine-fixture/README.md +118 -0
  194. data/spec_app/vendor/assets/bower_components/jasmine-fixture/bower.json +17 -0
  195. data/spec_app/vendor/assets/bower_components/jasmine-fixture/dist/jasmine-fixture.js +433 -0
  196. data/spec_app/vendor/assets/bower_components/jasmine-fixture/dist/jasmine-fixture.min.js +5 -0
  197. data/spec_app/vendor/assets/bower_components/jasmine-fixture/spec-e2e/basic-usage-spec.coffee +14 -0
  198. data/spec_app/vendor/assets/bower_components/jasmine-fixture/spec-e2e/helpers/invariants.coffee +17 -0
  199. data/spec_app/vendor/assets/bower_components/jasmine-fixture/spec-e2e/helpers/spec-within-a-spec.coffee +24 -0
  200. data/spec_app/vendor/assets/bower_components/jasmine-fixture/spec-e2e/helpers/tmp-files.coffee +8 -0
  201. data/spec_app/vendor/assets/bower_components/jasmine-fixture/spec-e2e/support/jasmine1-testem-config.json +11 -0
  202. data/spec_app/vendor/assets/bower_components/jasmine-fixture/spec-e2e/support/jasmine2-testem-config.json +11 -0
  203. data/spec_app/vendor/assets/bower_components/jasmine-fixture/vendor/js/jquery-1.11.0.js +10337 -0
  204. data/spec_app/vendor/assets/bower_components/jasmine-fixture/vendor/js/jquery-1.8.3.js +9472 -0
  205. data/spec_app/vendor/assets/bower_components/jasmine-fixture/vendor/js/jquery-2.1.0.js +9111 -0
  206. data/spec_app/vendor/assets/bower_components/jasmine-jquery/.bower.json +26 -0
  207. data/spec_app/vendor/assets/bower_components/jasmine-jquery/CONTRIBUTING.md +28 -0
  208. data/spec_app/vendor/assets/bower_components/jasmine-jquery/Gruntfile.js +49 -0
  209. data/spec_app/vendor/assets/bower_components/jasmine-jquery/LICENSE +20 -0
  210. data/spec_app/vendor/assets/bower_components/jasmine-jquery/README.md +367 -0
  211. data/spec_app/vendor/assets/bower_components/jasmine-jquery/bower.json +15 -0
  212. data/spec_app/vendor/assets/bower_components/jasmine-jquery/lib/jasmine-jquery.js +838 -0
  213. data/spec_app/vendor/assets/bower_components/jasmine-jquery/package.json +35 -0
  214. data/spec_app/vendor/assets/bower_components/jasmine-jquery/spec/fixtures/fixture_with_checkbox_with_checked.html +6 -0
  215. data/spec_app/vendor/assets/bower_components/jasmine-jquery/spec/fixtures/fixture_with_javascript.html +1 -0
  216. data/spec_app/vendor/assets/bower_components/jasmine-jquery/spec/fixtures/fixture_with_javascript_block.html +1 -0
  217. data/spec_app/vendor/assets/bower_components/jasmine-jquery/spec/fixtures/javascripts/jasmine_javascript_click.js +1 -0
  218. data/spec_app/vendor/assets/bower_components/jasmine-jquery/spec/fixtures/javascripts/jasmine_javascript_hover.js +1 -0
  219. data/spec_app/vendor/assets/bower_components/jasmine-jquery/spec/fixtures/json/jasmine_json_test.json +1 -0
  220. data/spec_app/vendor/assets/bower_components/jasmine-jquery/spec/fixtures/real_non_mocked_fixture.html +1 -0
  221. data/spec_app/vendor/assets/bower_components/jasmine-jquery/spec/fixtures/real_non_mocked_fixture_style.css +1 -0
  222. data/spec_app/vendor/assets/bower_components/jasmine-jquery/spec/suites/jasmine-jquery-spec.js +1842 -0
  223. data/spec_app/vendor/assets/bower_components/jasmine/.bower.json +50 -0
  224. data/spec_app/vendor/assets/bower_components/jasmine/CONTRIBUTING.md +130 -0
  225. data/spec_app/vendor/assets/bower_components/jasmine/GOALS_2.0.md +64 -0
  226. data/spec_app/vendor/assets/bower_components/jasmine/MANIFEST.in +5 -0
  227. data/spec_app/vendor/assets/bower_components/jasmine/MIT.LICENSE +20 -0
  228. data/spec_app/vendor/assets/bower_components/jasmine/README.md +73 -0
  229. data/spec_app/vendor/assets/bower_components/jasmine/RELEASE.md +73 -0
  230. data/spec_app/vendor/assets/bower_components/jasmine/bower.json +41 -0
  231. data/spec_app/vendor/assets/bower_components/jasmine/images/jasmine-horizontal.png +0 -0
  232. data/spec_app/vendor/assets/bower_components/jasmine/images/jasmine-horizontal.svg +102 -0
  233. data/spec_app/vendor/assets/bower_components/jasmine/images/jasmine_favicon.png +0 -0
  234. data/spec_app/vendor/assets/bower_components/jasmine/lib/console/console.js +190 -0
  235. data/spec_app/vendor/assets/bower_components/jasmine/lib/jasmine-core.js +37 -0
  236. data/spec_app/vendor/assets/bower_components/jasmine/lib/jasmine-core/boot.js +143 -0
  237. data/spec_app/vendor/assets/bower_components/jasmine/lib/jasmine-core/example/node_example/lib/jasmine_examples/Player.js +24 -0
  238. data/spec_app/vendor/assets/bower_components/jasmine/lib/jasmine-core/example/node_example/lib/jasmine_examples/Song.js +9 -0
  239. data/spec_app/vendor/assets/bower_components/jasmine/lib/jasmine-core/jasmine-html.js +446 -0
  240. data/spec_app/vendor/assets/bower_components/jasmine/lib/jasmine-core/jasmine.css +58 -0
  241. data/spec_app/vendor/assets/bower_components/jasmine/lib/jasmine-core/jasmine.js +3298 -0
  242. data/spec_app/vendor/assets/bower_components/jasmine/lib/jasmine-core/json2.js +489 -0
  243. data/spec_app/vendor/assets/bower_components/jasmine/lib/jasmine-core/node_boot.js +41 -0
  244. data/spec_app/vendor/assets/bower_components/jasmine/package.json +34 -0
  245. data/spec_app/vendor/assets/bower_components/jasmine/requirements.txt +1 -0
  246. data/spec_app/vendor/assets/bower_components/jquery/.bower.json +38 -0
  247. data/spec_app/vendor/assets/bower_components/jquery/MIT-LICENSE.txt +21 -0
  248. data/spec_app/vendor/assets/bower_components/jquery/bower.json +28 -0
  249. data/spec_app/vendor/assets/bower_components/jquery/dist/jquery.js +9210 -0
  250. data/spec_app/vendor/assets/bower_components/jquery/dist/jquery.min.js +5 -0
  251. data/spec_app/vendor/assets/bower_components/jquery/dist/jquery.min.map +1 -0
  252. data/spec_app/vendor/assets/bower_components/jquery/src/ajax.js +786 -0
  253. data/spec_app/vendor/assets/bower_components/jquery/src/ajax/jsonp.js +89 -0
  254. data/spec_app/vendor/assets/bower_components/jquery/src/ajax/load.js +75 -0
  255. data/spec_app/vendor/assets/bower_components/jquery/src/ajax/parseJSON.js +13 -0
  256. data/spec_app/vendor/assets/bower_components/jquery/src/ajax/parseXML.js +28 -0
  257. data/spec_app/vendor/assets/bower_components/jquery/src/ajax/script.js +64 -0
  258. data/spec_app/vendor/assets/bower_components/jquery/src/ajax/var/nonce.js +5 -0
  259. data/spec_app/vendor/assets/bower_components/jquery/src/ajax/var/rquery.js +3 -0
  260. data/spec_app/vendor/assets/bower_components/jquery/src/ajax/xhr.js +136 -0
  261. data/spec_app/vendor/assets/bower_components/jquery/src/attributes.js +11 -0
  262. data/spec_app/vendor/assets/bower_components/jquery/src/attributes/attr.js +141 -0
  263. data/spec_app/vendor/assets/bower_components/jquery/src/attributes/classes.js +158 -0
  264. data/spec_app/vendor/assets/bower_components/jquery/src/attributes/prop.js +94 -0
  265. data/spec_app/vendor/assets/bower_components/jquery/src/attributes/support.js +35 -0
  266. data/spec_app/vendor/assets/bower_components/jquery/src/attributes/val.js +161 -0
  267. data/spec_app/vendor/assets/bower_components/jquery/src/callbacks.js +205 -0
  268. data/spec_app/vendor/assets/bower_components/jquery/src/core.js +502 -0
  269. data/spec_app/vendor/assets/bower_components/jquery/src/core/access.js +60 -0
  270. data/spec_app/vendor/assets/bower_components/jquery/src/core/init.js +123 -0
  271. data/spec_app/vendor/assets/bower_components/jquery/src/core/parseHTML.js +39 -0
  272. data/spec_app/vendor/assets/bower_components/jquery/src/core/ready.js +97 -0
  273. data/spec_app/vendor/assets/bower_components/jquery/src/core/var/rsingleTag.js +4 -0
  274. data/spec_app/vendor/assets/bower_components/jquery/src/css.js +450 -0
  275. data/spec_app/vendor/assets/bower_components/jquery/src/css/addGetHookIf.js +22 -0
  276. data/spec_app/vendor/assets/bower_components/jquery/src/css/curCSS.js +57 -0
  277. data/spec_app/vendor/assets/bower_components/jquery/src/css/defaultDisplay.js +70 -0
  278. data/spec_app/vendor/assets/bower_components/jquery/src/css/hiddenVisibleSelectors.js +15 -0
  279. data/spec_app/vendor/assets/bower_components/jquery/src/css/support.js +96 -0
  280. data/spec_app/vendor/assets/bower_components/jquery/src/css/swap.js +28 -0
  281. data/spec_app/vendor/assets/bower_components/jquery/src/css/var/cssExpand.js +3 -0
  282. data/spec_app/vendor/assets/bower_components/jquery/src/css/var/getStyles.js +12 -0
  283. data/spec_app/vendor/assets/bower_components/jquery/src/css/var/isHidden.js +13 -0
  284. data/spec_app/vendor/assets/bower_components/jquery/src/css/var/rmargin.js +3 -0
  285. data/spec_app/vendor/assets/bower_components/jquery/src/css/var/rnumnonpx.js +5 -0
  286. data/spec_app/vendor/assets/bower_components/jquery/src/data.js +178 -0
  287. data/spec_app/vendor/assets/bower_components/jquery/src/data/Data.js +181 -0
  288. data/spec_app/vendor/assets/bower_components/jquery/src/data/accepts.js +20 -0
  289. data/spec_app/vendor/assets/bower_components/jquery/src/data/var/data_priv.js +5 -0
  290. data/spec_app/vendor/assets/bower_components/jquery/src/data/var/data_user.js +5 -0
  291. data/spec_app/vendor/assets/bower_components/jquery/src/deferred.js +149 -0
  292. data/spec_app/vendor/assets/bower_components/jquery/src/deprecated.js +13 -0
  293. data/spec_app/vendor/assets/bower_components/jquery/src/dimensions.js +50 -0
  294. data/spec_app/vendor/assets/bower_components/jquery/src/effects.js +648 -0
  295. data/spec_app/vendor/assets/bower_components/jquery/src/effects/Tween.js +114 -0
  296. data/spec_app/vendor/assets/bower_components/jquery/src/effects/animatedSelector.js +13 -0
  297. data/spec_app/vendor/assets/bower_components/jquery/src/event.js +868 -0
  298. data/spec_app/vendor/assets/bower_components/jquery/src/event/ajax.js +13 -0
  299. data/spec_app/vendor/assets/bower_components/jquery/src/event/alias.js +39 -0
  300. data/spec_app/vendor/assets/bower_components/jquery/src/event/support.js +9 -0
  301. data/spec_app/vendor/assets/bower_components/jquery/src/exports/amd.js +24 -0
  302. data/spec_app/vendor/assets/bower_components/jquery/src/exports/global.js +32 -0
  303. data/spec_app/vendor/assets/bower_components/jquery/src/intro.js +44 -0
  304. data/spec_app/vendor/assets/bower_components/jquery/src/jquery.js +37 -0
  305. data/spec_app/vendor/assets/bower_components/jquery/src/manipulation.js +580 -0
  306. data/spec_app/vendor/assets/bower_components/jquery/src/manipulation/_evalUrl.js +18 -0
  307. data/spec_app/vendor/assets/bower_components/jquery/src/manipulation/support.js +32 -0
  308. data/spec_app/vendor/assets/bower_components/jquery/src/manipulation/var/rcheckableType.js +3 -0
  309. data/spec_app/vendor/assets/bower_components/jquery/src/offset.js +207 -0
  310. data/spec_app/vendor/assets/bower_components/jquery/src/outro.js +1 -0
  311. data/spec_app/vendor/assets/bower_components/jquery/src/queue.js +142 -0
  312. data/spec_app/vendor/assets/bower_components/jquery/src/queue/delay.js +22 -0
  313. data/spec_app/vendor/assets/bower_components/jquery/src/selector-native.js +172 -0
  314. data/spec_app/vendor/assets/bower_components/jquery/src/selector-sizzle.js +14 -0
  315. data/spec_app/vendor/assets/bower_components/jquery/src/selector.js +1 -0
  316. data/spec_app/vendor/assets/bower_components/jquery/src/serialize.js +111 -0
  317. data/spec_app/vendor/assets/bower_components/jquery/src/sizzle/dist/sizzle.js +2067 -0
  318. data/spec_app/vendor/assets/bower_components/jquery/src/sizzle/dist/sizzle.min.js +3 -0
  319. data/spec_app/vendor/assets/bower_components/jquery/src/sizzle/dist/sizzle.min.map +1 -0
  320. data/spec_app/vendor/assets/bower_components/jquery/src/traversing.js +199 -0
  321. data/spec_app/vendor/assets/bower_components/jquery/src/traversing/findFilter.js +100 -0
  322. data/spec_app/vendor/assets/bower_components/jquery/src/traversing/var/rneedsContext.js +6 -0
  323. data/spec_app/vendor/assets/bower_components/jquery/src/var/arr.js +3 -0
  324. data/spec_app/vendor/assets/bower_components/jquery/src/var/class2type.js +4 -0
  325. data/spec_app/vendor/assets/bower_components/jquery/src/var/concat.js +5 -0
  326. data/spec_app/vendor/assets/bower_components/jquery/src/var/hasOwn.js +5 -0
  327. data/spec_app/vendor/assets/bower_components/jquery/src/var/indexOf.js +5 -0
  328. data/spec_app/vendor/assets/bower_components/jquery/src/var/pnum.js +3 -0
  329. data/spec_app/vendor/assets/bower_components/jquery/src/var/push.js +5 -0
  330. data/spec_app/vendor/assets/bower_components/jquery/src/var/rnotwhite.js +3 -0
  331. data/spec_app/vendor/assets/bower_components/jquery/src/var/slice.js +5 -0
  332. data/spec_app/vendor/assets/bower_components/jquery/src/var/strundefined.js +3 -0
  333. data/spec_app/vendor/assets/bower_components/jquery/src/var/support.js +4 -0
  334. data/spec_app/vendor/assets/bower_components/jquery/src/var/toString.js +5 -0
  335. data/spec_app/vendor/assets/bower_components/jquery/src/wrap.js +79 -0
  336. data/spec_app/vendor/assets/javascripts/.keep +0 -0
  337. data/spec_app/vendor/assets/stylesheets/.keep +0 -0
  338. data/unpoly-rails.gemspec +24 -0
  339. metadata +423 -0
@@ -0,0 +1,809 @@
1
+ ###*
2
+ Changing page fragments programmatically
3
+ ========================================
4
+
5
+ This module contains Unpoly's core functions to [change](/up.replace) or [destroy](/up.destroy)
6
+ page fragments via Javascript.
7
+
8
+ All the other Unpoly modules (like [`up.link`](/up.link) or [`up.modal`](/up.modal))
9
+ are based on this module.
10
+
11
+ @class up.flow
12
+ ###
13
+ up.flow = (($) ->
14
+
15
+ u = up.util
16
+
17
+ setSource = (element, sourceUrl) ->
18
+ $element = $(element)
19
+ sourceUrl = u.normalizeUrl(sourceUrl) if u.isPresent(sourceUrl)
20
+ $element.attr("up-source", sourceUrl)
21
+
22
+ ###*
23
+ Returns the URL the given element was retrieved from.
24
+
25
+ @method up.flow.source
26
+ @param {String|Element|jQuery} selectorOrElement
27
+ @experimental
28
+ ###
29
+ source = (selectorOrElement) ->
30
+ $element = $(selectorOrElement).closest('[up-source]')
31
+ u.presence($element.attr("up-source")) || up.browser.url()
32
+
33
+ ###*
34
+ Resolves the given selector (which might contain `&` references)
35
+ to an absolute selector.
36
+
37
+ @function up.flow.resolveSelector
38
+ @param {String|Element|jQuery} selectorOrElement
39
+ @param {String|Element|jQuery} origin
40
+ The element that this selector resolution is relative to.
41
+ That element's selector will be substituted for `&`.
42
+ @internal
43
+ ###
44
+ resolveSelector = (selectorOrElement, origin) ->
45
+ if u.isString(selectorOrElement)
46
+ selector = selectorOrElement
47
+ if u.contains(selector, '&')
48
+ if origin
49
+ originSelector = u.selectorForElement(origin)
50
+ selector = selector.replace(/\&/, originSelector)
51
+ else
52
+ u.error("Found origin reference (%s) in selector %s, but options.origin is missing", '&', selector)
53
+ else
54
+ selector = u.selectorForElement(selectorOrElement)
55
+ selector
56
+
57
+ ###*
58
+ Replaces elements on the current page with corresponding elements
59
+ from a new page fetched from the server.
60
+
61
+ The current and new elements must both match the given CSS selector.
62
+
63
+ The UJS variant of this is the [`a[up-target]`](/a-up-target) selector.
64
+
65
+ \#\#\#\# Example
66
+
67
+ Let's say your curent HTML looks like this:
68
+
69
+ <div class="one">old one</div>
70
+ <div class="two">old two</div>
71
+
72
+ We now replace the second `<div>`:
73
+
74
+ up.replace('.two', '/new');
75
+
76
+ The server renders a response for `/new`:
77
+
78
+ <div class="one">new one</div>
79
+ <div class="two">new two</div>
80
+
81
+ Unpoly looks for the selector `.two` in the response and [implants](/up.extract) it into
82
+ the current page. The current page now looks like this:
83
+
84
+ <div class="one">old one</div>
85
+ <div class="two">new two</div>
86
+
87
+ Note how only `.two` has changed. The update for `.one` was
88
+ discarded, since it didn't match the selector.
89
+
90
+ \#\#\#\# Appending or prepending instead of replacing
91
+
92
+ By default Unpoly will replace the given selector with the same
93
+ selector from a freshly fetched page. Instead of replacing you
94
+ can *append* the loaded content to the existing content by using the
95
+ `:after` pseudo selector. In the same fashion, you can use `:before`
96
+ to indicate that you would like the *prepend* the loaded content.
97
+
98
+ A practical example would be a paginated list of items:
99
+
100
+ <ul class="tasks">
101
+ <li>Wash car</li>
102
+ <li>Purchase supplies</li>
103
+ <li>Fix tent</li>
104
+ </ul>
105
+
106
+ In order to append more items from a URL, replace into
107
+ the `.tasks:after` selector:
108
+
109
+ up.replace('.tasks:after', '/page/2')
110
+
111
+ \#\#\#\# Setting the window title from the server
112
+
113
+ If the `replace` call changes history, the document title will be set
114
+ to the contents of a `<title>` tag in the response.
115
+
116
+ The server can also change the document title by setting
117
+ an `X-Up-Title` header in the response.
118
+
119
+ \#\#\#\# Optimizing response rendering
120
+
121
+ The server is free to optimize Unpoly requests by only rendering the HTML fragment
122
+ that is being updated. The request's `X-Up-Target` header will contain
123
+ the CSS selector for the updating fragment.
124
+
125
+ If you are using the `unpoly-rails` gem you can also access the selector via
126
+ `up.selector` in all controllers, views and helpers.
127
+
128
+ \#\#\#\# Events
129
+
130
+ Unpoly will emit [`up:fragment:destroyed`](/up:fragment:destroyed) on the element
131
+ that was replaced and [`up:fragment:inserted`](/up:fragment:inserted) on the new
132
+ element that replaces it.
133
+
134
+ @function up.replace
135
+ @param {String|Element|jQuery} selectorOrElement
136
+ The CSS selector to update. You can also pass a DOM element or jQuery element
137
+ here, in which case a selector will be inferred from the element's class and ID.
138
+ @param {String} url
139
+ The URL to fetch from the server.
140
+ @param {String} [options.failTarget='body']
141
+ The CSS selector to update if the server sends a non-200 status code.
142
+ @param {String} [options.title]
143
+ @param {String} [options.method='get']
144
+ @param {Object|Array} [options.data]
145
+ Parameters that should be sent as the request's payload.
146
+
147
+ Parameters can either be passed as an object (where the property names become
148
+ the param names and the property values become the param values) or as
149
+ an array of `{ name: 'param-name', value: 'param-value' }` objects
150
+ (compare to jQuery's [`serializeArray`](https://api.jquery.com/serializeArray/)).
151
+ @param {String} [options.transition='none']
152
+ @param {String|Boolean} [options.history=true]
153
+ If a `String` is given, it is used as the URL the browser's location bar and history.
154
+ If omitted or true, the `url` argument will be used.
155
+ If set to `false`, the history will remain unchanged.
156
+ @param {String|Boolean} [options.source=true]
157
+ @param {String} [options.reveal=false]
158
+ Whether to [reveal](/up.reveal) the element being updated, by
159
+ scrolling its containing viewport.
160
+ @param {Boolean} [options.restoreScroll=false]
161
+ If set to true, Unpoly will try to restore the scroll position
162
+ of all the viewports around or below the updated element. The position
163
+ will be reset to the last known top position before a previous
164
+ history change for the current URL.
165
+ @param {Boolean} [options.cache]
166
+ Whether to use a [cached response](/up.proxy) if available.
167
+ @param {Element|jQuery} [options.origin]
168
+ The element that triggered the replacement. The element's selector will
169
+ be substituted for the `&` shorthand in the target selector.
170
+ @param {String} [options.historyMethod='push']
171
+ @param {Object} [options.headers={}]
172
+ An object of additional header key/value pairs to send along
173
+ with the request.
174
+ @param {Boolean} [options.requireMatch=true]
175
+ Whether to raise an error if the given selector is missing in
176
+ either the current page or in the response.
177
+ @return {Promise}
178
+ A promise that will be resolved when the page has been updated.
179
+ @stable
180
+ ###
181
+ replace = (selectorOrElement, url, options) ->
182
+ up.puts "Replacing %s from %s (%o)", selectorOrElement, url, options
183
+ options = u.options(options)
184
+ target = resolveSelector(selectorOrElement, options.origin)
185
+ failTarget = u.option(options.failTarget, 'body')
186
+ failTarget = resolveSelector(failTarget, options.origin)
187
+
188
+ if !up.browser.canPushState() && options.history != false
189
+ unless options.preload
190
+ up.browser.loadPage(url, u.only(options, 'method', 'data'))
191
+ return u.unresolvablePromise()
192
+
193
+ request =
194
+ url: url
195
+ method: options.method
196
+ data: options.data
197
+ target: target
198
+ failTarget: failTarget
199
+ cache: options.cache
200
+ preload: options.preload
201
+ headers: options.headers
202
+
203
+ promise = up.ajax(request)
204
+
205
+ promise.done (html, textStatus, xhr) ->
206
+ processResponse(true, target, url, request, xhr, options)
207
+
208
+ promise.fail (xhr, textStatus, errorThrown) ->
209
+ processResponse(false, failTarget, url, request, xhr, options)
210
+ promise
211
+
212
+ ###*
213
+ @internal
214
+ ###
215
+ processResponse = (isSuccess, selector, url, request, xhr, options) ->
216
+ options.method = u.normalizeMethod(u.option(u.methodFromXhr(xhr), options.method))
217
+ options.title = u.option(u.titleFromXhr(xhr), options.title)
218
+ isReloadable = (options.method == 'GET')
219
+
220
+ # The server can send us the current path using a header value.
221
+ # This way we know the actual URL if the server has redirected.
222
+ if urlFromServer = u.locationFromXhr(xhr)
223
+ url = urlFromServer
224
+ if isSuccess
225
+ newRequest =
226
+ url: url
227
+ method: u.methodFromXhr(xhr)
228
+ target: selector
229
+ up.proxy.alias(request, newRequest)
230
+ else if isReloadable
231
+ if query = u.requestDataAsQuery(options.data)
232
+ url = "#{url}?#{query}"
233
+
234
+ if isSuccess
235
+ if isReloadable # e.g. GET returns 200 OK
236
+ options.history = url unless options.history is false || u.isString(options.history)
237
+ options.source = url unless options.source is false || u.isString(options.source)
238
+ else # e.g. POST returns 200 OK
239
+ options.history = false unless u.isString(options.history)
240
+ options.source = 'keep' unless u.isString(options.source)
241
+ else
242
+ options.transition = options.failTransition
243
+ options.failTransition = undefined
244
+ if isReloadable # e.g. GET returns 500 Internal Server Error
245
+ options.history = url unless options.history is false
246
+ options.source = url unless options.source is false
247
+ else # e.g. POST returns 500 Internal Server Error
248
+ options.source = 'keep'
249
+ options.history = false
250
+
251
+ if options.preload
252
+ u.resolvedPromise()
253
+ else
254
+ extract(selector, xhr.responseText, options)
255
+
256
+ ###*
257
+ Updates a selector on the current page with the
258
+ same selector from the given HTML string.
259
+
260
+ \#\#\#\# Example
261
+
262
+ Let's say your curent HTML looks like this:
263
+
264
+ <div class="one">old one</div>
265
+ <div class="two">old two</div>
266
+
267
+ We now replace the second `<div>`, using an HTML string
268
+ as the source:
269
+
270
+ html = '<div class="one">new one</div>' +
271
+ '<div class="two">new two</div>';
272
+
273
+ up.extract('.two', html);
274
+
275
+ Unpoly looks for the selector `.two` in the strings and updates its
276
+ contents in the current page. The current page now looks like this:
277
+
278
+ <div class="one">old one</div>
279
+ <div class="two">new two</div>
280
+
281
+ Note how only `.two` has changed. The update for `.one` was
282
+ discarded, since it didn't match the selector.
283
+
284
+ @function up.extract
285
+ @param {String|Element|jQuery} selectorOrElement
286
+ @param {String} html
287
+ @param {Object} [options]
288
+ See options for [`up.replace`](/up.replace).
289
+ @return {Promise}
290
+ A promise that will be resolved then the selector was updated
291
+ and all animation has finished.
292
+ @experimental
293
+ ###
294
+ extract = (selectorOrElement, html, options) ->
295
+ up.log.group 'Extracting %s from %d bytes of HTML', selectorOrElement, html?.length, ->
296
+ options = u.options(options,
297
+ historyMethod: 'push',
298
+ requireMatch: true,
299
+ keep: true
300
+ )
301
+ selector = resolveSelector(selectorOrElement, options.origin)
302
+ response = parseResponse(html, options)
303
+ options.title ||= response.title()
304
+
305
+ up.layout.saveScroll() unless options.saveScroll == false
306
+
307
+ options.beforeSwap?()
308
+ deferreds = []
309
+
310
+ updateHistory(options)
311
+
312
+ for step in parseImplantSteps(selector, options)
313
+ up.log.group 'Updating %s', step.selector, ->
314
+ $old = findOldFragment(step.selector, options)
315
+ $new = response.find(step.selector)?.first()
316
+ if $old && $new
317
+ deferred = swapElements($old, $new, step.pseudoClass, step.transition, options)
318
+ deferreds.push(deferred)
319
+
320
+ options.afterSwap?()
321
+ up.motion.when(deferreds...)
322
+
323
+ findOldFragment = (selector, options) ->
324
+ # Prefer to replace fragments in an open popup or modal
325
+ first(".up-popup #{selector}") ||
326
+ first(".up-modal #{selector}") ||
327
+ first(selector) ||
328
+ oldFragmentNotFound(selector, options)
329
+
330
+ oldFragmentNotFound = (selector, options) ->
331
+ if options.requireMatch
332
+ message = 'Could not find selector %s in current body HTML'
333
+ if message[0] == '#'
334
+ message += ' (avoid using IDs)'
335
+ u.error(message, selector)
336
+
337
+ parseResponse = (html, options) ->
338
+ # jQuery cannot construct transient elements that contain <html> or <body> tags
339
+ htmlElement = u.createElementFromHtml(html)
340
+ title: -> htmlElement.querySelector("title")?.textContent
341
+ find: (selector) ->
342
+ # Although we cannot have a jQuery collection from an entire HTML document,
343
+ # we can use jQuery's Sizzle engine to grep through a DOM tree.
344
+ # jQuery.find is the Sizzle function (https://github.com/jquery/sizzle/wiki#public-api)
345
+ # which gives us non-standard CSS selectors such as `:has`.
346
+ # It returns an array of DOM elements, NOT a jQuery collection.
347
+ if child = $.find(selector, htmlElement)[0]
348
+ $(child)
349
+ else if options.requireMatch
350
+ u.error("Could not find selector %s in response %o", selector, html)
351
+
352
+ updateHistory = (options) ->
353
+ if options.history
354
+ document.title = options.title if options.title
355
+ up.history[options.historyMethod](options.history)
356
+
357
+ swapElements = ($old, $new, pseudoClass, transition, options) ->
358
+ transition ||= 'none'
359
+
360
+ if options.source == 'keep'
361
+ options = u.merge(options, source: source($old))
362
+
363
+ # Ensure that all transitions and animations have completed.
364
+ up.motion.finish($old)
365
+
366
+ if pseudoClass
367
+ # Text nodes are wrapped in a .up-insertion container so we can
368
+ # animate them and measure their position/size for scrolling.
369
+ # This is not possible for container-less text nodes.
370
+ $wrapper = $new.contents().wrap('<span class="up-insertion"></span>').parent()
371
+
372
+ # Note that since we're prepending/appending instead of replacing,
373
+ # `$new` will not actually be inserted into the DOM, only its children.
374
+ if pseudoClass == 'before'
375
+ $old.prepend($wrapper)
376
+ else
377
+ $old.append($wrapper)
378
+
379
+ hello($wrapper.children(), options)
380
+
381
+ # Reveal element that was being prepended/appended.
382
+ promise = up.layout.revealOrRestoreScroll($wrapper, options)
383
+
384
+ # Since we're adding content instead of replacing, we'll only
385
+ # animate $new instead of morphing between $old and $new
386
+ promise = promise.then -> up.animate($wrapper, transition, options)
387
+
388
+ # Remove the wrapper now that is has served it purpose
389
+ promise = promise.then -> u.unwrapElement($wrapper)
390
+
391
+ else if keepPlan = findKeepPlan($old, $new, options)
392
+ emitFragmentKept(keepPlan)
393
+ promise = u.resolvedPromise()
394
+
395
+ else
396
+ replacement = ->
397
+
398
+ options.keepPlans = transferKeepableElements($old, $new, options)
399
+
400
+ # Don't insert the new element after the old element. For some reason
401
+ # this will make the browser scroll to the bottom of the new element.
402
+ $new.insertBefore($old)
403
+
404
+ # Remember where the element came from so we can
405
+ # offer reload functionality.
406
+ setSource($new, options.source) unless options.source is false
407
+
408
+ autofocus($new)
409
+
410
+ # The fragment should be compiled before animating,
411
+ # so transitions see .up-current classes
412
+ hello($new, options)
413
+
414
+ # Morphing will also process options.reveal
415
+ up.morph($old, $new, transition, options)
416
+
417
+ # Wrap the replacement as a destroy animation, so $old will
418
+ # get marked as .up-destroying right away.
419
+ promise = destroy($old, animation: replacement)
420
+
421
+ promise
422
+
423
+ transferKeepableElements = ($old, $new, options) ->
424
+ keepPlans = []
425
+ if options.keep
426
+ for keepable in $old.find('[up-keep]')
427
+ $keepable = $(keepable)
428
+ if plan = findKeepPlan($keepable, $new, u.merge(options, descendantsOnly: true))
429
+ # Replace $keepable with its clone so it looks good in a transition between
430
+ # $old and $new. Note that $keepable will still point to the same element
431
+ # after the replacement, which is now detached.
432
+ $keepableClone = $keepable.clone()
433
+ $keepable.replaceWith($keepableClone)
434
+ # Since we're going to swap the entire $old and $new containers afterwards,
435
+ # replace the matching element with $keepable so it will eventually return to the DOM.
436
+ plan.$newElement.replaceWith($keepable)
437
+ keepPlans.push(plan)
438
+ keepPlans
439
+
440
+ findKeepPlan = ($element, $new, options) ->
441
+ if options.keep
442
+ $keepable = $element
443
+ if partnerSelector = u.castedAttr($keepable, 'up-keep')
444
+ u.isString(partnerSelector) or partnerSelector = '&'
445
+ partnerSelector = resolveSelector(partnerSelector, $keepable)
446
+ if options.descendantsOnly
447
+ $partner = $new.find(partnerSelector)
448
+ else
449
+ $partner = u.findWithSelf($new, partnerSelector)
450
+ $partner = $partner.first()
451
+ if $partner.length && $partner.is('[up-keep]')
452
+ description =
453
+ $element: $keepable # the element that should be kept
454
+ $newElement: $partner # the element that would have replaced it but now does not
455
+ newData: up.syntax.data($partner) # the parsed up-data attribute of the element we will discard
456
+ keepEventArgs = u.merge(description, message: ['Keeping element %o', $keepable.get(0)])
457
+ if up.bus.nobodyPrevents('up:fragment:keep', keepEventArgs)
458
+ description
459
+
460
+ ###*
461
+ Elements with an `up-keep` attribute will be persisted during
462
+ [fragment updates](/a-up-target).
463
+
464
+ For example:
465
+
466
+ <audio up-keep src="song.mp3"></audio>
467
+
468
+ The element you're keeping should have an umambiguous class name, ID or `up-id`
469
+ attribute so Unpoly can find its new position within the page update.
470
+
471
+ Emits events [`up:fragment:keep`](/up:fragment:keep) and [`up:fragment:kept`](/up:fragment:kept).
472
+
473
+ \#\#\#\# Controlling if an element will be kept
474
+
475
+ Unpoly will **only** keep an existing element if:
476
+
477
+ - The existing element has an `up-keep` attribute
478
+ - The response contains an element matching the CSS selector of the existing element
479
+ - The matching element *also* has an `up-keep` attribute
480
+ - The [`up:fragment:keep`](/up:fragment:keep) event that is [emitted](/up.emit) on the existing element
481
+ is not prevented by a event listener.
482
+
483
+ Let's say we want only keep an `<audio>` element as long as it plays
484
+ the same song (as identified by the tag's `src` attribute).
485
+
486
+ On the client we can achieve this by listening to an `up:keep:fragment` event
487
+ and preventing it if the `src` attribute of the old and new element differ:
488
+
489
+ up.compiler('audio', function($element) {
490
+ $element.on('up:fragment:keep', function(event) {
491
+ if $element.attr('src') !== event.$newElement.attr('src') {
492
+ event.preventDefault();
493
+ }
494
+ });
495
+ });
496
+
497
+ If we don't want to solve this on the client, we can achieve the same effect
498
+ on the server. By setting the value of the `up-keep` attribute we can
499
+ define the CSS selector used for matching elements.
500
+
501
+ <audio up-keep="audio[src='song.mp3']" src="song.mp3"></audio>
502
+
503
+ Now, if a response no longer contains an `<audio src="song.mp3">` tag, the existing
504
+ element will be destroyed and replaced by a fragment from the response.
505
+
506
+ @selector [up-keep]
507
+ @stable
508
+ ###
509
+
510
+ ###*
511
+ This event is [emitted](/up.emit) before an existing element is [kept](/up-keep) during
512
+ a page update.
513
+
514
+ Event listeners can call `event.preventDefault()` on an `up:fragment:keep` event
515
+ to prevent the element from being persisted. If the event is prevented, the element
516
+ will be replaced by a fragment from the response.
517
+
518
+ @event up:fragment:keep
519
+ @param event.preventDefault()
520
+ Event listeners may call this method to prevent the element from being preserved.
521
+ @param {jQuery} event.$element
522
+ The fragment that will be kept.
523
+ @param {jqQuery} event.$newElement
524
+ The discarded element.
525
+ @param {jQuery} event.newData
526
+ The value of the [`up-data`](/up-data) attribute of the discarded element,
527
+ parsed as a JSON object.
528
+ @stable
529
+ ###
530
+
531
+ ###*
532
+ This event is [emitted](/up.emit) when an existing element has been [kept](/up-keep)
533
+ during a page update.
534
+
535
+ Event listeners can inspect the discarded update through `event.$newElement`
536
+ and `event.newData` and then modify the preserved element when necessary.
537
+
538
+ @event up:fragment:kept
539
+ @param {jQuery} event.$element
540
+ The fragment that has been kept.
541
+ @param {jqQuery} event.$newElement
542
+ The discarded element.
543
+ @param {jQuery} event.newData
544
+ The value of the [`up-data`](/up-data) attribute of the discarded element,
545
+ parsed as a JSON object.
546
+ @stable
547
+ ###
548
+
549
+ parseImplantSteps = (selector, options) ->
550
+ transitionArg = options.transition || options.animation || 'none'
551
+ comma = /\ *,\ */
552
+ disjunction = selector.split(comma)
553
+ if u.isString(transitions)
554
+ transitions = transitionArg.split(comma)
555
+ else
556
+ transitions = [transitionArg]
557
+ for selectorAtom, i in disjunction
558
+ # Splitting the atom
559
+ selectorParts = selectorAtom.match(/^(.+?)(?:\:(before|after))?$/)
560
+ selectorParts or u.error('Could not parse selector atom "%s"', selectorAtom)
561
+ selector = selectorParts[1]
562
+ if selector == 'html'
563
+ # If someone really asked us to replace the <html> root, the best
564
+ # we can do is replace the <body>.
565
+ selector = 'body'
566
+ pseudoClass = selectorParts[2]
567
+ transition = transitions[i] || u.last(transitions)
568
+ selector: selector
569
+ pseudoClass: pseudoClass
570
+ transition: transition
571
+
572
+ ###*
573
+ Compiles a page fragment that has been inserted into the DOM
574
+ without Unpoly.
575
+
576
+ **As long as you manipulate the DOM using Unpoly, you will never
577
+ need to call this method.** You only need to use `up.hello` if the
578
+ DOM is manipulated without Unpoly' involvement, e.g. by setting
579
+ the `innerHTML` property or calling jQuery methods like
580
+ `html`, `insertAfter` or `appendTo`:
581
+
582
+ $element = $('.element');
583
+ $element.html('<div>...</div>');
584
+ up.hello($element);
585
+
586
+ This function emits the [`up:fragment:inserted`](/up:fragment:inserted)
587
+ event.
588
+
589
+ @function up.hello
590
+ @param {String|Element|jQuery} selectorOrElement
591
+ @param {String|Element|jQuery} [options.origin]
592
+ @param {String|Element|jQuery} [options.kept]
593
+ @return {jQuery}
594
+ The compiled element
595
+ @stable
596
+ ###
597
+ hello = (selectorOrElement, options) ->
598
+ $element = $(selectorOrElement)
599
+ options = u.options(options, keepPlans: [])
600
+ keptElements = []
601
+ for plan in options.keepPlans
602
+ emitFragmentKept(plan)
603
+ keptElements.push(plan.$element)
604
+ up.syntax.compile($element, skip: keptElements)
605
+ emitFragmentInserted($element, options)
606
+ $element
607
+
608
+ ###*
609
+ When a page fragment has been [inserted or updated](/up.replace),
610
+ this event is [emitted](/up.emit) on the fragment.
611
+
612
+ \#\#\#\# Example
613
+
614
+ up.on('up:fragment:inserted', function(event, $fragment) {
615
+ console.log("Looks like we have a new %o!", $fragment);
616
+ });
617
+
618
+ @event up:fragment:inserted
619
+ @param {jQuery} event.$element
620
+ The fragment that has been inserted or updated.
621
+ @stable
622
+ ###
623
+ emitFragmentInserted = (fragment, options) ->
624
+ $fragment = $(fragment)
625
+ up.emit 'up:fragment:inserted',
626
+ $element: $fragment
627
+ message: ['Inserted fragment %o', $fragment.get(0)]
628
+ origin: options.origin
629
+
630
+ emitFragmentKept = (keepPlan) ->
631
+ eventAttrs = u.merge(keepPlan, message: ['Kept fragment %o', keepPlan.$element.get(0)])
632
+ up.emit('up:fragment:kept', eventAttrs)
633
+
634
+ autofocus = ($element) ->
635
+ selector = '[autofocus]:last'
636
+ $control = u.findWithSelf($element, selector)
637
+ if $control.length && $control.get(0) != document.activeElement
638
+ $control.focus()
639
+
640
+ isRealElement = ($element) ->
641
+ unreal = '.up-ghost, .up-destroying'
642
+ # Closest matches both the element itself
643
+ # as well as its ancestors
644
+ $element.closest(unreal).length == 0
645
+
646
+ ###*
647
+ Returns the first element matching the given selector, but
648
+ ignores elements that are being [destroyed](/up.destroy) or [transitioned](/up.morph).
649
+
650
+ If the given argument is already a jQuery collection (or an array
651
+ of DOM elements), the first element matching these conditions
652
+ is returned.
653
+
654
+ Returns `undefined` if no element matches these conditions.
655
+
656
+ @function up.first
657
+ @param {String|Element|jQuery|Array<Element>} selectorOrElement
658
+ @return {jQuery}
659
+ The first element that is neither a ghost or being destroyed,
660
+ or `undefined` if no such element was given.
661
+ @experimental
662
+ ###
663
+ first = (selectorOrElement) ->
664
+ elements = undefined
665
+ if u.isString(selectorOrElement)
666
+ elements = $(selectorOrElement).get()
667
+ else
668
+ elements = selectorOrElement
669
+ $match = undefined
670
+ for element in elements
671
+ $element = $(element)
672
+ if isRealElement($element)
673
+ $match = $element
674
+ break
675
+ $match
676
+
677
+ ###*
678
+ Destroys the given element or selector.
679
+
680
+ Takes care that all [`up.compiler`](/up.compiler) destructors, if any, are called.
681
+
682
+ The element is removed from the DOM.
683
+ Note that if you choose to animate the element removal using `options.animate`,
684
+ the element won't be removed until after the animation has completed.
685
+
686
+ Emits events [`up:fragment:destroy`](/up:fragment:destroy) and [`up:fragment:destroyed`](/up:fragment:destroyed).
687
+
688
+ @function up.destroy
689
+ @param {String|Element|jQuery} selectorOrElement
690
+ @param {String} [options.url]
691
+ @param {String} [options.title]
692
+ @param {String|Function} [options.animation='none']
693
+ The animation to use before the element is removed from the DOM.
694
+ @param {Number} [options.duration]
695
+ The duration of the animation. See [`up.animate`](/up.animate).
696
+ @param {Number} [options.delay]
697
+ The delay before the animation starts. See [`up.animate`](/up.animate).
698
+ @param {String} [options.easing]
699
+ The timing function that controls the animation's acceleration. [`up.animate`](/up.animate).
700
+ @return {Deferred}
701
+ A promise that will be resolved once the element has been removed from the DOM.
702
+ @stable
703
+ ###
704
+ destroy = (selectorOrElement, options) ->
705
+ $element = $(selectorOrElement)
706
+ unless $element.is('.up-placeholder, .up-tooltip, .up-modal, .up-popup')
707
+ destroyMessage = ['Destroying fragment %o', $element.get(0)]
708
+ destroyedMessage = ['Destroyed fragment %o', $element.get(0)]
709
+ if up.bus.nobodyPrevents('up:fragment:destroy', $element: $element, message: destroyMessage)
710
+ options = u.options(options, animation: false)
711
+ animateOptions = up.motion.animateOptions(options)
712
+ $element.addClass('up-destroying')
713
+ # If e.g. a modal or popup asks us to restore a URL, do this
714
+ # before emitting `fragment:destroy`. This way up.navigate sees the
715
+ # new URL and can assign/remove .up-current classes accordingly.
716
+ up.history.push(options.url) if u.isPresent(options.url)
717
+ document.title = options.title if u.isPresent(options.title)
718
+ animationDeferred = u.presence(options.animation, u.isDeferred) ||
719
+ up.motion.animate($element, options.animation, animateOptions)
720
+ animationDeferred.then ->
721
+ up.syntax.clean($element)
722
+ # Emit this while $element is still part of the DOM, so event
723
+ # listeners bound to the document will receive the event.
724
+ up.emit 'up:fragment:destroyed', $element: $element, message: destroyedMessage
725
+ $element.remove()
726
+ animationDeferred
727
+ else
728
+ # Although someone prevented the destruction, keep a uniform API for
729
+ # callers by returning a Deferred that will never be resolved.
730
+ $.Deferred()
731
+
732
+ ###*
733
+ Before a page fragment is being [destroyed](/up.destroy), this
734
+ event is [emitted](/up.emit) on the fragment.
735
+
736
+ If the destruction is animated, this event is emitted before the
737
+ animation begins.
738
+
739
+ @event up:fragment:destroy
740
+ @param {jQuery} event.$element
741
+ The page fragment that is about to be destroyed.
742
+ @param event.preventDefault()
743
+ Event listeners may call this method to prevent the fragment from being destroyed.
744
+ @stable
745
+ ###
746
+
747
+ ###*
748
+ This event is [emitted](/up.emit) right before a [destroyed](/up.destroy)
749
+ page fragment is removed from the DOM.
750
+
751
+ If the destruction is animated, this event is emitted after
752
+ the animation has ended.
753
+
754
+ @event up:fragment:destroyed
755
+ @param {jQuery} event.$element
756
+ The page fragment that is about to be removed from the DOM.
757
+ @stable
758
+ ###
759
+
760
+ ###*
761
+ Replaces the given element with a fresh copy fetched from the server.
762
+
763
+ \#\#\#\# Example
764
+
765
+ up.on('new-mail', function() {
766
+ up.reload('.inbox');
767
+ });
768
+
769
+ Unpoly remembers the URL from which a fragment was loaded, so you
770
+ don't usually need to give an URL when reloading.
771
+
772
+ @function up.reload
773
+ @param {String|Element|jQuery} selectorOrElement
774
+ @param {Object} [options]
775
+ See options for [`up.replace`](/up.replace)
776
+ @param {String} [options.url]
777
+ The URL from which to reload the fragment.
778
+ This defaults to the URL from which the fragment was originally loaded.
779
+ @stable
780
+ ###
781
+ reload = (selectorOrElement, options) ->
782
+ options = u.options(options, cache: false)
783
+ sourceUrl = options.url || source(selectorOrElement)
784
+ replace(selectorOrElement, sourceUrl, options)
785
+
786
+ up.on 'ready', ->
787
+ $body = $(document.body)
788
+ setSource($body, up.browser.url())
789
+ hello($body)
790
+
791
+ knife: eval(Knife?.point)
792
+ replace: replace
793
+ reload: reload
794
+ destroy: destroy
795
+ extract: extract
796
+ first: first
797
+ source: source
798
+ resolveSelector: resolveSelector
799
+ hello: hello
800
+
801
+ )(jQuery)
802
+
803
+ up.replace = up.flow.replace
804
+ up.extract = up.flow.extract
805
+ up.reload = up.flow.reload
806
+ up.destroy = up.flow.destroy
807
+ up.first = up.flow.first
808
+ up.hello = up.flow.hello
809
+