virgo 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +37 -0
  4. data/app/assets/javascripts/tinymce/manifest.coffee +1 -0
  5. data/app/assets/javascripts/tinymce/plugins/image_manager/plugin.coffee +234 -0
  6. data/app/assets/javascripts/virgo/admin/page_modules/edit_form.coffee +4 -0
  7. data/app/assets/javascripts/virgo/admin/page_modules/edit_subject_form.coffee +29 -0
  8. data/app/assets/javascripts/virgo/admin/posts/edit_locking.coffee +35 -0
  9. data/app/assets/javascripts/virgo/admin/posts/form.coffee +183 -0
  10. data/app/assets/javascripts/virgo/admin/slides/form.coffee +27 -0
  11. data/app/assets/javascripts/virgo/admin/slideshows/form.coffee +26 -0
  12. data/app/assets/javascripts/virgo/admin_only.coffee +1 -0
  13. data/app/assets/javascripts/virgo/application.coffee +51 -0
  14. data/app/assets/javascripts/virgo/common.coffee +83 -0
  15. data/app/assets/javascripts/virgo/common/character_counter.coffee +27 -0
  16. data/app/assets/javascripts/virgo/components/header.coffee +47 -0
  17. data/app/assets/javascripts/virgo/components/media_modal.coffee +151 -0
  18. data/app/assets/javascripts/virgo/components/share_bar.coffee +53 -0
  19. data/app/assets/javascripts/virgo/components/slideshow.coffee +35 -0
  20. data/app/assets/javascripts/virgo/lib/jquery-ui-multiselect.js +342 -0
  21. data/app/assets/javascripts/virgo/lib/jquery.browser.js +184 -0
  22. data/app/assets/javascripts/virgo/lib/jquery.cookie.js +114 -0
  23. data/app/assets/javascripts/virgo/lib/jquery.pjax.js +932 -0
  24. data/app/assets/javascripts/virgo/lib/jquery_ext.coffee +0 -0
  25. data/app/assets/javascripts/virgo/lib/manifest.coffee +5 -0
  26. data/app/assets/javascripts/virgo/lib/ouibounce.js +194 -0
  27. data/app/assets/javascripts/virgo/page_modules.coffee +175 -0
  28. data/app/assets/javascripts/virgo/posts/show.coffee +32 -0
  29. data/app/assets/stylesheets/tinymce/skin.sass +2 -0
  30. data/app/assets/stylesheets/tinymce/skins/railspress/content.min.sass +82 -0
  31. data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce-small.eot +0 -0
  32. data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce-small.svg +62 -0
  33. data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce-small.ttf +0 -0
  34. data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce-small.woff +0 -0
  35. data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce.eot +0 -0
  36. data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce.svg +63 -0
  37. data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce.ttf +0 -0
  38. data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce.woff +0 -0
  39. data/app/assets/stylesheets/tinymce/skins/railspress/img/anchor.gif +0 -0
  40. data/app/assets/stylesheets/tinymce/skins/railspress/img/loader.gif +0 -0
  41. data/app/assets/stylesheets/tinymce/skins/railspress/img/object.gif +0 -0
  42. data/app/assets/stylesheets/tinymce/skins/railspress/img/trans.gif +0 -0
  43. data/app/assets/stylesheets/tinymce/skins/railspress/skin.min.sass +1703 -0
  44. data/app/assets/stylesheets/virgo/_common.sass +65 -0
  45. data/app/assets/stylesheets/virgo/admin.sass +301 -0
  46. data/app/assets/stylesheets/virgo/admin/columns.sass +3 -0
  47. data/app/assets/stylesheets/virgo/admin/images.sass +14 -0
  48. data/app/assets/stylesheets/virgo/admin/media_modal.sass +72 -0
  49. data/app/assets/stylesheets/virgo/admin/post.sass +102 -0
  50. data/app/assets/stylesheets/virgo/admin/slideshow.sass +35 -0
  51. data/app/assets/stylesheets/virgo/admin/users.sass +2 -0
  52. data/app/assets/stylesheets/virgo/admin_only.sass +1 -0
  53. data/app/assets/stylesheets/virgo/application.sass +605 -0
  54. data/app/assets/stylesheets/virgo/columns.sass +51 -0
  55. data/app/assets/stylesheets/virgo/install.sass +15 -0
  56. data/app/assets/stylesheets/virgo/lib/bootstrap/bootstrap-imports.sass +9 -0
  57. data/app/assets/stylesheets/virgo/lib/bootstrap/bootstrap-overrides.sass +51 -0
  58. data/app/assets/stylesheets/virgo/lib/bootstrap/bootstrap-variables.sass +27 -0
  59. data/app/assets/stylesheets/virgo/lib/jquery-ui-multiselect.css +30 -0
  60. data/app/assets/stylesheets/virgo/lib/manifest.sass +3 -0
  61. data/app/assets/stylesheets/virgo/lib/ouibounce.css +222 -0
  62. data/app/assets/stylesheets/virgo/lib/select2-imports.sass +8 -0
  63. data/app/assets/stylesheets/virgo/page_modules.sass +54 -0
  64. data/app/assets/stylesheets/virgo/pages/authors.sass +30 -0
  65. data/app/assets/stylesheets/virgo/posts.sass +782 -0
  66. data/app/assets/stylesheets/virgo/search.sass +36 -0
  67. data/app/assets/stylesheets/virgo/ui.sass +434 -0
  68. data/app/assets/stylesheets/virgo/users.sass +9 -0
  69. data/app/controllers/virgo/admin/base_controller.rb +22 -0
  70. data/app/controllers/virgo/admin/categories_controller.rb +90 -0
  71. data/app/controllers/virgo/admin/columns_controller.rb +65 -0
  72. data/app/controllers/virgo/admin/images_controller.rb +89 -0
  73. data/app/controllers/virgo/admin/media_modal_controller.rb +101 -0
  74. data/app/controllers/virgo/admin/newsletters_controller.rb +37 -0
  75. data/app/controllers/virgo/admin/page_modules_controller.rb +62 -0
  76. data/app/controllers/virgo/admin/pages_controller.rb +6 -0
  77. data/app/controllers/virgo/admin/posts_controller.rb +173 -0
  78. data/app/controllers/virgo/admin/sites_controller.rb +31 -0
  79. data/app/controllers/virgo/admin/slides_controller.rb +71 -0
  80. data/app/controllers/virgo/admin/slideshows_controller.rb +66 -0
  81. data/app/controllers/virgo/admin/tags_controller.rb +98 -0
  82. data/app/controllers/virgo/admin/users/sessions_controller.rb +21 -0
  83. data/app/controllers/virgo/admin/users_controller.rb +84 -0
  84. data/app/controllers/virgo/application_controller.rb +151 -0
  85. data/app/controllers/virgo/categories_controller.rb +20 -0
  86. data/app/controllers/virgo/columns_controller.rb +24 -0
  87. data/app/controllers/virgo/images_controller.rb +9 -0
  88. data/app/controllers/virgo/install_controller.rb +46 -0
  89. data/app/controllers/virgo/page_modules_controller.rb +23 -0
  90. data/app/controllers/virgo/pages_controller.rb +29 -0
  91. data/app/controllers/virgo/posts_controller.rb +101 -0
  92. data/app/controllers/virgo/search_controller.rb +35 -0
  93. data/app/controllers/virgo/subscribers_controller.rb +54 -0
  94. data/app/controllers/virgo/tags_controller.rb +15 -0
  95. data/app/controllers/virgo/users/confirmations_controller.rb +18 -0
  96. data/app/controllers/virgo/users/passwords_controller.rb +31 -0
  97. data/app/controllers/virgo/users/registrations_controller.rb +41 -0
  98. data/app/controllers/virgo/users/sessions_controller.rb +32 -0
  99. data/app/controllers/virgo/users_controller.rb +15 -0
  100. data/app/helpers/virgo/admin/post_helper.rb +54 -0
  101. data/app/helpers/virgo/admin_helper.rb +15 -0
  102. data/app/helpers/virgo/application_helper.rb +124 -0
  103. data/app/helpers/virgo/hooks_helper.rb +30 -0
  104. data/app/helpers/virgo/page_modules_helper.rb +4 -0
  105. data/app/helpers/virgo/post_helper.rb +159 -0
  106. data/app/helpers/virgo/render_helper.rb +33 -0
  107. data/app/helpers/virgo/search_helper.rb +49 -0
  108. data/app/helpers/virgo/shortcode_helper.rb +7 -0
  109. data/app/helpers/virgo/text_helper.rb +12 -0
  110. data/app/helpers/virgo/video_helper.rb +34 -0
  111. data/app/models/concerns/virgo/common/slug_history.rb +36 -0
  112. data/app/models/concerns/virgo/common/uuid.rb +22 -0
  113. data/app/models/concerns/virgo/post/recommendations.rb +15 -0
  114. data/app/models/concerns/virgo/post/search.rb +100 -0
  115. data/app/models/concerns/virgo/user/search.rb +39 -0
  116. data/app/models/virgo/ability.rb +59 -0
  117. data/app/models/virgo/category.rb +44 -0
  118. data/app/models/virgo/column.rb +25 -0
  119. data/app/models/virgo/form_model.rb +19 -0
  120. data/app/models/virgo/image.rb +63 -0
  121. data/app/models/virgo/page_module.rb +42 -0
  122. data/app/models/virgo/page_module_post.rb +8 -0
  123. data/app/models/virgo/post.rb +391 -0
  124. data/app/models/virgo/post_category.rb +8 -0
  125. data/app/models/virgo/post_search.rb +7 -0
  126. data/app/models/virgo/post_tag.rb +10 -0
  127. data/app/models/virgo/site.rb +36 -0
  128. data/app/models/virgo/slide.rb +23 -0
  129. data/app/models/virgo/slideshow.rb +37 -0
  130. data/app/models/virgo/slug_history.rb +5 -0
  131. data/app/models/virgo/subscriber.rb +5 -0
  132. data/app/models/virgo/tag.rb +34 -0
  133. data/app/models/virgo/user.rb +145 -0
  134. data/app/uploaders/virgo/application_uploader.rb +11 -0
  135. data/app/uploaders/virgo/avatar_uploader.rb +21 -0
  136. data/app/uploaders/virgo/featured_image_uploader.rb +28 -0
  137. data/app/uploaders/virgo/image_uploader.rb +40 -0
  138. data/app/uploaders/virgo/slide_uploader.rb +19 -0
  139. data/app/views/layouts/virgo/admin.haml +16 -0
  140. data/app/views/layouts/virgo/admin/site_settings.haml +14 -0
  141. data/app/views/layouts/virgo/application.haml +63 -0
  142. data/app/views/layouts/virgo/errors.haml +42 -0
  143. data/app/views/layouts/virgo/main.haml +5 -0
  144. data/app/views/layouts/virgo/posts.haml +10 -0
  145. data/app/views/virgo/admin/categories/_form.haml +19 -0
  146. data/app/views/virgo/admin/categories/edit.haml +7 -0
  147. data/app/views/virgo/admin/categories/index.haml +38 -0
  148. data/app/views/virgo/admin/categories/modal_form.haml +11 -0
  149. data/app/views/virgo/admin/categories/new.haml +7 -0
  150. data/app/views/virgo/admin/categories/success_modal.haml +14 -0
  151. data/app/views/virgo/admin/columns/_form.haml +30 -0
  152. data/app/views/virgo/admin/columns/edit.haml +1 -0
  153. data/app/views/virgo/admin/columns/index.haml +30 -0
  154. data/app/views/virgo/admin/columns/new.haml +1 -0
  155. data/app/views/virgo/admin/images/_form.haml +46 -0
  156. data/app/views/virgo/admin/images/edit.haml +1 -0
  157. data/app/views/virgo/admin/images/index.haml +32 -0
  158. data/app/views/virgo/admin/images/new.haml +1 -0
  159. data/app/views/virgo/admin/media_modal/_image_settings.haml +17 -0
  160. data/app/views/virgo/admin/media_modal/_library_panel.haml +26 -0
  161. data/app/views/virgo/admin/media_modal/_upload_panel.haml +21 -0
  162. data/app/views/virgo/admin/media_modal/_upload_success.haml +7 -0
  163. data/app/views/virgo/admin/media_modal/index.haml +19 -0
  164. data/app/views/virgo/admin/newsletters/changelog.haml +25 -0
  165. data/app/views/virgo/admin/newsletters/edit.haml +6 -0
  166. data/app/views/virgo/admin/page_modules/edit.haml +33 -0
  167. data/app/views/virgo/admin/page_modules/edit_subject.haml +43 -0
  168. data/app/views/virgo/admin/pages/help.haml +114 -0
  169. data/app/views/virgo/admin/posts/_author_dropdown.haml +1 -0
  170. data/app/views/virgo/admin/posts/_categories_form.haml +7 -0
  171. data/app/views/virgo/admin/posts/_category.haml +22 -0
  172. data/app/views/virgo/admin/posts/_form.haml +201 -0
  173. data/app/views/virgo/admin/posts/_revisions.haml +40 -0
  174. data/app/views/virgo/admin/posts/edit.haml +9 -0
  175. data/app/views/virgo/admin/posts/index.haml +59 -0
  176. data/app/views/virgo/admin/posts/new.haml +1 -0
  177. data/app/views/virgo/admin/posts/revision_detail.haml +28 -0
  178. data/app/views/virgo/admin/posts/revisions.haml +3 -0
  179. data/app/views/virgo/admin/sites/edit.haml +12 -0
  180. data/app/views/virgo/admin/slides/_form.haml +41 -0
  181. data/app/views/virgo/admin/slides/edit.haml +1 -0
  182. data/app/views/virgo/admin/slides/new.haml +1 -0
  183. data/app/views/virgo/admin/slideshows/_form.haml +49 -0
  184. data/app/views/virgo/admin/slideshows/edit.haml +1 -0
  185. data/app/views/virgo/admin/slideshows/index.haml +43 -0
  186. data/app/views/virgo/admin/slideshows/new.haml +1 -0
  187. data/app/views/virgo/admin/tags/_form.haml +16 -0
  188. data/app/views/virgo/admin/tags/edit.haml +7 -0
  189. data/app/views/virgo/admin/tags/index.haml +33 -0
  190. data/app/views/virgo/admin/tags/modal_form.haml +12 -0
  191. data/app/views/virgo/admin/tags/new.haml +7 -0
  192. data/app/views/virgo/admin/tags/success_modal.haml +14 -0
  193. data/app/views/virgo/admin/users/_form.haml +62 -0
  194. data/app/views/virgo/admin/users/edit.haml +1 -0
  195. data/app/views/virgo/admin/users/index.haml +34 -0
  196. data/app/views/virgo/admin/users/new.haml +1 -0
  197. data/app/views/virgo/application/sitemap.xml.builder +0 -0
  198. data/app/views/virgo/authors/_profile_long.haml +13 -0
  199. data/app/views/virgo/authors/_profile_short.haml +10 -0
  200. data/app/views/virgo/categories/show.haml +24 -0
  201. data/app/views/virgo/columns/index.haml +19 -0
  202. data/app/views/virgo/columns/show.haml +18 -0
  203. data/app/views/virgo/common/_admin_navbar.haml +100 -0
  204. data/app/views/virgo/common/_alerts.haml +40 -0
  205. data/app/views/virgo/common/_analytics.html.erb +1 -0
  206. data/app/views/virgo/common/_base_errors.haml +8 -0
  207. data/app/views/virgo/common/_category_heading.haml +26 -0
  208. data/app/views/virgo/common/_disqus.html.erb +24 -0
  209. data/app/views/virgo/common/_disqus_comment_count.html.erb +12 -0
  210. data/app/views/virgo/common/_fb_api.html.erb +30 -0
  211. data/app/views/virgo/common/_fonts.html.erb +1 -0
  212. data/app/views/virgo/common/_footer.haml +13 -0
  213. data/app/views/virgo/common/_footer_javascript.html.erb +0 -0
  214. data/app/views/virgo/common/_header_analytics.html.erb +9 -0
  215. data/app/views/virgo/common/_list_signup.haml +20 -0
  216. data/app/views/virgo/common/_ouibounce_modal.haml +26 -0
  217. data/app/views/virgo/common/_public_nav.haml +58 -0
  218. data/app/views/virgo/common/_search_form.haml +8 -0
  219. data/app/views/virgo/common/_share_bar.haml +45 -0
  220. data/app/views/virgo/common/_user_vars.haml +2 -0
  221. data/app/views/virgo/errors/404.haml +10 -0
  222. data/app/views/virgo/errors/500.haml +10 -0
  223. data/app/views/virgo/images/_shortcode.haml +4 -0
  224. data/app/views/virgo/install/index.haml +58 -0
  225. data/app/views/virgo/install/success.haml +12 -0
  226. data/app/views/virgo/page_modules/_latest_posts.haml +29 -0
  227. data/app/views/virgo/page_modules/_popular_posts.haml +51 -0
  228. data/app/views/virgo/pages/authors.haml +8 -0
  229. data/app/views/virgo/pages/home.haml +0 -0
  230. data/app/views/virgo/pages/show.haml +6 -0
  231. data/app/views/virgo/posts/_author_info.haml +16 -0
  232. data/app/views/virgo/posts/_post_full.haml +63 -0
  233. data/app/views/virgo/posts/_post_short.haml +30 -0
  234. data/app/views/virgo/posts/_post_thumb_box.haml +13 -0
  235. data/app/views/virgo/posts/_recommendations.haml +12 -0
  236. data/app/views/virgo/posts/_sidebar_thumb.haml +4 -0
  237. data/app/views/virgo/posts/index.haml +22 -0
  238. data/app/views/virgo/posts/latest.haml +16 -0
  239. data/app/views/virgo/posts/more.haml +10 -0
  240. data/app/views/virgo/posts/rss.xml.builder +17 -0
  241. data/app/views/virgo/posts/show.haml +17 -0
  242. data/app/views/virgo/search/_form.haml +5 -0
  243. data/app/views/virgo/search/_item.haml +22 -0
  244. data/app/views/virgo/search/index.haml +11 -0
  245. data/app/views/virgo/search/new.haml +4 -0
  246. data/app/views/virgo/shortcode_templates/blockquote.haml +9 -0
  247. data/app/views/virgo/shortcode_templates/h1.haml +1 -0
  248. data/app/views/virgo/shortcode_templates/h2.haml +1 -0
  249. data/app/views/virgo/shortcode_templates/h3.haml +1 -0
  250. data/app/views/virgo/shortcode_templates/h4.haml +1 -0
  251. data/app/views/virgo/shortcode_templates/image.haml +17 -0
  252. data/app/views/virgo/shortcode_templates/pullquote.haml +3 -0
  253. data/app/views/virgo/shortcode_templates/slideshow.haml +35 -0
  254. data/app/views/virgo/shortcode_templates/tweet.haml +3 -0
  255. data/app/views/virgo/shortcode_templates/video.haml +8 -0
  256. data/app/views/virgo/subscribers/_success_modal.haml +13 -0
  257. data/app/views/virgo/subscribers/modal.haml +15 -0
  258. data/app/views/virgo/tags/show.haml +16 -0
  259. data/app/views/virgo/users/confirmations/new.html.haml +10 -0
  260. data/app/views/virgo/users/devise_mailer/confirmation_instructions.html.erb +7 -0
  261. data/app/views/virgo/users/devise_mailer/reset_password_instructions.html.haml +6 -0
  262. data/app/views/virgo/users/devise_mailer/unlock_instructions.html.haml +5 -0
  263. data/app/views/virgo/users/passwords/edit.html.haml +14 -0
  264. data/app/views/virgo/users/passwords/new.html.haml +14 -0
  265. data/app/views/virgo/users/registrations/edit.html.haml +30 -0
  266. data/app/views/virgo/users/registrations/new.html.haml +13 -0
  267. data/app/views/virgo/users/sessions/new.html.haml +12 -0
  268. data/app/views/virgo/users/shared/_links.haml +26 -0
  269. data/app/views/virgo/users/show.haml +11 -0
  270. data/app/views/virgo/users/unlocks/new.html.haml +9 -0
  271. data/config/initializers/action_mailer.rb +7 -0
  272. data/config/initializers/active_record.rb +72 -0
  273. data/config/initializers/active_record_symbolize.rb +18 -0
  274. data/config/initializers/assets.rb +17 -0
  275. data/config/initializers/backtrace_silencers.rb +7 -0
  276. data/config/initializers/carrierwave.rb +36 -0
  277. data/config/initializers/cookies_serializer.rb +3 -0
  278. data/config/initializers/devise.rb +257 -0
  279. data/config/initializers/filter_parameter_logging.rb +4 -0
  280. data/config/initializers/hash.rb +5 -0
  281. data/config/initializers/inflections.rb +16 -0
  282. data/config/initializers/mime_types.rb +4 -0
  283. data/config/initializers/nil_class.rb +13 -0
  284. data/config/initializers/non_digest_assets.rb +3 -0
  285. data/config/initializers/paper_trail.rb +30 -0
  286. data/config/initializers/schema_plus.rb +5 -0
  287. data/config/initializers/session_store.rb +3 -0
  288. data/config/initializers/shortcode.rb +12 -0
  289. data/config/initializers/simple_form.rb +170 -0
  290. data/config/initializers/simple_form_bootstrap.rb +136 -0
  291. data/config/initializers/string.rb +5 -0
  292. data/config/initializers/time_formatting.rb +23 -0
  293. data/config/initializers/wrap_parameters.rb +14 -0
  294. data/config/locales/devise.en.yml +60 -0
  295. data/config/routes.rb +96 -0
  296. data/config/schedule.rb +3 -0
  297. data/db/migrate/20150729174510_create_virgo_schema.rb +225 -0
  298. data/lib/generators/virgo/install/install_generator.rb +16 -0
  299. data/lib/generators/virgo/views/views_generator.rb +22 -0
  300. data/lib/tasks/virgo_tasks.rake +7 -0
  301. data/lib/virgo.rb +4 -0
  302. data/lib/virgo/development_mail_interceptor.rb +6 -0
  303. data/lib/virgo/email_validator.rb +6 -0
  304. data/lib/virgo/engine.rb +73 -0
  305. data/lib/virgo/reduce_validator.rb +6 -0
  306. data/lib/virgo/upload_helpers.rb +89 -0
  307. data/lib/virgo/version.rb +3 -0
  308. data/lib/virgo/view_help.rb +3 -0
  309. metadata +1051 -0
