virgo 0.1

Sign up to get free protection for your applications and to get access to all the features.
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);