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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +37 -0
- data/app/assets/javascripts/tinymce/manifest.coffee +1 -0
- data/app/assets/javascripts/tinymce/plugins/image_manager/plugin.coffee +234 -0
- data/app/assets/javascripts/virgo/admin/page_modules/edit_form.coffee +4 -0
- data/app/assets/javascripts/virgo/admin/page_modules/edit_subject_form.coffee +29 -0
- data/app/assets/javascripts/virgo/admin/posts/edit_locking.coffee +35 -0
- data/app/assets/javascripts/virgo/admin/posts/form.coffee +183 -0
- data/app/assets/javascripts/virgo/admin/slides/form.coffee +27 -0
- data/app/assets/javascripts/virgo/admin/slideshows/form.coffee +26 -0
- data/app/assets/javascripts/virgo/admin_only.coffee +1 -0
- data/app/assets/javascripts/virgo/application.coffee +51 -0
- data/app/assets/javascripts/virgo/common.coffee +83 -0
- data/app/assets/javascripts/virgo/common/character_counter.coffee +27 -0
- data/app/assets/javascripts/virgo/components/header.coffee +47 -0
- data/app/assets/javascripts/virgo/components/media_modal.coffee +151 -0
- data/app/assets/javascripts/virgo/components/share_bar.coffee +53 -0
- data/app/assets/javascripts/virgo/components/slideshow.coffee +35 -0
- data/app/assets/javascripts/virgo/lib/jquery-ui-multiselect.js +342 -0
- data/app/assets/javascripts/virgo/lib/jquery.browser.js +184 -0
- data/app/assets/javascripts/virgo/lib/jquery.cookie.js +114 -0
- data/app/assets/javascripts/virgo/lib/jquery.pjax.js +932 -0
- data/app/assets/javascripts/virgo/lib/jquery_ext.coffee +0 -0
- data/app/assets/javascripts/virgo/lib/manifest.coffee +5 -0
- data/app/assets/javascripts/virgo/lib/ouibounce.js +194 -0
- data/app/assets/javascripts/virgo/page_modules.coffee +175 -0
- data/app/assets/javascripts/virgo/posts/show.coffee +32 -0
- data/app/assets/stylesheets/tinymce/skin.sass +2 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/content.min.sass +82 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce-small.eot +0 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce-small.svg +62 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce-small.ttf +0 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce-small.woff +0 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce.eot +0 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce.svg +63 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce.ttf +0 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/fonts/tinymce.woff +0 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/img/anchor.gif +0 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/img/loader.gif +0 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/img/object.gif +0 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/img/trans.gif +0 -0
- data/app/assets/stylesheets/tinymce/skins/railspress/skin.min.sass +1703 -0
- data/app/assets/stylesheets/virgo/_common.sass +65 -0
- data/app/assets/stylesheets/virgo/admin.sass +301 -0
- data/app/assets/stylesheets/virgo/admin/columns.sass +3 -0
- data/app/assets/stylesheets/virgo/admin/images.sass +14 -0
- data/app/assets/stylesheets/virgo/admin/media_modal.sass +72 -0
- data/app/assets/stylesheets/virgo/admin/post.sass +102 -0
- data/app/assets/stylesheets/virgo/admin/slideshow.sass +35 -0
- data/app/assets/stylesheets/virgo/admin/users.sass +2 -0
- data/app/assets/stylesheets/virgo/admin_only.sass +1 -0
- data/app/assets/stylesheets/virgo/application.sass +605 -0
- data/app/assets/stylesheets/virgo/columns.sass +51 -0
- data/app/assets/stylesheets/virgo/install.sass +15 -0
- data/app/assets/stylesheets/virgo/lib/bootstrap/bootstrap-imports.sass +9 -0
- data/app/assets/stylesheets/virgo/lib/bootstrap/bootstrap-overrides.sass +51 -0
- data/app/assets/stylesheets/virgo/lib/bootstrap/bootstrap-variables.sass +27 -0
- data/app/assets/stylesheets/virgo/lib/jquery-ui-multiselect.css +30 -0
- data/app/assets/stylesheets/virgo/lib/manifest.sass +3 -0
- data/app/assets/stylesheets/virgo/lib/ouibounce.css +222 -0
- data/app/assets/stylesheets/virgo/lib/select2-imports.sass +8 -0
- data/app/assets/stylesheets/virgo/page_modules.sass +54 -0
- data/app/assets/stylesheets/virgo/pages/authors.sass +30 -0
- data/app/assets/stylesheets/virgo/posts.sass +782 -0
- data/app/assets/stylesheets/virgo/search.sass +36 -0
- data/app/assets/stylesheets/virgo/ui.sass +434 -0
- data/app/assets/stylesheets/virgo/users.sass +9 -0
- data/app/controllers/virgo/admin/base_controller.rb +22 -0
- data/app/controllers/virgo/admin/categories_controller.rb +90 -0
- data/app/controllers/virgo/admin/columns_controller.rb +65 -0
- data/app/controllers/virgo/admin/images_controller.rb +89 -0
- data/app/controllers/virgo/admin/media_modal_controller.rb +101 -0
- data/app/controllers/virgo/admin/newsletters_controller.rb +37 -0
- data/app/controllers/virgo/admin/page_modules_controller.rb +62 -0
- data/app/controllers/virgo/admin/pages_controller.rb +6 -0
- data/app/controllers/virgo/admin/posts_controller.rb +173 -0
- data/app/controllers/virgo/admin/sites_controller.rb +31 -0
- data/app/controllers/virgo/admin/slides_controller.rb +71 -0
- data/app/controllers/virgo/admin/slideshows_controller.rb +66 -0
- data/app/controllers/virgo/admin/tags_controller.rb +98 -0
- data/app/controllers/virgo/admin/users/sessions_controller.rb +21 -0
- data/app/controllers/virgo/admin/users_controller.rb +84 -0
- data/app/controllers/virgo/application_controller.rb +151 -0
- data/app/controllers/virgo/categories_controller.rb +20 -0
- data/app/controllers/virgo/columns_controller.rb +24 -0
- data/app/controllers/virgo/images_controller.rb +9 -0
- data/app/controllers/virgo/install_controller.rb +46 -0
- data/app/controllers/virgo/page_modules_controller.rb +23 -0
- data/app/controllers/virgo/pages_controller.rb +29 -0
- data/app/controllers/virgo/posts_controller.rb +101 -0
- data/app/controllers/virgo/search_controller.rb +35 -0
- data/app/controllers/virgo/subscribers_controller.rb +54 -0
- data/app/controllers/virgo/tags_controller.rb +15 -0
- data/app/controllers/virgo/users/confirmations_controller.rb +18 -0
- data/app/controllers/virgo/users/passwords_controller.rb +31 -0
- data/app/controllers/virgo/users/registrations_controller.rb +41 -0
- data/app/controllers/virgo/users/sessions_controller.rb +32 -0
- data/app/controllers/virgo/users_controller.rb +15 -0
- data/app/helpers/virgo/admin/post_helper.rb +54 -0
- data/app/helpers/virgo/admin_helper.rb +15 -0
- data/app/helpers/virgo/application_helper.rb +124 -0
- data/app/helpers/virgo/hooks_helper.rb +30 -0
- data/app/helpers/virgo/page_modules_helper.rb +4 -0
- data/app/helpers/virgo/post_helper.rb +159 -0
- data/app/helpers/virgo/render_helper.rb +33 -0
- data/app/helpers/virgo/search_helper.rb +49 -0
- data/app/helpers/virgo/shortcode_helper.rb +7 -0
- data/app/helpers/virgo/text_helper.rb +12 -0
- data/app/helpers/virgo/video_helper.rb +34 -0
- data/app/models/concerns/virgo/common/slug_history.rb +36 -0
- data/app/models/concerns/virgo/common/uuid.rb +22 -0
- data/app/models/concerns/virgo/post/recommendations.rb +15 -0
- data/app/models/concerns/virgo/post/search.rb +100 -0
- data/app/models/concerns/virgo/user/search.rb +39 -0
- data/app/models/virgo/ability.rb +59 -0
- data/app/models/virgo/category.rb +44 -0
- data/app/models/virgo/column.rb +25 -0
- data/app/models/virgo/form_model.rb +19 -0
- data/app/models/virgo/image.rb +63 -0
- data/app/models/virgo/page_module.rb +42 -0
- data/app/models/virgo/page_module_post.rb +8 -0
- data/app/models/virgo/post.rb +391 -0
- data/app/models/virgo/post_category.rb +8 -0
- data/app/models/virgo/post_search.rb +7 -0
- data/app/models/virgo/post_tag.rb +10 -0
- data/app/models/virgo/site.rb +36 -0
- data/app/models/virgo/slide.rb +23 -0
- data/app/models/virgo/slideshow.rb +37 -0
- data/app/models/virgo/slug_history.rb +5 -0
- data/app/models/virgo/subscriber.rb +5 -0
- data/app/models/virgo/tag.rb +34 -0
- data/app/models/virgo/user.rb +145 -0
- data/app/uploaders/virgo/application_uploader.rb +11 -0
- data/app/uploaders/virgo/avatar_uploader.rb +21 -0
- data/app/uploaders/virgo/featured_image_uploader.rb +28 -0
- data/app/uploaders/virgo/image_uploader.rb +40 -0
- data/app/uploaders/virgo/slide_uploader.rb +19 -0
- data/app/views/layouts/virgo/admin.haml +16 -0
- data/app/views/layouts/virgo/admin/site_settings.haml +14 -0
- data/app/views/layouts/virgo/application.haml +63 -0
- data/app/views/layouts/virgo/errors.haml +42 -0
- data/app/views/layouts/virgo/main.haml +5 -0
- data/app/views/layouts/virgo/posts.haml +10 -0
- data/app/views/virgo/admin/categories/_form.haml +19 -0
- data/app/views/virgo/admin/categories/edit.haml +7 -0
- data/app/views/virgo/admin/categories/index.haml +38 -0
- data/app/views/virgo/admin/categories/modal_form.haml +11 -0
- data/app/views/virgo/admin/categories/new.haml +7 -0
- data/app/views/virgo/admin/categories/success_modal.haml +14 -0
- data/app/views/virgo/admin/columns/_form.haml +30 -0
- data/app/views/virgo/admin/columns/edit.haml +1 -0
- data/app/views/virgo/admin/columns/index.haml +30 -0
- data/app/views/virgo/admin/columns/new.haml +1 -0
- data/app/views/virgo/admin/images/_form.haml +46 -0
- data/app/views/virgo/admin/images/edit.haml +1 -0
- data/app/views/virgo/admin/images/index.haml +32 -0
- data/app/views/virgo/admin/images/new.haml +1 -0
- data/app/views/virgo/admin/media_modal/_image_settings.haml +17 -0
- data/app/views/virgo/admin/media_modal/_library_panel.haml +26 -0
- data/app/views/virgo/admin/media_modal/_upload_panel.haml +21 -0
- data/app/views/virgo/admin/media_modal/_upload_success.haml +7 -0
- data/app/views/virgo/admin/media_modal/index.haml +19 -0
- data/app/views/virgo/admin/newsletters/changelog.haml +25 -0
- data/app/views/virgo/admin/newsletters/edit.haml +6 -0
- data/app/views/virgo/admin/page_modules/edit.haml +33 -0
- data/app/views/virgo/admin/page_modules/edit_subject.haml +43 -0
- data/app/views/virgo/admin/pages/help.haml +114 -0
- data/app/views/virgo/admin/posts/_author_dropdown.haml +1 -0
- data/app/views/virgo/admin/posts/_categories_form.haml +7 -0
- data/app/views/virgo/admin/posts/_category.haml +22 -0
- data/app/views/virgo/admin/posts/_form.haml +201 -0
- data/app/views/virgo/admin/posts/_revisions.haml +40 -0
- data/app/views/virgo/admin/posts/edit.haml +9 -0
- data/app/views/virgo/admin/posts/index.haml +59 -0
- data/app/views/virgo/admin/posts/new.haml +1 -0
- data/app/views/virgo/admin/posts/revision_detail.haml +28 -0
- data/app/views/virgo/admin/posts/revisions.haml +3 -0
- data/app/views/virgo/admin/sites/edit.haml +12 -0
- data/app/views/virgo/admin/slides/_form.haml +41 -0
- data/app/views/virgo/admin/slides/edit.haml +1 -0
- data/app/views/virgo/admin/slides/new.haml +1 -0
- data/app/views/virgo/admin/slideshows/_form.haml +49 -0
- data/app/views/virgo/admin/slideshows/edit.haml +1 -0
- data/app/views/virgo/admin/slideshows/index.haml +43 -0
- data/app/views/virgo/admin/slideshows/new.haml +1 -0
- data/app/views/virgo/admin/tags/_form.haml +16 -0
- data/app/views/virgo/admin/tags/edit.haml +7 -0
- data/app/views/virgo/admin/tags/index.haml +33 -0
- data/app/views/virgo/admin/tags/modal_form.haml +12 -0
- data/app/views/virgo/admin/tags/new.haml +7 -0
- data/app/views/virgo/admin/tags/success_modal.haml +14 -0
- data/app/views/virgo/admin/users/_form.haml +62 -0
- data/app/views/virgo/admin/users/edit.haml +1 -0
- data/app/views/virgo/admin/users/index.haml +34 -0
- data/app/views/virgo/admin/users/new.haml +1 -0
- data/app/views/virgo/application/sitemap.xml.builder +0 -0
- data/app/views/virgo/authors/_profile_long.haml +13 -0
- data/app/views/virgo/authors/_profile_short.haml +10 -0
- data/app/views/virgo/categories/show.haml +24 -0
- data/app/views/virgo/columns/index.haml +19 -0
- data/app/views/virgo/columns/show.haml +18 -0
- data/app/views/virgo/common/_admin_navbar.haml +100 -0
- data/app/views/virgo/common/_alerts.haml +40 -0
- data/app/views/virgo/common/_analytics.html.erb +1 -0
- data/app/views/virgo/common/_base_errors.haml +8 -0
- data/app/views/virgo/common/_category_heading.haml +26 -0
- data/app/views/virgo/common/_disqus.html.erb +24 -0
- data/app/views/virgo/common/_disqus_comment_count.html.erb +12 -0
- data/app/views/virgo/common/_fb_api.html.erb +30 -0
- data/app/views/virgo/common/_fonts.html.erb +1 -0
- data/app/views/virgo/common/_footer.haml +13 -0
- data/app/views/virgo/common/_footer_javascript.html.erb +0 -0
- data/app/views/virgo/common/_header_analytics.html.erb +9 -0
- data/app/views/virgo/common/_list_signup.haml +20 -0
- data/app/views/virgo/common/_ouibounce_modal.haml +26 -0
- data/app/views/virgo/common/_public_nav.haml +58 -0
- data/app/views/virgo/common/_search_form.haml +8 -0
- data/app/views/virgo/common/_share_bar.haml +45 -0
- data/app/views/virgo/common/_user_vars.haml +2 -0
- data/app/views/virgo/errors/404.haml +10 -0
- data/app/views/virgo/errors/500.haml +10 -0
- data/app/views/virgo/images/_shortcode.haml +4 -0
- data/app/views/virgo/install/index.haml +58 -0
- data/app/views/virgo/install/success.haml +12 -0
- data/app/views/virgo/page_modules/_latest_posts.haml +29 -0
- data/app/views/virgo/page_modules/_popular_posts.haml +51 -0
- data/app/views/virgo/pages/authors.haml +8 -0
- data/app/views/virgo/pages/home.haml +0 -0
- data/app/views/virgo/pages/show.haml +6 -0
- data/app/views/virgo/posts/_author_info.haml +16 -0
- data/app/views/virgo/posts/_post_full.haml +63 -0
- data/app/views/virgo/posts/_post_short.haml +30 -0
- data/app/views/virgo/posts/_post_thumb_box.haml +13 -0
- data/app/views/virgo/posts/_recommendations.haml +12 -0
- data/app/views/virgo/posts/_sidebar_thumb.haml +4 -0
- data/app/views/virgo/posts/index.haml +22 -0
- data/app/views/virgo/posts/latest.haml +16 -0
- data/app/views/virgo/posts/more.haml +10 -0
- data/app/views/virgo/posts/rss.xml.builder +17 -0
- data/app/views/virgo/posts/show.haml +17 -0
- data/app/views/virgo/search/_form.haml +5 -0
- data/app/views/virgo/search/_item.haml +22 -0
- data/app/views/virgo/search/index.haml +11 -0
- data/app/views/virgo/search/new.haml +4 -0
- data/app/views/virgo/shortcode_templates/blockquote.haml +9 -0
- data/app/views/virgo/shortcode_templates/h1.haml +1 -0
- data/app/views/virgo/shortcode_templates/h2.haml +1 -0
- data/app/views/virgo/shortcode_templates/h3.haml +1 -0
- data/app/views/virgo/shortcode_templates/h4.haml +1 -0
- data/app/views/virgo/shortcode_templates/image.haml +17 -0
- data/app/views/virgo/shortcode_templates/pullquote.haml +3 -0
- data/app/views/virgo/shortcode_templates/slideshow.haml +35 -0
- data/app/views/virgo/shortcode_templates/tweet.haml +3 -0
- data/app/views/virgo/shortcode_templates/video.haml +8 -0
- data/app/views/virgo/subscribers/_success_modal.haml +13 -0
- data/app/views/virgo/subscribers/modal.haml +15 -0
- data/app/views/virgo/tags/show.haml +16 -0
- data/app/views/virgo/users/confirmations/new.html.haml +10 -0
- data/app/views/virgo/users/devise_mailer/confirmation_instructions.html.erb +7 -0
- data/app/views/virgo/users/devise_mailer/reset_password_instructions.html.haml +6 -0
- data/app/views/virgo/users/devise_mailer/unlock_instructions.html.haml +5 -0
- data/app/views/virgo/users/passwords/edit.html.haml +14 -0
- data/app/views/virgo/users/passwords/new.html.haml +14 -0
- data/app/views/virgo/users/registrations/edit.html.haml +30 -0
- data/app/views/virgo/users/registrations/new.html.haml +13 -0
- data/app/views/virgo/users/sessions/new.html.haml +12 -0
- data/app/views/virgo/users/shared/_links.haml +26 -0
- data/app/views/virgo/users/show.haml +11 -0
- data/app/views/virgo/users/unlocks/new.html.haml +9 -0
- data/config/initializers/action_mailer.rb +7 -0
- data/config/initializers/active_record.rb +72 -0
- data/config/initializers/active_record_symbolize.rb +18 -0
- data/config/initializers/assets.rb +17 -0
- data/config/initializers/backtrace_silencers.rb +7 -0
- data/config/initializers/carrierwave.rb +36 -0
- data/config/initializers/cookies_serializer.rb +3 -0
- data/config/initializers/devise.rb +257 -0
- data/config/initializers/filter_parameter_logging.rb +4 -0
- data/config/initializers/hash.rb +5 -0
- data/config/initializers/inflections.rb +16 -0
- data/config/initializers/mime_types.rb +4 -0
- data/config/initializers/nil_class.rb +13 -0
- data/config/initializers/non_digest_assets.rb +3 -0
- data/config/initializers/paper_trail.rb +30 -0
- data/config/initializers/schema_plus.rb +5 -0
- data/config/initializers/session_store.rb +3 -0
- data/config/initializers/shortcode.rb +12 -0
- data/config/initializers/simple_form.rb +170 -0
- data/config/initializers/simple_form_bootstrap.rb +136 -0
- data/config/initializers/string.rb +5 -0
- data/config/initializers/time_formatting.rb +23 -0
- data/config/initializers/wrap_parameters.rb +14 -0
- data/config/locales/devise.en.yml +60 -0
- data/config/routes.rb +96 -0
- data/config/schedule.rb +3 -0
- data/db/migrate/20150729174510_create_virgo_schema.rb +225 -0
- data/lib/generators/virgo/install/install_generator.rb +16 -0
- data/lib/generators/virgo/views/views_generator.rb +22 -0
- data/lib/tasks/virgo_tasks.rake +7 -0
- data/lib/virgo.rb +4 -0
- data/lib/virgo/development_mail_interceptor.rb +6 -0
- data/lib/virgo/email_validator.rb +6 -0
- data/lib/virgo/engine.rb +73 -0
- data/lib/virgo/reduce_validator.rb +6 -0
- data/lib/virgo/upload_helpers.rb +89 -0
- data/lib/virgo/version.rb +3 -0
- data/lib/virgo/view_help.rb +3 -0
- 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);
|