@@ -0,0 +1,114 @@
1
+ /*!
2
+ * jQuery Cookie Plugin v1.4.1
3
+ * https://github.com/carhartl/jquery-cookie
4
+ *
5
+ * Copyright 2006, 2014 Klaus Hartl
6
+ * Released under the MIT license
7
+ */
8
+ (function (factory) {
9
+ if (typeof define === 'function' && define.amd) {
10
+ // AMD (Register as an anonymous module)
11
+ define(['jquery'], factory);
12
+ } else if (typeof exports === 'object') {
13
+ // Node/CommonJS
14
+ module.exports = factory(require('jquery'));
15
+ } else {
16
+ // Browser globals
17
+ factory(jQuery);
18
+ }
19
+ }(function ($) {
20
+
21
+ var pluses = /\+/g;
22
+
23
+ function encode(s) {
24
+ return config.raw ? s : encodeURIComponent(s);
25
+ }
26
+
27
+ function decode(s) {
28
+ return config.raw ? s : decodeURIComponent(s);
29
+ }
30
+
31
+ function stringifyCookieValue(value) {
32
+ return encode(config.json ? JSON.stringify(value) : String(value));
33
+ }
34
+
35
+ function parseCookieValue(s) {
36
+ if (s.indexOf('"') === 0) {
37
+ // This is a quoted cookie as according to RFC2068, unescape...
38
+ s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
39
+ }
40
+
41
+ try {
42
+ // Replace server-side written pluses with spaces.
43
+ // If we can't decode the cookie, ignore it, it's unusable.
44
+ // If we can't parse the cookie, ignore it, it's unusable.
45
+ s = decodeURIComponent(s.replace(pluses, ' '));
46
+ return config.json ? JSON.parse(s) : s;
47
+ } catch(e) {}
48
+ }
49
+
50
+ function read(s, converter) {
51
+ var value = config.raw ? s : parseCookieValue(s);
52
+ return $.isFunction(converter) ? converter(value) : value;
53
+ }
54
+
55
+ var config = $.cookie = function (key, value, options) {
56
+
57
+ // Write
58
+
59
+ if (arguments.length > 1 && !$.isFunction(value)) {
60
+ options = $.extend({}, config.defaults, options);
61
+
62
+ if (typeof options.expires === 'number') {
63
+ var days = options.expires, t = options.expires = new Date();
64
+ t.setMilliseconds(t.getMilliseconds() + days * 864e+5);
65
+ }
66
+
67
+ return (document.cookie = [
68
+ encode(key), '=', stringifyCookieValue(value),
69
+ options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
70
+ options.path ? '; path=' + options.path : '',
71
+ options.domain ? '; domain=' + options.domain : '',
72
+ options.secure ? '; secure' : ''
73
+ ].join(''));
74
+ }
75
+
76
+ // Read
77
+
78
+ var result = key ? undefined : {},
79
+ // To prevent the for loop in the first place assign an empty array
80
+ // in case there are no cookies at all. Also prevents odd result when
81
+ // calling $.cookie().
82
+ cookies = document.cookie ? document.cookie.split('; ') : [],
83
+ i = 0,
84
+ l = cookies.length;
85
+
86
+ for (; i < l; i++) {
87
+ var parts = cookies[i].split('='),
88
+ name = decode(parts.shift()),
89
+ cookie = parts.join('=');
90
+
91
+ if (key === name) {
92
+ // If second argument (value) is a function it's a converter...
93
+ result = read(cookie, value);
94
+ break;
95
+ }
96
+
97
+ // Prevent storing a cookie that we couldn't decode.
98
+ if (!key && (cookie = read(cookie)) !== undefined) {
99
+ result[name] = cookie;
100
+ }
101
+ }
102
+
103
+ return result;
104
+ };
105
+
106
+ config.defaults = {};
107
+
108
+ $.removeCookie = function (key, options) {
109
+ // Must not alter options, thus extending a fresh object...
110
+ $.cookie(key, '', $.extend({}, options, { expires: -1 }));
111
+ return !$.cookie(key);
112
+ };
113
+
114
+ }));
@@ -0,0 +1,932 @@
1
+ /*!
2
+ * Copyright 2012, Chris Wanstrath
3
+ * Released under the MIT License
4
+ * https://github.com/defunkt/jquery-pjax
5
+ */
6
+
7
+ (function($){
8
+
9
+ // When called on a container with a selector, fetches the href with
10
+ // ajax into the container or with the data-pjax attribute on the link
11
+ // itself.
12
+ //
13
+ // Tries to make sure the back button and ctrl+click work the way
14
+ // you'd expect.
15
+ //
16
+ // Exported as $.fn.pjax
17
+ //
18
+ // Accepts a jQuery ajax options object that may include these
19
+ // pjax specific options:
20
+ //
21
+ //
22
+ // container - Where to stick the response body. Usually a String selector.
23
+ // $(container).html(xhr.responseBody)
24
+ // (default: current jquery context)
25
+ // push - Whether to pushState the URL. Defaults to true (of course).
26
+ // replace - Want to use replaceState instead? That's cool.
27
+ //
28
+ // For convenience the second parameter can be either the container or
29
+ // the options object.
30
+ //
31
+ // Returns the jQuery object
32
+ function fnPjax(selector, container, options) {
33
+ var context = this
34
+ return this.on('click.pjax', selector, function(event) {
35
+ var opts = $.extend({}, optionsFor(container, options))
36
+ if (!opts.container)
37
+ opts.container = $(this).attr('data-pjax') || context
38
+ handleClick(event, opts)
39
+ })
40
+ }
41
+
42
+ // Public: pjax on click handler
43
+ //
44
+ // Exported as $.pjax.click.
45
+ //
46
+ // event - "click" jQuery.Event
47
+ // options - pjax options
48
+ //
49
+ // Examples
50
+ //
51
+ // $(document).on('click', 'a', $.pjax.click)
52
+ // // is the same as
53
+ // $(document).pjax('a')
54
+ //
55
+ // $(document).on('click', 'a', function(event) {
56
+ // var container = $(this).closest('[data-pjax-container]')
57
+ // $.pjax.click(event, container)
58
+ // })
59
+ //
60
+ // Returns nothing.
61
+ function handleClick(event, container, options) {
62
+ options = optionsFor(container, options)
63
+
64
+ var link = event.currentTarget
65
+
66
+ if (link.tagName.toUpperCase() !== 'A')
67
+ throw "$.fn.pjax or $.pjax.click requires an anchor element"
68
+
69
+ // Middle click, cmd click, and ctrl click should open
70
+ // links in a new tab as normal.
71
+ if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey )
72
+ return
73
+
74
+ // Ignore cross origin links
75
+ if ( location.protocol !== link.protocol || location.hostname !== link.hostname )
76
+ return
77
+
78
+ // Ignore case when a hash is being tacked on the current URL
79
+ if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) )
80
+ return
81
+
82
+ // Ignore event with default prevented
83
+ if (event.isDefaultPrevented())
84
+ return
85
+
86
+ var defaults = {
87
+ url: link.href,
88
+ container: $(link).attr('data-pjax'),
89
+ target: link
90
+ }
91
+
92
+ var opts = $.extend({}, defaults, options)
93
+ var clickEvent = $.Event('pjax:click')
94
+ $(link).trigger(clickEvent, [opts])
95
+
96
+ if (!clickEvent.isDefaultPrevented()) {
97
+ pjax(opts)
98
+ event.preventDefault()
99
+ $(link).trigger('pjax:clicked', [opts])
100
+ }
101
+ }
102
+
103
+ // Public: pjax on form submit handler
104
+ //
105
+ // Exported as $.pjax.submit
106
+ //
107
+ // event - "click" jQuery.Event
108
+ // options - pjax options
109
+ //
110
+ // Examples
111
+ //
112
+ // $(document).on('submit', 'form', function(event) {
113
+ // var container = $(this).closest('[data-pjax-container]')
114
+ // $.pjax.submit(event, container)
115
+ // })
116
+ //
117
+ // Returns nothing.
118
+ function handleSubmit(event, container, options) {
119
+ options = optionsFor(container, options)
120
+
121
+ var form = event.currentTarget
122
+
123
+ if (form.tagName.toUpperCase() !== 'FORM')
124
+ throw "$.pjax.submit requires a form element"
125
+
126
+ var defaults = {
127
+ type: form.method.toUpperCase(),
128
+ url: form.action,
129
+ container: $(form).attr('data-pjax'),
130
+ target: form
131
+ }
132
+
133
+ if (defaults.type !== 'GET' && window.FormData !== undefined) {
134
+ defaults.data = new FormData(form);
135
+ defaults.processData = false;
136
+ defaults.contentType = false;
137
+ } else {
138
+ // Can't handle file uploads, exit
139
+ if ($(form).find(':file').length) {
140
+ return;
141
+ }
142
+
143
+ // Fallback to manually serializing the fields
144
+ defaults.data = $(form).serializeArray();
145
+ }
146
+
147
+ pjax($.extend({}, defaults, options))
148
+
149
+ event.preventDefault()
150
+ }
151
+
152
+ // Loads a URL with ajax, puts the response body inside a container,
153
+ // then pushState()'s the loaded URL.
154
+ //
155
+ // Works just like $.ajax in that it accepts a jQuery ajax
156
+ // settings object (with keys like url, type, data, etc).
157
+ //
158
+ // Accepts these extra keys:
159
+ //
160
+ // container - Where to stick the response body.
161
+ // $(container).html(xhr.responseBody)
162
+ // push - Whether to pushState the URL. Defaults to true (of course).
163
+ // replace - Want to use replaceState instead? That's cool.
164
+ //
165
+ // Use it just like $.ajax:
166
+ //
167
+ // var xhr = $.pjax({ url: this.href, container: '#main' })
168
+ // console.log( xhr.readyState )
169
+ //
170
+ // Returns whatever $.ajax returns.
171
+ function pjax(options) {
172
+ options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
173
+
174
+ if ($.isFunction(options.url)) {
175
+ options.url = options.url()
176
+ }
177
+
178
+ var target = options.target
179
+
180
+ var hash = parseURL(options.url).hash
181
+
182
+ var context = options.context = findContainerFor(options.container)
183
+
184
+ // We want the browser to maintain two separate internal caches: one
185
+ // for pjax'd partial page loads and one for normal page loads.
186
+ // Without adding this secret parameter, some browsers will often
187
+ // confuse the two.
188
+ if (!options.data) options.data = {}
189
+ if ($.isArray(options.data)) {
190
+ options.data.push({name: '_pjax', value: context.selector})
191
+ } else {
192
+ options.data._pjax = context.selector
193
+ }
194
+
195
+ function fire(type, args, props) {
196
+ if (!props) props = {}
197
+ props.relatedTarget = target
198
+ var event = $.Event(type, props)
199
+ context.trigger(event, args)
200
+ return !event.isDefaultPrevented()
201
+ }
202
+
203
+ var timeoutTimer
204
+
205
+ options.beforeSend = function(xhr, settings) {
206
+ // No timeout for non-GET requests
207
+ // Its not safe to request the resource again with a fallback method.
208
+ if (settings.type !== 'GET') {
209
+ settings.timeout = 0
210
+ }
211
+
212
+ xhr.setRequestHeader('X-PJAX', 'true')
213
+ xhr.setRequestHeader('X-PJAX-Container', context.selector)
214
+
215
+ if (!fire('pjax:beforeSend', [xhr, settings]))
216
+ return false
217
+
218
+ if (settings.timeout > 0) {
219
+ timeoutTimer = setTimeout(function() {
220
+ if (fire('pjax:timeout', [xhr, options]))
221
+ xhr.abort('timeout')
222
+ }, settings.timeout)
223
+
224
+ // Clear timeout setting so jquerys internal timeout isn't invoked
225
+ settings.timeout = 0
226
+ }
227
+
228
+ var url = parseURL(settings.url)
229
+ url.hash = hash
230
+ options.requestUrl = stripInternalParams(url.href)
231
+ }
232
+
233
+ options.complete = function(xhr, textStatus) {
234
+ if (timeoutTimer)
235
+ clearTimeout(timeoutTimer)
236
+
237
+ fire('pjax:complete', [xhr, textStatus, options])
238
+
239
+ fire('pjax:end', [xhr, options])
240
+ }
241
+
242
+ options.error = function(xhr, textStatus, errorThrown) {
243
+ var container = extractContainer("", xhr, options)
244
+
245
+ var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])
246
+ if (options.type == 'GET' && textStatus !== 'abort' && allowed) {
247
+ locationReplace(container.url)
248
+ }
249
+ }
250
+
251
+ options.success = function(data, status, xhr) {
252
+ var previousState = pjax.state;
253
+
254
+ // If $.pjax.defaults.version is a function, invoke it first.
255
+ // Otherwise it can be a static string.
256
+ var currentVersion = (typeof $.pjax.defaults.version === 'function') ?
257
+ $.pjax.defaults.version() :
258
+ $.pjax.defaults.version
259
+
260
+ var latestVersion = xhr.getResponseHeader('X-PJAX-Version')
261
+
262
+ var container = extractContainer(data, xhr, options)
263
+
264
+ var url = parseURL(container.url)
265
+ if (hash) {
266
+ url.hash = hash
267
+ container.url = url.href
268
+ }
269
+
270
+ // If there is a layout version mismatch, hard load the new url
271
+ if (currentVersion && latestVersion && currentVersion !== latestVersion) {
272
+ locationReplace(container.url)
273
+ return
274
+ }
275
+
276
+ // If the new response is missing a body, hard load the page
277
+ if (!container.contents) {
278
+ locationReplace(container.url)
279
+ return
280
+ }
281
+
282
+ pjax.state = {
283
+ id: options.id || uniqueId(),
284
+ url: container.url,
285
+ title: container.title,
286
+ container: context.selector,
287
+ fragment: options.fragment,
288
+ timeout: options.timeout
289
+ }
290
+
291
+ if (options.push || options.replace) {
292
+ window.history.replaceState(pjax.state, container.title, container.url)
293
+ }
294
+
295
+ // Clear out any focused controls before inserting new page contents.
296
+ try {
297
+ document.activeElement.blur()
298
+ } catch (e) { }
299
+
300
+ if (container.title) document.title = container.title
301
+
302
+ fire('pjax:beforeReplace', [container.contents, options], {
303
+ state: pjax.state,
304
+ previousState: previousState
305
+ })
306
+
307
+ context.html(container.contents)
308
+
309
+ // FF bug: Won't autofocus fields that are inserted via JS.
310
+ // This behavior is incorrect. So if theres no current focus, autofocus
311
+ // the last field.
312
+ //
313
+ // http://www.w3.org/html/wg/drafts/html/master/forms.html
314
+ var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]
315
+ if (autofocusEl && document.activeElement !== autofocusEl) {
316
+ autofocusEl.focus();
317
+ }
318
+
319
+ executeScriptTags(container.scripts)
320
+
321
+ var scrollTo = options.scrollTo
322
+
323
+ // Ensure browser scrolls to the element referenced by the URL anchor
324
+ if (hash) {
325
+ var name = decodeURIComponent(hash.slice(1))
326
+ var target = document.getElementById(name) || document.getElementsByName(name)[0]
327
+ if (target) scrollTo = $(target).offset().top
328
+ }
329
+
330
+ if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo)
331
+
332
+ fire('pjax:success', [data, status, xhr, options])
333
+ }
334
+
335
+
336
+ // Initialize pjax.state for the initial page load. Assume we're
337
+ // using the container and options of the link we're loading for the
338
+ // back button to the initial page. This ensures good back button
339
+ // behavior.
340
+ if (!pjax.state) {
341
+ pjax.state = {
342
+ id: uniqueId(),
343
+ url: window.location.href,
344
+ title: document.title,
345
+ container: context.selector,
346
+ fragment: options.fragment,
347
+ timeout: options.timeout
348
+ }
349
+ window.history.replaceState(pjax.state, document.title)
350
+ }
351
+
352
+ // Cancel the current request if we're already pjaxing
353
+ abortXHR(pjax.xhr)
354
+
355
+ pjax.options = options
356
+ var xhr = pjax.xhr = $.ajax(options)
357
+
358
+ if (xhr.readyState > 0) {
359
+ if (options.push && !options.replace) {
360
+ // Cache current container element before replacing it
361
+ cachePush(pjax.state.id, cloneContents(context))
362
+
363
+ window.history.pushState(null, "", options.requestUrl)
364
+ }
365
+
366
+ fire('pjax:start', [xhr, options])
367
+ fire('pjax:send', [xhr, options])
368
+ }
369
+
370
+ return pjax.xhr
371
+ }
372
+
373
+ // Public: Reload current page with pjax.
374
+ //
375
+ // Returns whatever $.pjax returns.
376
+ function pjaxReload(container, options) {
377
+ var defaults = {
378
+ url: window.location.href,
379
+ push: false,
380
+ replace: true,
381
+ scrollTo: false
382
+ }
383
+
384
+ return pjax($.extend(defaults, optionsFor(container, options)))
385
+ }
386
+
387
+ // Internal: Hard replace current state with url.
388
+ //
389
+ // Work for around WebKit
390
+ // https://bugs.webkit.org/show_bug.cgi?id=93506
391
+ //
392
+ // Returns nothing.
393
+ function locationReplace(url) {
394
+ window.history.replaceState(null, "", pjax.state.url)
395
+ window.location.replace(url)
396
+ }
397
+
398
+
399
+ var initialPop = true
400
+ var initialURL = window.location.href
401
+ var initialState = window.history.state
402
+
403
+ // Initialize $.pjax.state if possible
404
+ // Happens when reloading a page and coming forward from a different
405
+ // session history.
406
+ if (initialState && initialState.container) {
407
+ pjax.state = initialState
408
+ }
409
+
410
+ // Non-webkit browsers don't fire an initial popstate event
411
+ if ('state' in window.history) {
412
+ initialPop = false
413
+ }
414
+
415
+ // popstate handler takes care of the back and forward buttons
416
+ //
417
+ // You probably shouldn't use pjax on pages with other pushState
418
+ // stuff yet.
419
+ function onPjaxPopstate(event) {
420
+
421
+ // Hitting back or forward should override any pending PJAX request.
422
+ if (!initialPop) {
423
+ abortXHR(pjax.xhr)
424
+ }
425
+
426
+ var previousState = pjax.state
427
+ var state = event.state
428
+
429
+ if (state && state.container) {
430
+ // When coming forward from a separate history session, will get an
431
+ // initial pop with a state we are already at. Skip reloading the current
432
+ // page.
433
+ if (initialPop && initialURL == state.url) return
434
+
435
+ var direction, containerSelector = state.container
436
+
437
+ if (previousState) {
438
+ // If popping back to the same state, just skip.
439
+ // Could be clicking back from hashchange rather than a pushState.
440
+ if (previousState.id === state.id) return
441
+
442
+ // Since state IDs always increase, we can deduce the navigation direction
443
+ direction = previousState.id < state.id ? 'forward' : 'back'
444
+ if (direction == 'back') containerSelector = previousState.container
445
+ }
446
+
447
+ var container = $(containerSelector)
448
+ if (container.length) {
449
+ var contents = cacheMapping[state.id]
450
+
451
+ if (previousState) {
452
+ // Cache current container before replacement and inform the
453
+ // cache which direction the history shifted.
454
+ cachePop(direction, previousState.id, cloneContents(container))
455
+ }
456
+
457
+ var popstateEvent = $.Event('pjax:popstate', {
458
+ state: state,
459
+ direction: direction
460
+ })
461
+ container.trigger(popstateEvent)
462
+
463
+ var options = {
464
+ id: state.id,
465
+ url: state.url,
466
+ container: container,
467
+ push: false,
468
+ fragment: state.fragment,
469
+ timeout: state.timeout,
470
+ scrollTo: false
471
+ }
472
+
473
+ if (contents) {
474
+ container.trigger('pjax:start', [null, options])
475
+
476
+ pjax.state = state
477
+ if (state.title) document.title = state.title
478
+ var beforeReplaceEvent = $.Event('pjax:beforeReplace', {
479
+ state: state,
480
+ previousState: previousState
481
+ })
482
+ container.trigger(beforeReplaceEvent, [contents, options])
483
+ container.html(contents)
484
+
485
+ container.trigger('pjax:end', [null, options])
486
+ } else {
487
+ pjax(options)
488
+ }
489
+
490
+ // Force reflow/relayout before the browser tries to restore the
491
+ // scroll position.
492
+ container[0].offsetHeight
493
+ } else {
494
+ locationReplace(location.href)
495
+ }
496
+ }
497
+ initialPop = false
498
+ }
499
+
500
+ // Fallback version of main pjax function for browsers that don't
501
+ // support pushState.
502
+ //
503
+ // Returns nothing since it retriggers a hard form submission.
504
+ function fallbackPjax(options) {
505
+ var url = $.isFunction(options.url) ? options.url() : options.url,
506
+ method = options.type ? options.type.toUpperCase() : 'GET'
507
+
508
+ var form = $('<form>', {
509
+ method: method === 'GET' ? 'GET' : 'POST',
510
+ action: url,
511
+ style: 'display:none'
512
+ })
513
+
514
+ if (method !== 'GET' && method !== 'POST') {
515
+ form.append($('<input>', {
516
+ type: 'hidden',
517
+ name: '_method',
518
+ value: method.toLowerCase()
519
+ }))
520
+ }
521
+
522
+ var data = options.data
523
+ if (typeof data === 'string') {
524
+ $.each(data.split('&'), function(index, value) {
525
+ var pair = value.split('=')
526
+ form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
527
+ })
528
+ } else if ($.isArray(data)) {
529
+ $.each(data, function(index, value) {
530
+ form.append($('<input>', {type: 'hidden', name: value.name, value: value.value}))
531
+ })
532
+ } else if (typeof data === 'object') {
533
+ var key
534
+ for (key in data)
535
+ form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
536
+ }
537
+
538
+ $(document.body).append(form)
539
+ form.submit()
540
+ }
541
+
542
+ // Internal: Abort an XmlHttpRequest if it hasn't been completed,
543
+ // also removing its event handlers.
544
+ function abortXHR(xhr) {
545
+ if ( xhr && xhr.readyState < 4) {
546
+ xhr.onreadystatechange = $.noop
547
+ xhr.abort()
548
+ }
549
+ }
550
+
551
+ // Internal: Generate unique id for state object.
552
+ //
553
+ // Use a timestamp instead of a counter since ids should still be
554
+ // unique across page loads.
555
+ //
556
+ // Returns Number.
557
+ function uniqueId() {
558
+ return (new Date).getTime()
559
+ }
560
+
561
+ function cloneContents(container) {
562
+ var cloned = container.clone()
563
+ // Unmark script tags as already being eval'd so they can get executed again
564
+ // when restored from cache. HAXX: Uses jQuery internal method.
565
+ cloned.find('script').each(function(){
566
+ if (!this.src) jQuery._data(this, 'globalEval', false)
567
+ })
568
+ return cloned.contents()
569
+ }
570
+
571
+ // Internal: Strips named query param from url
572
+ //
573
+ // url - String
574
+ //
575
+ // Returns String.
576
+ function stripParam(url, name) {
577
+ return url
578
+ .replace(new RegExp('[?&]' + name + '=[^&#]*'), '')
579
+ .replace(/[?&]($|#)/, '\1')
580
+ .replace(/[?&]/, '?')
581
+ }
582
+
583
+ function stripInternalParams(url) {
584
+ url = stripParam(url, '_pjax')
585
+ url = stripParam(url, '_')
586
+ return url
587
+ }
588
+
589
+ // Internal: Parse URL components and returns a Locationish object.
590
+ //
591
+ // url - String URL
592
+ //
593
+ // Returns HTMLAnchorElement that acts like Location.
594
+ function parseURL(url) {
595
+ var a = document.createElement('a')
596
+ a.href = url
597
+ return a
598
+ }
599
+
600
+ // Internal: Return the `href` component of given URL object with the hash
601
+ // portion removed.
602
+ //
603
+ // location - Location or HTMLAnchorElement
604
+ //
605
+ // Returns String
606
+ function stripHash(location) {
607
+ return location.href.replace(/#.*/, '')
608
+ }
609
+
610
+ // Internal: Build options Object for arguments.
611
+ //
612
+ // For convenience the first parameter can be either the container or
613
+ // the options object.
614
+ //
615
+ // Examples
616
+ //
617
+ // optionsFor('#container')
618
+ // // => {container: '#container'}
619
+ //
620
+ // optionsFor('#container', {push: true})
621
+ // // => {container: '#container', push: true}
622
+ //
623
+ // optionsFor({container: '#container', push: true})
624
+ // // => {container: '#container', push: true}
625
+ //
626
+ // Returns options Object.
627
+ function optionsFor(container, options) {
628
+ // Both container and options
629
+ if ( container && options )
630
+ options.container = container
631
+
632
+ // First argument is options Object
633
+ else if ( $.isPlainObject(container) )
634
+ options = container
635
+
636
+ // Only container
637
+ else
638
+ options = {container: container}
639
+
640
+ // Find and validate container
641
+ if (options.container)
642
+ options.container = findContainerFor(options.container)
643
+
644
+ return options
645
+ }
646
+
647
+ // Internal: Find container element for a variety of inputs.
648
+ //
649
+ // Because we can't persist elements using the history API, we must be
650
+ // able to find a String selector that will consistently find the Element.
651
+ //
652
+ // container - A selector String, jQuery object, or DOM Element.
653
+ //
654
+ // Returns a jQuery object whose context is `document` and has a selector.
655
+ function findContainerFor(container) {
656
+ container = $(container)
657
+
658
+ if ( !container.length ) {
659
+ throw "no pjax container for " + container.selector
660
+ } else if ( container.selector !== '' && container.context === document ) {
661
+ return container
662
+ } else if ( container.attr('id') ) {
663
+ return $('#' + container.attr('id'))
664
+ } else {
665
+ throw "cant get selector for pjax container!"
666
+ }
667
+ }
668
+
669
+ // Internal: Filter and find all elements matching the selector.
670
+ //
671
+ // Where $.fn.find only matches descendants, findAll will test all the
672
+ // top level elements in the jQuery object as well.
673
+ //
674
+ // elems - jQuery object of Elements
675
+ // selector - String selector to match
676
+ //
677
+ // Returns a jQuery object.
678
+ function findAll(elems, selector) {
679
+ return elems.filter(selector).add(elems.find(selector));
680
+ }
681
+
682
+ function parseHTML(html) {
683
+ return $.parseHTML(html, document, true)
684
+ }
685
+
686
+ // Internal: Extracts container and metadata from response.
687
+ //
688
+ // 1. Extracts X-PJAX-URL header if set
689
+ // 2. Extracts inline <title> tags
690
+ // 3. Builds response Element and extracts fragment if set
691
+ //
692
+ // data - String response data
693
+ // xhr - XHR response
694
+ // options - pjax options Object
695
+ //
696
+ // Returns an Object with url, title, and contents keys.
697
+ function extractContainer(data, xhr, options) {
698
+ var obj = {}, fullDocument = /<html/i.test(data)
699
+
700
+ // Prefer X-PJAX-URL header if it was set, otherwise fallback to
701
+ // using the original requested url.
702
+ var serverUrl = xhr.getResponseHeader('X-PJAX-URL')
703
+ obj.url = serverUrl ? stripInternalParams(serverUrl) : options.requestUrl
704
+
705
+ // Attempt to parse response html into elements
706
+ if (fullDocument) {
707
+ var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]))
708
+ var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]))
709
+ } else {
710
+ var $head = $body = $(parseHTML(data))
711
+ }
712
+
713
+ // If response data is empty, return fast
714
+ if ($body.length === 0)
715
+ return obj
716
+
717
+ // If there's a <title> tag in the header, use it as
718
+ // the page's title.
719
+ obj.title = findAll($head, 'title').last().text()
720
+
721
+ if (options.fragment) {
722
+ // If they specified a fragment, look for it in the response
723
+ // and pull it out.
724
+ if (options.fragment === 'body') {
725
+ var $fragment = $body
726
+ } else {
727
+ var $fragment = findAll($body, options.fragment).first()
728
+ }
729
+
730
+ if ($fragment.length) {
731
+ obj.contents = options.fragment === 'body' ? $fragment : $fragment.contents()
732
+
733
+ // If there's no title, look for data-title and title attributes
734
+ // on the fragment
735
+ if (!obj.title)
736
+ obj.title = $fragment.attr('title') || $fragment.data('title')
737
+ }
738
+
739
+ } else if (!fullDocument) {
740
+ obj.contents = $body
741
+ }
742
+
743
+ // Clean up any <title> tags
744
+ if (obj.contents) {
745
+ // Remove any parent title elements
746
+ obj.contents = obj.contents.not(function() { return $(this).is('title') })
747
+
748
+ // Then scrub any titles from their descendants
749
+ obj.contents.find('title').remove()
750
+
751
+ // Gather all script[src] elements
752
+ obj.scripts = findAll(obj.contents, 'script[src]').remove()
753
+ obj.contents = obj.contents.not(obj.scripts)
754
+ }
755
+
756
+ // Trim any whitespace off the title
757
+ if (obj.title) obj.title = $.trim(obj.title)
758
+
759
+ return obj
760
+ }
761
+
762
+ // Load an execute scripts using standard script request.
763
+ //
764
+ // Avoids jQuery's traditional $.getScript which does a XHR request and
765
+ // globalEval.
766
+ //
767
+ // scripts - jQuery object of script Elements
768
+ //
769
+ // Returns nothing.
770
+ function executeScriptTags(scripts) {
771
+ if (!scripts) return
772
+
773
+ var existingScripts = $('script[src]')
774
+
775
+ scripts.each(function() {
776
+ var src = this.src
777
+ var matchedScripts = existingScripts.filter(function() {
778
+ return this.src === src
779
+ })
780
+ if (matchedScripts.length) return
781
+
782
+ var script = document.createElement('script')
783
+ var type = $(this).attr('type')
784
+ if (type) script.type = type
785
+ script.src = $(this).attr('src')
786
+ document.head.appendChild(script)
787
+ })
788
+ }
789
+
790
+ // Internal: History DOM caching class.
791
+ var cacheMapping = {}
792
+ var cacheForwardStack = []
793
+ var cacheBackStack = []
794
+
795
+ // Push previous state id and container contents into the history
796
+ // cache. Should be called in conjunction with `pushState` to save the
797
+ // previous container contents.
798
+ //
799
+ // id - State ID Number
800
+ // value - DOM Element to cache
801
+ //
802
+ // Returns nothing.
803
+ function cachePush(id, value) {
804
+ cacheMapping[id] = value
805
+ cacheBackStack.push(id)
806
+
807
+ // Remove all entries in forward history stack after pushing a new page.
808
+ trimCacheStack(cacheForwardStack, 0)
809
+
810
+ // Trim back history stack to max cache length.
811
+ trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength)
812
+ }
813
+
814
+ // Shifts cache from directional history cache. Should be
815
+ // called on `popstate` with the previous state id and container
816
+ // contents.
817
+ //
818
+ // direction - "forward" or "back" String
819
+ // id - State ID Number
820
+ // value - DOM Element to cache
821
+ //
822
+ // Returns nothing.
823
+ function cachePop(direction, id, value) {
824
+ var pushStack, popStack
825
+ cacheMapping[id] = value
826
+
827
+ if (direction === 'forward') {
828
+ pushStack = cacheBackStack
829
+ popStack = cacheForwardStack
830
+ } else {
831
+ pushStack = cacheForwardStack
832
+ popStack = cacheBackStack
833
+ }
834
+
835
+ pushStack.push(id)
836
+ if (id = popStack.pop())
837
+ delete cacheMapping[id]
838
+
839
+ // Trim whichever stack we just pushed to to max cache length.
840
+ trimCacheStack(pushStack, pjax.defaults.maxCacheLength)
841
+ }
842
+
843
+ // Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no
844
+ // longer than the specified length, deleting cached DOM elements as necessary.
845
+ //
846
+ // stack - Array of state IDs
847
+ // length - Maximum length to trim to
848
+ //
849
+ // Returns nothing.
850
+ function trimCacheStack(stack, length) {
851
+ while (stack.length > length)
852
+ delete cacheMapping[stack.shift()]
853
+ }
854
+
855
+ // Public: Find version identifier for the initial page load.
856
+ //
857
+ // Returns String version or undefined.
858
+ function findVersion() {
859
+ return $('meta').filter(function() {
860
+ var name = $(this).attr('http-equiv')
861
+ return name && name.toUpperCase() === 'X-PJAX-VERSION'
862
+ }).attr('content')
863
+ }
864
+
865
+ // Install pjax functions on $.pjax to enable pushState behavior.
866
+ //
867
+ // Does nothing if already enabled.
868
+ //
869
+ // Examples
870
+ //
871
+ // $.pjax.enable()
872
+ //
873
+ // Returns nothing.
874
+ function enable() {
875
+ $.fn.pjax = fnPjax
876
+ $.pjax = pjax
877
+ $.pjax.enable = $.noop
878
+ $.pjax.disable = disable
879
+ $.pjax.click = handleClick
880
+ $.pjax.submit = handleSubmit
881
+ $.pjax.reload = pjaxReload
882
+ $.pjax.defaults = {
883
+ timeout: 650,
884
+ push: true,
885
+ replace: false,
886
+ type: 'GET',
887
+ dataType: 'html',
888
+ scrollTo: 0,
889
+ maxCacheLength: 20,
890
+ version: findVersion
891
+ }
892
+ $(window).on('popstate.pjax', onPjaxPopstate)
893
+ }
894
+
895
+ // Disable pushState behavior.
896
+ //
897
+ // This is the case when a browser doesn't support pushState. It is
898
+ // sometimes useful to disable pushState for debugging on a modern
899
+ // browser.
900
+ //
901
+ // Examples
902
+ //
903
+ // $.pjax.disable()
904
+ //
905
+ // Returns nothing.
906
+ function disable() {
907
+ $.fn.pjax = function() { return this }
908
+ $.pjax = fallbackPjax
909
+ $.pjax.enable = enable
910
+ $.pjax.disable = $.noop
911
+ $.pjax.click = $.noop
912
+ $.pjax.submit = $.noop
913
+ $.pjax.reload = function() { window.location.reload() }
914
+
915
+ $(window).off('popstate.pjax', onPjaxPopstate)
916
+ }
917
+
918
+
919
+ // Add the state property to jQuery's event object so we can use it in
920
+ // $(window).bind('popstate')
921
+ if ( $.inArray('state', $.event.props) < 0 )
922
+ $.event.props.push('state')
923
+
924
+ // Is pjax supported by this browser?
925
+ $.support.pjax =
926
+ window.history && window.history.pushState && window.history.replaceState &&
927
+ // pushState isn't reliable on iOS until 5.
928
+ !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)
929
+
930
+ $.support.pjax ? enable() : disable()
931
+
932
+ })(jQuery);