jobbr 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +22 -0
  4. data/Gemfile.lock +164 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +3 -0
  7. data/Rakefile +28 -0
  8. data/app/assets/images/jobbr/.gitkeep +0 -0
  9. data/app/assets/javascripts/jobbr/application.js.coffee +34 -0
  10. data/app/assets/stylesheets/jobbr/application.css.scss +79 -0
  11. data/app/controllers/jobbr/application_controller.rb +19 -0
  12. data/app/controllers/jobbr/delayed_jobs_controller.rb +17 -0
  13. data/app/controllers/jobbr/jobs_controller.rb +17 -0
  14. data/app/controllers/jobbr/runs_controller.rb +12 -0
  15. data/app/helpers/jobbr/application_helper.rb +36 -0
  16. data/app/models/jobbr/delayed_job.rb +38 -0
  17. data/app/models/jobbr/job.rb +110 -0
  18. data/app/models/jobbr/log_message.rb +15 -0
  19. data/app/models/jobbr/run.rb +61 -0
  20. data/app/models/jobbr/scheduled_job.rb +29 -0
  21. data/app/models/jobbr/standalone_tasks.rb +56 -0
  22. data/app/views/jobbr/jobs/_job_list.html.haml +23 -0
  23. data/app/views/jobbr/jobs/index.html.haml +6 -0
  24. data/app/views/jobbr/jobs/show.html.haml +30 -0
  25. data/app/views/jobbr/runs/_logs.html.haml +7 -0
  26. data/app/views/jobbr/runs/show.html.haml +31 -0
  27. data/app/views/layouts/jobbr/application.html.haml +20 -0
  28. data/config/locales/jobbr.en.yml +39 -0
  29. data/config/routes.rb +11 -0
  30. data/jobbr.gemspec +25 -0
  31. data/lib/jobbr.rb +4 -0
  32. data/lib/jobbr/engine.rb +7 -0
  33. data/lib/jobbr/logger.rb +55 -0
  34. data/lib/jobbr/mongoid.rb +54 -0
  35. data/lib/jobbr/version.rb +3 -0
  36. data/lib/jobbr/whenever.rb +24 -0
  37. data/lib/tasks/jobbr_tasks.rake +14 -0
  38. data/script/rails +8 -0
  39. data/spec/dummy/README.rdoc +261 -0
  40. data/spec/dummy/Rakefile +7 -0
  41. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  42. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  43. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  44. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  45. data/spec/dummy/app/mailers/.gitkeep +0 -0
  46. data/spec/dummy/app/models/.gitkeep +0 -0
  47. data/spec/dummy/app/models/scheduled_jobs/dummy_scheduled_job.rb +15 -0
  48. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  49. data/spec/dummy/config.ru +4 -0
  50. data/spec/dummy/config/application.rb +62 -0
  51. data/spec/dummy/config/boot.rb +10 -0
  52. data/spec/dummy/config/database.yml +25 -0
  53. data/spec/dummy/config/environment.rb +5 -0
  54. data/spec/dummy/config/environments/development.rb +37 -0
  55. data/spec/dummy/config/environments/production.rb +67 -0
  56. data/spec/dummy/config/environments/test.rb +37 -0
  57. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  58. data/spec/dummy/config/initializers/inflections.rb +15 -0
  59. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  60. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  61. data/spec/dummy/config/initializers/session_store.rb +8 -0
  62. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  63. data/spec/dummy/config/locales/en.yml +5 -0
  64. data/spec/dummy/config/mongoid.yml +80 -0
  65. data/spec/dummy/config/routes.rb +4 -0
  66. data/spec/dummy/config/schedule.rb +10 -0
  67. data/spec/dummy/lib/assets/.gitkeep +0 -0
  68. data/spec/dummy/log/.gitkeep +0 -0
  69. data/spec/dummy/public/404.html +26 -0
  70. data/spec/dummy/public/422.html +26 -0
  71. data/spec/dummy/public/500.html +25 -0
  72. data/spec/dummy/public/favicon.ico +0 -0
  73. data/spec/dummy/script/rails +6 -0
  74. data/spec/models/delayed_job_spec.rb +37 -0
  75. data/spec/models/scheduled_job_spec.rb +106 -0
  76. data/spec/spec_helper.rb +32 -0
  77. data/vendor/assets/fonts/FontAwesome.otf +0 -0
  78. data/vendor/assets/fonts/fontawesome-webfont.eot +0 -0
  79. data/vendor/assets/fonts/fontawesome-webfont.svg +284 -0
  80. data/vendor/assets/fonts/fontawesome-webfont.ttf +0 -0
  81. data/vendor/assets/fonts/fontawesome-webfont.woff +0 -0
  82. data/vendor/assets/javascripts/bootstrap.js +7 -0
  83. data/vendor/assets/javascripts/jquery-pjax.js +677 -0
  84. data/vendor/assets/stylesheets/bootstrap.css.scss +705 -0
  85. data/vendor/assets/stylesheets/font-awesome.css.scss +534 -0
  86. metadata +275 -0
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Bootstrap.js by @fat & @mdo
3
+ * plugins: bootstrap-transition.js, bootstrap-modal.js, bootstrap-dropdown.js, bootstrap-scrollspy.js, bootstrap-tab.js, bootstrap-tooltip.js, bootstrap-popover.js, bootstrap-affix.js, bootstrap-alert.js, bootstrap-button.js, bootstrap-collapse.js, bootstrap-carousel.js, bootstrap-typeahead.js
4
+ * Copyright 2012 Twitter, Inc.
5
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
6
+ */
7
+ !function(a){a(function(){a.support.transition=function(){var a=function(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},c;for(c in b)if(a.style[c]!==undefined)return b[c]}();return a&&{end:a}}()})}(window.jQuery),!function(a){var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this,c=a.Event("show");this.$element.trigger(c);if(this.isShown||c.isDefaultPrevented())return;a("body").addClass("modal-open"),this.isShown=!0,this.escape(),this.backdrop(function(){var c=a.support.transition&&b.$element.hasClass("fade");b.$element.parent().length||b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in").attr("aria-hidden",!1).focus(),b.enforceFocus(),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();var c=this;b=a.Event("hide"),this.$element.trigger(b);if(!this.isShown||b.isDefaultPrevented())return;this.isShown=!1,a("body").removeClass("modal-open"),this.escape(),a(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),a.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var b=this;a(document).on("focusin.modal",function(a){b.$element[0]!==a.target&&!b.$element.has(a.target).length&&b.$element.focus()})},escape:function(){var a=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(b){b.which==27&&a.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),b.hideModal()},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),b.hideModal()})},hideModal:function(a){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,a.proxy(this.removeBackdrop,this)):this.removeBackdrop()):b&&b()}},a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a(function(){a("body").on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d=c.attr("href"),e=a(c.attr("data-target")||d&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({remote:!/#/.test(d)&&d},e.data(),c.data());b.preventDefault(),e.modal(f).one("hide",function(){c.focus()})})})}(window.jQuery),!function(a){function d(){e(a(b)).removeClass("open")}function e(b){var c=b.attr("data-target"),d;return c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,"")),d=a(c),d.length||(d=b.parent()),d}var b="[data-toggle=dropdown]",c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),f,g;if(c.is(".disabled, :disabled"))return;return f=e(c),g=f.hasClass("open"),d(),g||(f.toggleClass("open"),c.focus()),!1},keydown:function(b){var c,d,f,g,h,i;if(!/(38|40|27)/.test(b.keyCode))return;c=a(this),b.preventDefault(),b.stopPropagation();if(c.is(".disabled, :disabled"))return;g=e(c),h=g.hasClass("open");if(!h||h&&b.keyCode==27)return c.click();d=a("[role=menu] li:not(.divider) a",g);if(!d.length)return;i=d.index(d.filter(":focus")),b.keyCode==38&&i>0&&i--,b.keyCode==40&&i<d.length-1&&i++,~i||(i=0),d.eq(i).focus()}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api touchstart.dropdown.data-api",d),a("body").on("click.dropdown touchstart.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.dropdown.data-api touchstart.dropdown.data-api",b,c.prototype.toggle).on("keydown.dropdown.data-api touchstart.dropdown.data-api",b+", [role=menu]",c.prototype.keydown)})}(window.jQuery),!function(a){function b(b,c){var d=a.proxy(this.process,this),e=a(b).is("body")?a(window):a(b),f;this.options=a.extend({},a.fn.scrollspy.defaults,c),this.$scrollElement=e.on("scroll.scroll-spy.data-api",d),this.selector=(this.options.target||(f=a(b).attr("href"))&&f.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=a("body"),this.refresh(),this.process()}b.prototype={constructor:b,refresh:function(){var b=this,c;this.offsets=a([]),this.targets=a([]),c=this.$body.find(this.selector).map(function(){var b=a(this),c=b.data("target")||b.attr("href"),d=/^#\w/.test(c)&&a(c);return d&&d.length&&[[d.position().top,c]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,c=b-this.$scrollElement.height(),d=this.offsets,e=this.targets,f=this.activeTarget,g;if(a>=c)return f!=(g=e.last()[0])&&this.activate(g);for(g=d.length;g--;)f!=e[g]&&a>=d[g]&&(!d[g+1]||a<=d[g+1])&&this.activate(e[g])},activate:function(b){var c,d;this.activeTarget=b,a(this.selector).parent(".active").removeClass("active"),d=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',c=a(d).parent("li").addClass("active"),c.parent(".dropdown-menu").length&&(c=c.closest("li.dropdown").addClass("active")),c.trigger("activate")}},a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),!function(a){var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f,g;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active a").last()[0],g=a.Event("show",{relatedTarget:e}),b.trigger(g);if(g.isDefaultPrevented())return;f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}},a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a(function(){a("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})})}(window.jQuery),!function(a){var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,this.options.trigger=="click"?this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this)):this.options.trigger!="manual"&&(e=this.options.trigger=="hover"?"mouseenter":"focus",f=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(e+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f+"."+this.type,this.options.selector,a.proxy(this.leave,this))),this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,b,this.$element.data()),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);if(!c.options.delay||!c.options.delay.show)return c.show();clearTimeout(this.timeout),c.hoverState="in",this.timeout=setTimeout(function(){c.hoverState=="in"&&c.show()},c.options.delay.show)},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!c.options.delay||!c.options.delay.hide)return c.hide();c.hoverState="out",this.timeout=setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide)},show:function(){var a,b,c,d,e,f,g;if(this.hasContent()&&this.enabled){a=this.tip(),this.setContent(),this.options.animation&&a.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,a[0],this.$element[0]):this.options.placement,b=/in/.test(f),a.remove().css({top:0,left:0,display:"block"}).appendTo(b?this.$element:document.body),c=this.getPosition(b),d=a[0].offsetWidth,e=a[0].offsetHeight;switch(b?f.split(" ")[1]:f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}a.css(g).addClass(f).addClass("in")}},setContent:function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},hide:function(){function d(){var b=setTimeout(function(){c.off(a.support.transition.end).remove()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.remove()})}var b=this,c=this.tip();return c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d():c.remove(),this},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(b){return a.extend({},b?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}},a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover",title:"",delay:0,html:!0}}(window.jQuery),!function(a){var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content > *")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-content")||(typeof c.content=="function"?c.content.call(b[0]):c.content),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}}),a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(a){var b=function(b,c){this.options=a.extend({},a.fn.affix.defaults,c),this.$window=a(window).on("scroll.affix.data-api",a.proxy(this.checkPosition,this)),this.$element=a(b),this.checkPosition()};b.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var b=a(document).height(),c=this.$window.scrollTop(),d=this.$element.offset(),e=this.options.offset,f=e.bottom,g=e.top,h="affix affix-top affix-bottom",i;typeof e!="object"&&(f=g=e),typeof g=="function"&&(g=e.top()),typeof f=="function"&&(f=e.bottom()),i=this.unpin!=null&&c+this.unpin<=d.top?!1:f!=null&&d.top+this.$element.height()>=b-f?"bottom":g!=null&&c<=g?"top":!1;if(this.affixed===i)return;this.affixed=i,this.unpin=i=="bottom"?d.top-c:null,this.$element.removeClass(h).addClass("affix"+(i?"-"+i:""))},a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("affix"),f=typeof c=="object"&&c;e||d.data("affix",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.defaults={offset:0},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery),!function(a){var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger(b=a.Event("close"));if(b.isDefaultPrevented())return;e.removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})})}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b,c,d,e;if(this.transitioning)return;b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find("> .accordion-group > .in");if(d&&d.length){e=d.data("collapse");if(e&&e.transitioning)return;d.collapse("hide"),e||d.data("collapse",null)}this.$element[b](0),this.transition("addClass",a.Event("show"),"shown"),a.support.transition&&this.$element[b](this.$element[0][c])},hide:function(){var b;if(this.transitioning)return;b=this.dimension(),this.reset(this.$element[b]()),this.transition("removeClass",a.Event("hide"),"hidden"),this.$element[b](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a!==null?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c.type=="show"&&e.reset(),e.transitioning=0,e.$element.trigger(d)};this.$element.trigger(c);if(c.isDefaultPrevented())return;this.transitioning=1,this.$element[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();c[a(e).hasClass("in")?"addClass":"removeClass"]("collapsed"),a(e).collapse(f)})})}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.options=c,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(b){return b||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},to:function(b){var c=this.$element.find(".item.active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this,j=a.Event("slide",{relatedTarget:e[0]});this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();if(e.hasClass("active"))return;if(a.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(j);if(j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})}else{this.$element.trigger(j);if(j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=a.extend({},a.fn.carousel.defaults,typeof c=="object"&&c),g=typeof c=="string"?c:f.slide;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):g?e[g]():f.interval&&e.cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.$menu=a(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(a)).change(),this.hide()},updater:function(a){return a},show:function(){var b=a.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:b.top+b.height,left:b.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(c=a.isFunction(this.source)?this.source(this.query,a.proxy(this.process,this)):this.source,c?this.process(c):this)},process:function(b){var c=this;return b=a.grep(b,function(a){return c.matcher(a)}),b=this.sorter(b),b.length?this.render(b.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(a){return~a.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){var b=[],c=[],d=[],e;while(e=a.shift())e.toLowerCase().indexOf(this.query.toLowerCase())?~e.indexOf(this.query)?c.push(e):d.push(e):b.push(e);return b.concat(c,d)},highlighter:function(a){var b=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return a.replace(new RegExp("("+b+")","ig"),function(a,b){return"<strong>"+b+"</strong>"})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),(a.browser.chrome||a.browser.webkit||a.browser.msie)&&this.$element.on("keydown",a.proxy(this.keydown,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this))},move:function(a){if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:a.preventDefault(),this.prev();break;case 40:a.preventDefault(),this.next()}a.stopPropagation()},keydown:function(b){this.suppressKeyPressRepeat=!~a.inArray(b.keyCode,[40,38,9,13,27]),this.move(b)},keypress:function(a){if(this.suppressKeyPressRepeat)return;this.move(a)},keyup:function(a){switch(a.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},blur:function(a){var b=this;setTimeout(function(){b.hide()},150)},click:function(a){a.stopPropagation(),a.preventDefault(),this.select()},mouseenter:function(b){this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")}},a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},a.fn.typeahead.Constructor=b,a(function(){a("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;b.preventDefault(),c.typeahead(c.data())})})}(window.jQuery)
@@ -0,0 +1,677 @@
1
+ // jquery.pjax.js
2
+ // copyright chris wanstrath
3
+ // https://github.com/defunkt/jquery-pjax
4
+
5
+ (function($){
6
+
7
+ // When called on a link, fetches the href with ajax into the
8
+ // container specified as the first parameter or with the data-pjax
9
+ // attribute on the link itself.
10
+ //
11
+ // Tries to make sure the back button and ctrl+click work the way
12
+ // you'd expect.
13
+ //
14
+ // Accepts a jQuery ajax options object that may include these
15
+ // pjax specific options:
16
+ //
17
+ // container - Where to stick the response body. Usually a String selector.
18
+ // $(container).html(xhr.responseBody)
19
+ // push - Whether to pushState the URL. Defaults to true (of course).
20
+ // replace - Want to use replaceState instead? That's cool.
21
+ //
22
+ // For convenience the first parameter can be either the container or
23
+ // the options object.
24
+ //
25
+ // Returns the jQuery object
26
+ $.fn.pjax = function( container, options ) {
27
+ return this.on('click.pjax', function(event){
28
+ handleClick(event, container, options)
29
+ })
30
+ }
31
+
32
+ // Public: pjax on click handler
33
+ //
34
+ // Exported as $.pjax.click.
35
+ //
36
+ // event - "click" jQuery.Event
37
+ // options - pjax options
38
+ //
39
+ // Examples
40
+ //
41
+ // $('a').live('click', $.pjax.click)
42
+ // // is the same as
43
+ // $('a').pjax()
44
+ //
45
+ // $(document).on('click', 'a', function(event) {
46
+ // var container = $(this).closest('[data-pjax-container]')
47
+ // return $.pjax.click(event, container)
48
+ // })
49
+ //
50
+ // Returns false if pjax runs, otherwise nothing.
51
+ function handleClick(event, container, options) {
52
+ options = optionsFor(container, options)
53
+
54
+ var link = event.currentTarget
55
+
56
+ if (link.tagName.toUpperCase() !== 'A')
57
+ throw "$.fn.pjax or $.pjax.click requires an anchor element"
58
+
59
+ // Middle click, cmd click, and ctrl click should open
60
+ // links in a new tab as normal.
61
+ if ( event.which > 1 || event.metaKey || event.ctrlKey )
62
+ return
63
+
64
+ // Ignore cross origin links
65
+ if ( location.protocol !== link.protocol || location.host !== link.host )
66
+ return
67
+
68
+ // Ignore anchors on the same page
69
+ if (link.hash && link.href.replace(link.hash, '') ===
70
+ location.href.replace(location.hash, ''))
71
+ return
72
+
73
+ // Ignore empty anchor "foo.html#"
74
+ if (link.href === location.href + '#')
75
+ return
76
+
77
+ var defaults = {
78
+ url: link.href,
79
+ container: $(link).attr('data-pjax'),
80
+ target: link,
81
+ clickedElement: $(link), // DEPRECATED: use target
82
+ fragment: null
83
+ }
84
+
85
+ $.pjax($.extend({}, defaults, options))
86
+
87
+ event.preventDefault()
88
+ }
89
+
90
+
91
+ // Loads a URL with ajax, puts the response body inside a container,
92
+ // then pushState()'s the loaded URL.
93
+ //
94
+ // Works just like $.ajax in that it accepts a jQuery ajax
95
+ // settings object (with keys like url, type, data, etc).
96
+ //
97
+ // Accepts these extra keys:
98
+ //
99
+ // container - Where to stick the response body.
100
+ // $(container).html(xhr.responseBody)
101
+ // push - Whether to pushState the URL. Defaults to true (of course).
102
+ // replace - Want to use replaceState instead? That's cool.
103
+ //
104
+ // Use it just like $.ajax:
105
+ //
106
+ // var xhr = $.pjax({ url: this.href, container: '#main' })
107
+ // console.log( xhr.readyState )
108
+ //
109
+ // Returns whatever $.ajax returns.
110
+ var pjax = $.pjax = function( options ) {
111
+ options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
112
+
113
+ if ($.isFunction(options.url)) {
114
+ options.url = options.url()
115
+ }
116
+
117
+ var target = options.target
118
+
119
+ // DEPRECATED: use options.target
120
+ if (!target && options.clickedElement) target = options.clickedElement[0]
121
+
122
+ var hash = parseURL(options.url).hash
123
+
124
+ // DEPRECATED: Save references to original event callbacks. However,
125
+ // listening for custom pjax:* events is prefered.
126
+ var oldBeforeSend = options.beforeSend,
127
+ oldComplete = options.complete,
128
+ oldSuccess = options.success,
129
+ oldError = options.error
130
+
131
+ var context = options.context = findContainerFor(options.container)
132
+
133
+ // We want the browser to maintain two separate internal caches: one
134
+ // for pjax'd partial page loads and one for normal page loads.
135
+ // Without adding this secret parameter, some browsers will often
136
+ // confuse the two.
137
+ if (!options.data) options.data = {}
138
+ options.data._pjax = context.selector
139
+
140
+ function fire(type, args) {
141
+ var event = $.Event(type, { relatedTarget: target })
142
+ context.trigger(event, args)
143
+ return !event.isDefaultPrevented()
144
+ }
145
+
146
+ var timeoutTimer
147
+
148
+ options.beforeSend = function(xhr, settings) {
149
+ if (settings.timeout > 0) {
150
+ timeoutTimer = setTimeout(function() {
151
+ if (fire('pjax:timeout', [xhr, options]))
152
+ xhr.abort('timeout')
153
+ }, settings.timeout)
154
+
155
+ // Clear timeout setting so jquerys internal timeout isn't invoked
156
+ settings.timeout = 0
157
+ }
158
+
159
+ xhr.setRequestHeader('X-PJAX', 'true')
160
+ xhr.setRequestHeader('X-PJAX-Container', context.selector)
161
+
162
+ var result
163
+
164
+ // DEPRECATED: Invoke original `beforeSend` handler
165
+ if (oldBeforeSend) {
166
+ result = oldBeforeSend.apply(this, arguments)
167
+ if (result === false) return false
168
+ }
169
+
170
+ if (!fire('pjax:beforeSend', [xhr, settings]))
171
+ return false
172
+
173
+ options.requestUrl = parseURL(settings.url).href
174
+ }
175
+
176
+ options.complete = function(xhr, textStatus) {
177
+ if (timeoutTimer)
178
+ clearTimeout(timeoutTimer)
179
+
180
+ // DEPRECATED: Invoke original `complete` handler
181
+ if (oldComplete) oldComplete.apply(this, arguments)
182
+
183
+ fire('pjax:complete', [xhr, textStatus, options])
184
+
185
+ fire('pjax:end', [xhr, options])
186
+ // end.pjax is deprecated
187
+ fire('end.pjax', [xhr, options])
188
+ }
189
+
190
+ options.error = function(xhr, textStatus, errorThrown) {
191
+ var container = extractContainer("", xhr, options)
192
+
193
+ // DEPRECATED: Invoke original `error` handler
194
+ if (oldError) oldError.apply(this, arguments)
195
+
196
+ var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])
197
+ if (textStatus !== 'abort' && allowed)
198
+ window.location = container.url
199
+ }
200
+
201
+ options.success = function(data, status, xhr) {
202
+ var container = extractContainer(data, xhr, options)
203
+
204
+ if (!container.contents) {
205
+ window.location = container.url
206
+ return
207
+ }
208
+
209
+ pjax.state = {
210
+ id: options.id || uniqueId(),
211
+ url: container.url,
212
+ title: container.title,
213
+ container: context.selector,
214
+ fragment: options.fragment,
215
+ timeout: options.timeout
216
+ }
217
+
218
+ if (options.push || options.replace) {
219
+ window.history.replaceState(pjax.state, container.title, container.url)
220
+ }
221
+
222
+ if (container.title) document.title = container.title
223
+ context.html(container.contents)
224
+
225
+ // Scroll to top by default
226
+ if (typeof options.scrollTo === 'number')
227
+ $(window).scrollTop(options.scrollTo)
228
+
229
+ // Google Analytics support
230
+ if ( (options.replace || options.push) && window._gaq )
231
+ _gaq.push(['_trackPageview'])
232
+
233
+ // If the URL has a hash in it, make sure the browser
234
+ // knows to navigate to the hash.
235
+ if ( hash !== '' ) {
236
+ // Avoid using simple hash set here. Will add another history
237
+ // entry. Replace the url with replaceState and scroll to target
238
+ // by hand.
239
+ //
240
+ // window.location.hash = hash
241
+ var url = parseURL(container.url)
242
+ url.hash = hash
243
+
244
+ pjax.state.url = url.href
245
+ window.history.replaceState(pjax.state, container.title, url.href)
246
+
247
+ var target = $(url.hash)
248
+ if (target.length) $(window).scrollTop(target.offset().top)
249
+ }
250
+
251
+ // DEPRECATED: Invoke original `success` handler
252
+ if (oldSuccess) oldSuccess.apply(this, arguments)
253
+
254
+ fire('pjax:success', [data, status, xhr, options])
255
+ }
256
+
257
+
258
+ // Initialize pjax.state for the initial page load. Assume we're
259
+ // using the container and options of the link we're loading for the
260
+ // back button to the initial page. This ensures good back button
261
+ // behavior.
262
+ if (!pjax.state) {
263
+ pjax.state = {
264
+ id: uniqueId(),
265
+ url: window.location.href,
266
+ title: document.title,
267
+ container: context.selector,
268
+ fragment: options.fragment,
269
+ timeout: options.timeout
270
+ }
271
+ window.history.replaceState(pjax.state, document.title)
272
+ }
273
+
274
+ // Cancel the current request if we're already pjaxing
275
+ var xhr = pjax.xhr
276
+ if ( xhr && xhr.readyState < 4) {
277
+ xhr.onreadystatechange = $.noop
278
+ xhr.abort()
279
+ }
280
+
281
+ pjax.options = options
282
+ var xhr = pjax.xhr = $.ajax(options)
283
+
284
+ if (xhr.readyState > 0) {
285
+ // pjax event is deprecated
286
+ $(document).trigger('pjax', [xhr, options])
287
+
288
+ if (options.push && !options.replace) {
289
+ // Cache current container element before replacing it
290
+ cachePush(pjax.state.id, context.clone().contents())
291
+
292
+ window.history.pushState(null, "", options.url)
293
+ }
294
+
295
+ fire('pjax:start', [xhr, options])
296
+ // start.pjax is deprecated
297
+ fire('start.pjax', [xhr, options])
298
+
299
+ fire('pjax:send', [xhr, options])
300
+ }
301
+
302
+ return pjax.xhr
303
+ }
304
+
305
+
306
+ // Internal: Generate unique id for state object.
307
+ //
308
+ // Use a timestamp instead of a counter since ids should still be
309
+ // unique across page loads.
310
+ //
311
+ // Returns Number.
312
+ function uniqueId() {
313
+ return (new Date).getTime()
314
+ }
315
+
316
+ // Internal: Strips _pjax param from url
317
+ //
318
+ // url - String
319
+ //
320
+ // Returns String.
321
+ function stripPjaxParam(url) {
322
+ return url
323
+ .replace(/\?_pjax=[^&]+&?/, '?')
324
+ .replace(/_pjax=[^&]+&?/, '')
325
+ .replace(/[\?&]$/, '')
326
+ }
327
+
328
+ // Internal: Parse URL components and returns a Locationish object.
329
+ //
330
+ // url - String URL
331
+ //
332
+ // Returns HTMLAnchorElement that acts like Location.
333
+ function parseURL(url) {
334
+ var a = document.createElement('a')
335
+ a.href = url
336
+ return a
337
+ }
338
+
339
+ // Internal: Build options Object for arguments.
340
+ //
341
+ // For convenience the first parameter can be either the container or
342
+ // the options object.
343
+ //
344
+ // Examples
345
+ //
346
+ // optionsFor('#container')
347
+ // // => {container: '#container'}
348
+ //
349
+ // optionsFor('#container', {push: true})
350
+ // // => {container: '#container', push: true}
351
+ //
352
+ // optionsFor({container: '#container', push: true})
353
+ // // => {container: '#container', push: true}
354
+ //
355
+ // Returns options Object.
356
+ function optionsFor(container, options) {
357
+ // Both container and options
358
+ if ( container && options )
359
+ options.container = container
360
+
361
+ // First argument is options Object
362
+ else if ( $.isPlainObject(container) )
363
+ options = container
364
+
365
+ // Only container
366
+ else
367
+ options = {container: container}
368
+
369
+ // Find and validate container
370
+ if (options.container)
371
+ options.container = findContainerFor(options.container)
372
+
373
+ return options
374
+ }
375
+
376
+ // Internal: Find container element for a variety of inputs.
377
+ //
378
+ // Because we can't persist elements using the history API, we must be
379
+ // able to find a String selector that will consistently find the Element.
380
+ //
381
+ // container - A selector String, jQuery object, or DOM Element.
382
+ //
383
+ // Returns a jQuery object whose context is `document` and has a selector.
384
+ function findContainerFor(container) {
385
+ container = $(container)
386
+
387
+ if ( !container.length ) {
388
+ throw "no pjax container for " + container.selector
389
+ } else if ( container.selector !== '' && container.context === document ) {
390
+ return container
391
+ } else if ( container.attr('id') ) {
392
+ return $('#' + container.attr('id'))
393
+ } else {
394
+ throw "cant get selector for pjax container!"
395
+ }
396
+ }
397
+
398
+ // Internal: Filter and find all elements matching the selector.
399
+ //
400
+ // Where $.fn.find only matches descendants, findAll will test all the
401
+ // top level elements in the jQuery object as well.
402
+ //
403
+ // elems - jQuery object of Elements
404
+ // selector - String selector to match
405
+ //
406
+ // Returns a jQuery object.
407
+ function findAll(elems, selector) {
408
+ var results = $()
409
+ elems.each(function() {
410
+ if ($(this).is(selector))
411
+ results = results.add(this)
412
+ results = results.add(selector, this)
413
+ })
414
+ return results
415
+ }
416
+
417
+ // Internal: Extracts container and metadata from response.
418
+ //
419
+ // 1. Extracts X-PJAX-URL header if set
420
+ // 2. Extracts inline <title> tags
421
+ // 3. Builds response Element and extracts fragment if set
422
+ //
423
+ // data - String response data
424
+ // xhr - XHR response
425
+ // options - pjax options Object
426
+ //
427
+ // Returns an Object with url, title, and contents keys.
428
+ function extractContainer(data, xhr, options) {
429
+ var obj = {}
430
+
431
+ // Prefer X-PJAX-URL header if it was set, otherwise fallback to
432
+ // using the original requested url.
433
+ obj.url = stripPjaxParam(xhr.getResponseHeader('X-PJAX-URL') || options.requestUrl)
434
+
435
+ // Attempt to parse response html into elements
436
+ var $data = $(data)
437
+
438
+ // If response data is empty, return fast
439
+ if ($data.length === 0)
440
+ return obj
441
+
442
+ // If there's a <title> tag in the response, use it as
443
+ // the page's title.
444
+ obj.title = findAll($data, 'title').last().text()
445
+
446
+ if (options.fragment) {
447
+ // If they specified a fragment, look for it in the response
448
+ // and pull it out.
449
+ var $fragment = findAll($data, options.fragment).first()
450
+
451
+ if ($fragment.length) {
452
+ obj.contents = $fragment.contents()
453
+
454
+ // If there's no title, look for data-title and title attributes
455
+ // on the fragment
456
+ if (!obj.title)
457
+ obj.title = $fragment.attr('title') || $fragment.data('title')
458
+ }
459
+
460
+ } else if (!/<html/i.test(data)) {
461
+ obj.contents = $data
462
+ }
463
+
464
+ // Clean up any <title> tags
465
+ if (obj.contents) {
466
+ // Remove any parent title elements
467
+ obj.contents = obj.contents.not('title')
468
+
469
+ // Then scrub any titles from their descendents
470
+ obj.contents.find('title').remove()
471
+ }
472
+
473
+ // Trim any whitespace off the title
474
+ if (obj.title) obj.title = $.trim(obj.title)
475
+
476
+ return obj
477
+ }
478
+
479
+ // Public: Reload current page with pjax.
480
+ //
481
+ // Returns whatever $.pjax returns.
482
+ pjax.reload = function(container, options) {
483
+ var defaults = {
484
+ url: window.location.href,
485
+ push: false,
486
+ replace: true,
487
+ scrollTo: false
488
+ }
489
+
490
+ return $.pjax($.extend(defaults, optionsFor(container, options)))
491
+ }
492
+
493
+
494
+ pjax.defaults = {
495
+ timeout: 650,
496
+ push: true,
497
+ replace: false,
498
+ type: 'GET',
499
+ dataType: 'html',
500
+ scrollTo: 0,
501
+ maxCacheLength: 20
502
+ }
503
+
504
+ // Internal: History DOM caching class.
505
+ var cacheMapping = {}
506
+ var cacheForwardStack = []
507
+ var cacheBackStack = []
508
+ // Push previous state id and container contents into the history
509
+ // cache. Should be called in conjunction with `pushState` to save the
510
+ // previous container contents.
511
+ //
512
+ // id - State ID Number
513
+ // value - DOM Element to cache
514
+ //
515
+ // Returns nothing.
516
+ function cachePush(id, value) {
517
+ cacheMapping[id] = value
518
+ cacheBackStack.push(id)
519
+
520
+ // Remove all entires in forward history stack after pushing
521
+ // a new page.
522
+ while (cacheForwardStack.length)
523
+ delete cacheMapping[cacheForwardStack.shift()]
524
+
525
+ // Trim back history stack to max cache length.
526
+ while (cacheBackStack.length > pjax.defaults.maxCacheLength)
527
+ delete cacheMapping[cacheBackStack.shift()]
528
+ }
529
+ // Shifts cache from directional history cache. Should be
530
+ // called on `popstate` with the previous state id and container
531
+ // contents.
532
+ //
533
+ // direction - "forward" or "back" String
534
+ // id - State ID Number
535
+ // value - DOM Element to cache
536
+ //
537
+ // Returns nothing.
538
+ function cachePop(direction, id, value) {
539
+ var pushStack, popStack
540
+ cacheMapping[id] = value
541
+
542
+ if (direction === 'forward') {
543
+ pushStack = cacheBackStack
544
+ popStack = cacheForwardStack
545
+ } else {
546
+ pushStack = cacheForwardStack
547
+ popStack = cacheBackStack
548
+ }
549
+
550
+ pushStack.push(id)
551
+ if (id = popStack.pop())
552
+ delete cacheMapping[id]
553
+ }
554
+
555
+
556
+ // Export $.pjax.click
557
+ pjax.click = handleClick
558
+
559
+
560
+ // popstate handler takes care of the back and forward buttons
561
+ //
562
+ // You probably shouldn't use pjax on pages with other pushState
563
+ // stuff yet.
564
+ $(window).bind('popstate', function(event){
565
+ var state = event.state
566
+
567
+ if (state && state.container) {
568
+ var container = $(state.container)
569
+ if (container.length) {
570
+ var contents = cacheMapping[state.id]
571
+
572
+ if (pjax.state) {
573
+ // Since state ids always increase, we can deduce the history
574
+ // direction from the previous state.
575
+ var direction = pjax.state.id < state.id ? 'forward' : 'back'
576
+
577
+ // Cache current container before replacement and inform the
578
+ // cache which direction the history shifted.
579
+ cachePop(direction, pjax.state.id, container.clone().contents())
580
+ }
581
+
582
+ var popstateEvent = $.Event('pjax:popstate', {
583
+ state: state,
584
+ direction: direction
585
+ })
586
+ container.trigger(popstateEvent)
587
+
588
+ var options = {
589
+ id: state.id,
590
+ url: state.url,
591
+ container: container,
592
+ push: false,
593
+ fragment: state.fragment,
594
+ timeout: state.timeout,
595
+ scrollTo: false
596
+ }
597
+
598
+ if (contents) {
599
+ // pjax event is deprecated
600
+ $(document).trigger('pjax', [null, options])
601
+ container.trigger('pjax:start', [null, options])
602
+ // end.pjax event is deprecated
603
+ container.trigger('start.pjax', [null, options])
604
+
605
+ if (state.title) document.title = state.title
606
+ container.html(contents)
607
+ pjax.state = state
608
+
609
+ container.trigger('pjax:end', [null, options])
610
+ // end.pjax event is deprecated
611
+ container.trigger('end.pjax', [null, options])
612
+ } else {
613
+ $.pjax(options)
614
+ }
615
+
616
+ // Force reflow/relayout before the browser tries to restore the
617
+ // scroll position.
618
+ container[0].offsetHeight
619
+ } else {
620
+ window.location = location.href
621
+ }
622
+ }
623
+ })
624
+
625
+
626
+ // Add the state property to jQuery's event object so we can use it in
627
+ // $(window).bind('popstate')
628
+ if ( $.inArray('state', $.event.props) < 0 )
629
+ $.event.props.push('state')
630
+
631
+
632
+ // Is pjax supported by this browser?
633
+ $.support.pjax =
634
+ window.history && window.history.pushState && window.history.replaceState
635
+ // pushState isn't reliable on iOS until 5.
636
+ && !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/)
637
+
638
+ // Fall back to normalcy for older browsers.
639
+ if ( !$.support.pjax ) {
640
+ $.pjax = function( options ) {
641
+ var url = $.isFunction(options.url) ? options.url() : options.url,
642
+ method = options.type ? options.type.toUpperCase() : 'GET'
643
+
644
+ var form = $('<form>', {
645
+ method: method === 'GET' ? 'GET' : 'POST',
646
+ action: url,
647
+ style: 'display:none'
648
+ })
649
+
650
+ if (method !== 'GET' && method !== 'POST') {
651
+ form.append($('<input>', {
652
+ type: 'hidden',
653
+ name: '_method',
654
+ value: method.toLowerCase()
655
+ }))
656
+ }
657
+
658
+ var data = options.data
659
+ if (typeof data === 'string') {
660
+ $.each(data.split('&'), function(index, value) {
661
+ var pair = value.split('=')
662
+ form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
663
+ })
664
+ } else if (typeof data === 'object') {
665
+ for (key in data)
666
+ form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
667
+ }
668
+
669
+ $(document.body).append(form)
670
+ form.submit()
671
+ }
672
+ $.pjax.click = $.noop
673
+ $.pjax.reload = window.location.reload
674
+ $.fn.pjax = function() { return this }
675
+ }
676
+
677
+ })(jQuery);