hell 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.document +5 -0
  2. data/Gemfile +14 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.markdown +108 -0
  5. data/Rakefile +38 -0
  6. data/VERSION +1 -0
  7. data/bin/hell +12 -0
  8. data/lib/hell.rb +0 -0
  9. data/lib/hell/app.rb +128 -0
  10. data/lib/hell/config.ru +4 -0
  11. data/lib/hell/hell.rb +0 -0
  12. data/lib/hell/lib/helpers.rb +119 -0
  13. data/lib/hell/lib/monkey_patch.rb +79 -0
  14. data/lib/hell/public/assets/css/bootstrap-responsive.css +1088 -0
  15. data/lib/hell/public/assets/css/bootstrap-responsive.min.css +9 -0
  16. data/lib/hell/public/assets/css/bootstrap.css +5893 -0
  17. data/lib/hell/public/assets/css/bootstrap.min.css +9 -0
  18. data/lib/hell/public/assets/css/hell.css +158 -0
  19. data/lib/hell/public/assets/ico/favicon.ico +0 -0
  20. data/lib/hell/public/assets/ico/favicon.png +0 -0
  21. data/lib/hell/public/assets/img/glyphicons-halflings-white.png +0 -0
  22. data/lib/hell/public/assets/img/glyphicons-halflings.png +0 -0
  23. data/lib/hell/public/assets/js/backbone-localstorage.js +84 -0
  24. data/lib/hell/public/assets/js/backbone.js +1431 -0
  25. data/lib/hell/public/assets/js/backbone.min.js +38 -0
  26. data/lib/hell/public/assets/js/bankersbox.js +768 -0
  27. data/lib/hell/public/assets/js/bootstrap.growl.js +2 -0
  28. data/lib/hell/public/assets/js/bootstrap.js +2025 -0
  29. data/lib/hell/public/assets/js/bootstrap.min.js +6 -0
  30. data/lib/hell/public/assets/js/hashchange.min.js +9 -0
  31. data/lib/hell/public/assets/js/hell.js +444 -0
  32. data/lib/hell/public/assets/js/jquery.min.js +4 -0
  33. data/lib/hell/public/assets/js/timeago.js +152 -0
  34. data/lib/hell/public/assets/js/underscore.min.js +1 -0
  35. data/lib/hell/views/index.erb +146 -0
  36. data/test/helper.rb +18 -0
  37. data/test/test_hell.rb +7 -0
  38. metadata +201 -0
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Bootstrap.js by @fat & @mdo
3
+ * Copyright 2012 Twitter, Inc.
4
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
5
+ */
6
+ !function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()},e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")},e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=n,this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},to:function(t){var n=this.$element.find(".item.active"),r=n.parent().children(),i=r.index(n),s=this;if(t>r.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){s.to(t)}):i==t?this.pause().cycle():this.slide(t>i?"next":"prev",e(r[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.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(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0]});if(i.hasClass("active"))return;if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}},e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e(document).on("click.carousel.data-api","[data-slide]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data());i.carousel(s),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning)return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning)return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=typeof n=="object"&&n;i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;return n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=e(n),r.length||(r=t.parent()),r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||(s.toggleClass("open"),n.focus()),!1},keydown:function(t){var n,r,s,o,u,a;if(!/(38|40|27)/.test(t.keyCode))return;n=e(this),t.preventDefault(),t.stopPropagation();if(n.is(".disabled, :disabled"))return;o=i(n),u=o.hasClass("open");if(!u||u&&t.keyCode==27)return n.click();r=e("[role=menu] li:not(.divider) a",o);if(!r.length)return;a=r.index(r.filter(":focus")),t.keyCode==38&&a>0&&a--,t.keyCode==40&&a<r.length-1&&a++,~a||(a=0),r.eq(a).focus()}},e.fn.dropdown=function(t){return this.each(function(){var r=e(this),i=r.data("dropdown");i||r.data("dropdown",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.dropdown.Constructor=n,e(document).on("click.dropdown.data-api touchstart.dropdown.data-api",r).on("click.dropdown touchstart.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("click.dropdown.data-api touchstart.dropdown.data-api",t,n.prototype.toggle).on("keydown.dropdown.data-api touchstart.dropdown.data-api",t+", [role=menu]",n.prototype.keydown)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=n,this.$element=e(t).delegate('[data-dismiss="modal"]',"click.dismiss.modal",e.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};t.prototype={constructor:t,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var t=this,n=e.Event("show");this.$element.trigger(n);if(this.isShown||n.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var n=e.support.transition&&t.$element.hasClass("fade");t.$element.parent().length||t.$element.appendTo(document.body),t.$element.show(),n&&t.$element[0].offsetWidth,t.$element.addClass("in").attr("aria-hidden",!1),t.enforceFocus(),n?t.$element.one(e.support.transition.end,function(){t.$element.focus().trigger("shown")}):t.$element.focus().trigger("shown")})},hide:function(t){t&&t.preventDefault();var n=this;t=e.Event("hide"),this.$element.trigger(t);if(!this.isShown||t.isDefaultPrevented())return;this.isShown=!1,this.escape(),e(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),e.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var t=this;e(document).on("focusin.modal",function(e){t.$element[0]!==e.target&&!t.$element.has(e.target).length&&t.$element.focus()})},escape:function(){var e=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(t){t.which==27&&e.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var t=this,n=setTimeout(function(){t.$element.off(e.support.transition.end),t.hideModal()},500);this.$element.one(e.support.transition.end,function(){clearTimeout(n),t.hideModal()})},hideModal:function(e){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(t){var n=this,r=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&r;this.$backdrop=e('<div class="modal-backdrop '+r+'" />').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,e.proxy(this.removeBackdrop,this)):this.removeBackdrop()):t&&t()}},e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,this.options.trigger=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):this.options.trigger!="manual"&&(i=this.options.trigger=="hover"?"mouseenter":"focus",s=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this))),this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,t,this.$element.data()),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);if(!n.options.delay||!n.options.delay.show)return n.show();clearTimeout(this.timeout),n.hoverState="in",this.timeout=setTimeout(function(){n.hoverState=="in"&&n.show()},n.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var e,t,n,r,i,s,o;if(this.hasContent()&&this.enabled){e=this.tip(),this.setContent(),this.options.animation&&e.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,e[0],this.$element[0]):this.options.placement,t=/in/.test(s),e.detach().css({top:0,left:0,display:"block"}).insertAfter(this.$element),n=this.getPosition(t),r=e[0].offsetWidth,i=e[0].offsetHeight;switch(t?s.split(" ")[1]:s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}e.offset(o).addClass(s).addClass("in")}},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function r(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip();return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?r():n.detach(),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(t){return e.extend({},t?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(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(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);n[n.tip().hasClass("in")?"hide":"show"]()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}},e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.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:!1}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content > *")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-content")||(typeof n.content=="function"?n.content.call(t[0]):n.content),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}}),e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.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(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var t=e(this),n=t.data("target")||t.attr("href"),r=/^#\w/.test(n)&&e(n);return r&&r.length&&[[r.position().top,n]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}},e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}},e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),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=e(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:t.top+t.height,left:t.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length<this.options.minLength?this.shown?this.hide():this:(n=e.isFunction(this.source)?this.source(this.query,e.proxy(this.process,this)):this.source,n?this.process(n):this)},process:function(t){var n=this;return t=e.grep(t,function(e){return n.matcher(e)}),t=this.sorter(t),t.length?this.render(t.slice(0,this.options.items)).show():this.shown?this.hide():this},matcher:function(e){return~e.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(e){var t=[],n=[],r=[],i;while(i=e.shift())i.toLowerCase().indexOf(this.query.toLowerCase())?~i.indexOf(this.query)?n.push(i):r.push(i):t.push(i);return t.concat(n,r)},highlighter:function(e){var t=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&");return e.replace(new RegExp("("+t+")","ig"),function(e,t){return"<strong>"+t+"</strong>"})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=!~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},blur:function(e){var t=this;setTimeout(function(){t.hide()},150)},click:function(e){e.stopPropagation(),e.preventDefault(),this.select()},mouseenter:function(t){this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")}},e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1},e.fn.typeahead.Constructor=t,e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;t.preventDefault(),n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))},e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);
@@ -0,0 +1,9 @@
1
+ /*
2
+ * jQuery hashchange event - v1.3 - 7/21/2010
3
+ * http://benalman.com/projects/jquery-hashchange-plugin/
4
+ *
5
+ * Copyright (c) 2010 "Cowboy" Ben Alman
6
+ * Dual licensed under the MIT and GPL licenses.
7
+ * http://benalman.com/about/license/
8
+ */
9
+ (function($,e,b){var c="hashchange",h=document,f,g=$.event.special,i=h.documentMode,d="on"+c in e&&(i===b||i>7);function a(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}$.fn[c]=function(j){return j?this.bind(c,j):this.trigger(c)};$.fn[c].delay=50;g[c]=$.extend(g[c],{setup:function(){if(d){return false}$(f.start)},teardown:function(){if(d){return false}$(f.stop)}});f=(function(){var j={},p,m=a(),k=function(q){return q},l=k,o=k;j.start=function(){p||n()};j.stop=function(){p&&clearTimeout(p);p=b};function n(){var r=a(),q=o(m);if(r!==m){l(m=r,q);$(e).trigger(c)}else{if(q!==m){location.href=location.href.replace(/#.*/,"")+q}}p=setTimeout(n,$.fn[c].delay)}$.browser.msie&&!d&&(function(){var q,r;j.start=function(){if(!q){r=$.fn[c].src;r=r&&r+a();q=$('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){r||l(a());n()}).attr("src",r||"javascript:0").insertAfter("body")[0].contentWindow;h.onpropertychange=function(){try{if(event.propertyName==="title"){q.document.title=h.title}}catch(s){}}}};j.stop=k;o=function(){return a(q.location.href)};l=function(v,s){var u=q.document,t=$.fn[c].domain;if(v!==s){u.title=h.title;u.open();t&&u.write('<script>document.domain="'+t+'"<\/script>');u.close();q.location.hash=v}}})();return j})()})(jQuery,this);
@@ -0,0 +1,444 @@
1
+ $(function() {
2
+
3
+ var src = null;
4
+
5
+ var ex = _.extend(Backbone.Model.prototype, {
6
+
7
+ _errors: {},
8
+
9
+ // Convenience function to mass update properties
10
+ // unless they have a value
11
+ setUnless: function(attrs, options) {
12
+ var attr, val;
13
+ options = options || {};
14
+
15
+ attrs = attrs && attrs.length ? attrs : this.set_unless;
16
+
17
+ for (attr in attrs) {
18
+ if (!this.has(attr)) {
19
+ val = attrs[attr];
20
+ this.set(attr, val, options);
21
+ }
22
+ }
23
+ },
24
+
25
+ clearErrors: function() {
26
+ this._errors = {};
27
+ },
28
+
29
+ // Return errors for a field or for the model
30
+ getErrors: function(field) {
31
+ if (!field) return this._errors;
32
+ if (_.has(this._errors, field)) return this._errors[field];
33
+ return undefined;
34
+ },
35
+
36
+ // Set an error message for a given field
37
+ setError: function(field, error) {
38
+ this._errors = this._errors || {};
39
+ this._errors[field] = this._errors[field] || [];
40
+ this._errors[field].push(error);
41
+ },
42
+
43
+ // Returns whether the current validates call has produced errors
44
+ hasErrors: function() {
45
+ this._errors = this._errors || {};
46
+ var errors = _.reject(this._errors, function(v, k) { return v.length === 0; });
47
+ return errors.length > 0;
48
+ },
49
+
50
+ // Custom validation rule
51
+ validates: function(attrs) {
52
+ // Do not require passing in the attributes being checked
53
+ attrs = attrs || this.attributes;
54
+
55
+ // Clear all errors before continuing
56
+ this.clearErrors();
57
+
58
+ // Run validation
59
+ this._validates(attrs);
60
+
61
+ // Trigger `errors` or `validates` events
62
+ var hasErrors = this.hasErrors();
63
+ this.trigger(hasErrors ? 'errors' : 'validates', this, this._errors);
64
+ return !hasErrors;
65
+ },
66
+
67
+ _validates: function(attrs) {
68
+ return true;
69
+ }
70
+
71
+ });
72
+
73
+ var Task = Backbone.Model.extend({
74
+
75
+ _status_badge_map: {
76
+ 'fail': 'important',
77
+ 'pending': 'warning',
78
+ 'sent': 'info',
79
+ 'success': 'success',
80
+ 'finished': 'inverse'
81
+ },
82
+
83
+ set_unless: {
84
+ 'timestamp': new Date().getTime(),
85
+ 'iso_time': new Date(new Date().getTime()).toISOString(),
86
+ 'status': 'pending'
87
+ },
88
+
89
+ defaults: function() {
90
+ return {
91
+ task_id: null,
92
+ name: null,
93
+ environment: null,
94
+ verbose: true,
95
+ timestamp: null,
96
+ status: null
97
+ };
98
+ },
99
+
100
+ initialize: function() {
101
+ this.setUnless({}, {silent: true});
102
+ },
103
+
104
+ execute: function() {
105
+ var that = this;
106
+ $('.task-output').empty();
107
+
108
+ HellApp.set_flash("Sending execution request", "info");
109
+ HellApp.toggle_overlay();
110
+ var jqxhr = $.ajax({
111
+ url: HellApp.www_base_dir + 'tasks/' + that.command() + '/background?verbose=' + that.get('verbose')
112
+ });
113
+
114
+ jqxhr.always(function(data) {
115
+ HellApp.close_sockets(src);
116
+ HellApp.toggle_overlay();
117
+ });
118
+
119
+ jqxhr.fail(function(data) {
120
+ HellApp.set_flash("Failure receiving server response");
121
+ that.setStatus("fail");
122
+ });
123
+
124
+ jqxhr.done(function(data) {
125
+ HellApp.set_flash("Received ajax response", "info");
126
+ that.set('task_id', data.task_id);
127
+ that.setStatus("sent");
128
+ that.save();
129
+
130
+ // Create new websocket
131
+ src = new EventSource(HellApp.www_base_dir + 'logs/' + that.get('task_id') + '/tail');
132
+
133
+ // Received the first response
134
+ src.addEventListener('start', function () {
135
+ HellApp.set_flash("Received the first response for " + that.get('name'), "success");
136
+ that.setStatus("started");
137
+ });
138
+
139
+ // Close the websocket when there are no more events
140
+ src.addEventListener('end', function () {
141
+ HellApp.set_flash("Received the last response " + that.get('name'), "info");
142
+ that.setStatus("finished");
143
+ HellApp.close_sockets(src);
144
+ });
145
+
146
+ // Append the messages to the .task-output div
147
+ src.onmessage = function(e) {
148
+ var message = jQuery.parseJSON(e.data);
149
+ $('.task-output').append(message.message.replace(/~+$/, ''));
150
+ $(".task-output").animate({scrollTop: $(".task-output").prop("scrollHeight")}, 1);
151
+ };
152
+ });
153
+ },
154
+
155
+ view: function() {
156
+ HellApp.set_flash("Viewing a past task", "info");
157
+
158
+ // Close all websockets
159
+ HellApp.close_sockets(src);
160
+ HellApp.toggle_overlay();
161
+ window.location.hash = "#run";
162
+
163
+ var jqxhr = $.ajax({url: HellApp.www_base_dir + 'logs/' + this.get('task_id') + '/view'});
164
+ jqxhr.always(function(data) { HellApp.toggle_overlay(); });
165
+ jqxhr.done(function(data) {
166
+ HellApp.set_flash("Received proper server response", "success");
167
+ $('.task-output').html(data.replace(/~+$/, ''));
168
+ $(".task-output").animate({scrollTop: $(".task-output").prop("scrollHeight")}, 1);
169
+ });
170
+ jqxhr.fail(function(data) { HellApp.set_flash("Failure receiving server response"); });
171
+ },
172
+
173
+ // Backbone's built-in validation only allows for a single error
174
+ // per save. Using this method, we can attach multiple error messages
175
+ // to a single field. This will not work for embedded documents
176
+ _validates: function(attrs) {
177
+ // Validate that there is a task
178
+ if (attrs.name === null || attrs.name.length === 0) {
179
+ this.setError('name', 'Please enter a capistrano task');
180
+ }
181
+
182
+ // Validate task name in available_commands
183
+ else if ($.inArray(attrs.name, HellApp.tasks) === -1) {
184
+ this.setError('name', 'Capistrano command not in your defined tasks');
185
+ }
186
+
187
+ // Validate that an environment is set
188
+ if (HellApp.require_env && attrs.environment === null || attrs.environment.length === 0) {
189
+ this.setError('environment', 'Environment required to run any tasks');
190
+ }
191
+
192
+ if (attrs.environment && attrs.environment.length > 0) {
193
+ if ($.inArray(attrs.environment, HellApp.environments) === -1) {
194
+ this.setError('environment', 'Invalid environment ' + attrs.environment + ' specified');
195
+ }
196
+ }
197
+ },
198
+
199
+ command: function() {
200
+ var cmd = [ this.get('environment') + ' ', this.get('name') ];
201
+ return _.reject(cmd, function(v) { return v.length === 0; }).join(' ');
202
+ },
203
+
204
+ cap_command: function() {
205
+ return [ 'cap', this.command() ].join(' ');
206
+ },
207
+
208
+ badge: function() {
209
+ return this._status_badge_map[this.get('status')];
210
+ },
211
+
212
+ toFullJSON: function() {
213
+ return _.extend(this.toJSON(), {
214
+ command: this.cap_command(),
215
+ badge: this.badge()
216
+ });
217
+ },
218
+
219
+ toRerunJSON: function() {
220
+ var omit = _.keys(this.set_unless);
221
+ omit.push("id");
222
+ return _.omit(this.toJSON(), omit);
223
+ },
224
+
225
+ setStatus: function(s) {
226
+ this.save({status: s});
227
+ return this;
228
+ }
229
+
230
+ });
231
+
232
+ var TaskView = Backbone.View.extend({
233
+
234
+ tagName: 'tr',
235
+
236
+ template: _.template($('#task-template').html()),
237
+
238
+ events: {
239
+ 'click .rerun' : 'rerun',
240
+ 'click .view' : 'view',
241
+ 'click .destroy' : 'destroy'
242
+ },
243
+
244
+ initialize: function() {
245
+ this.model.bind('change', this.render, this);
246
+ this.model.bind('destroy', this.remove, this);
247
+ this.model.bind('error', this.invalid, this);
248
+ return this;
249
+ },
250
+
251
+ render: function() {
252
+ this.$el.html(this.template(this.model.toFullJSON()));
253
+ this.$el.find('.status').html(this.model.get('status'));
254
+ this.$(".timeago small").timeago();
255
+ return this;
256
+ },
257
+
258
+ rerun: function() {
259
+ window.location.hash = "#run";
260
+ HellApp.form.trigger('task-form:add', this.model.toRerunJSON());
261
+ },
262
+
263
+ view: function() {
264
+ window.location.hash = "#run";
265
+ this.model.view();
266
+ },
267
+
268
+ destroy: function() {
269
+ this.model.destroy();
270
+ }
271
+
272
+ });
273
+
274
+ var TaskList = Backbone.Collection.extend({
275
+
276
+ model: Task,
277
+
278
+ localStorage: new Store('hell-tasks'),
279
+
280
+ initialize: function() {
281
+ this.bind('add', this.onTaskAdded, this);
282
+ },
283
+
284
+ onTaskAdded: function(task, collection, options) {
285
+ task.save();
286
+ task.execute();
287
+ },
288
+
289
+ comparator: function(task) {
290
+ return task.get('timestamp');
291
+ },
292
+
293
+ clear: function() {
294
+ var ids = this.pluck("id"),
295
+ that = this;
296
+
297
+ // We need to iterate over the ids because indexes
298
+ // change for models when iterating over the collection
299
+ // using _.each()
300
+ _.each(ids, function(id) { that.get(id).destroy(); });
301
+ },
302
+
303
+ addTask: function(data, options) {
304
+ var task = new Task();
305
+ task.on('errors', options.error);
306
+ task.on('validates', this.add, this);
307
+ task.set(data, {silent: true});
308
+ task.validates();
309
+ return task;
310
+ }
311
+
312
+ });
313
+
314
+ window.Tasks = new TaskList();
315
+
316
+ var TaskCollectionView = Backbone.View.extend({
317
+
318
+ el: $('#history'),
319
+
320
+ events: {
321
+ 'click .clear-tasks': 'clearTasks'
322
+ },
323
+
324
+ initialize: function() {
325
+ Tasks.bind('add', this.addOne, this);
326
+ Tasks.bind('reset', this.addAll, this);
327
+ Tasks.bind('all', this.render, this);
328
+ Tasks.fetch();
329
+ },
330
+
331
+ addOne: function(task) {
332
+ var view = new TaskView({model: task});
333
+ this.$('tbody').prepend(view.render().el);
334
+ },
335
+
336
+ addAll: function() {
337
+ Tasks.each(this.addOne);
338
+ },
339
+
340
+ clearTasks: function(e) {
341
+ Tasks.clear();
342
+ }
343
+
344
+ });
345
+
346
+ var TaskFormView = Backbone.View.extend({
347
+
348
+ el: $('#run'),
349
+
350
+ events: {
351
+ 'submit form' : 'onFormSubmit'
352
+ },
353
+
354
+ initialize: function() {
355
+ _.bindAll(this, 'onError');
356
+ this.bind('task-form:add', this.onTaskAdd, this);
357
+ this.$('.cap-command').typeahead({source: HellApp.tasks});
358
+ return this;
359
+ },
360
+
361
+ onError: function(task, errors) {
362
+ _.each(errors, function(fieldErrors) {
363
+ _.each(fieldErrors, function(error) {
364
+ $.bootstrapGrowl(error, {allow_dismiss: false, type: 'error', width: 200});
365
+ });
366
+ });
367
+ },
368
+
369
+ onFormSubmit: function(e) {
370
+ e.preventDefault();
371
+
372
+ var data = {};
373
+ $.each(this.$('form').serializeArray(), function(i, field) {
374
+ data[field.name] = field.value;
375
+ });
376
+
377
+ if (this.$('.environment .active').length) {
378
+ data.environment = this.$('.environment .active').attr('environment');
379
+ }
380
+
381
+ this.trigger('task-form:add', data);
382
+ },
383
+
384
+ onTaskAdd: function(data) {
385
+ Tasks.addTask(data, { error: this.onError });
386
+ return this;
387
+ }
388
+
389
+ });
390
+
391
+ HellApp = HellApp || {};
392
+ _.extend(HellApp, {
393
+ toggle_overlay: function() {
394
+ var docHeight = $(document).height();
395
+ if ($('.black-overlay').is(":visible")) {
396
+ $('.black-overlay').hide();
397
+ $('.progress').hide();
398
+ } else {
399
+ $('.black-overlay').height(docHeight);
400
+ $('.black-overlay').show();
401
+ $('.progress').show();
402
+ }
403
+ },
404
+ set_flash: function(message, flash_type) {
405
+ flash_type = flash_type || "error";
406
+ $.bootstrapGrowl(message, {
407
+ allow_dismiss: false, type: flash_type, width: 200
408
+ });
409
+ },
410
+ close_sockets: function(src) {
411
+ if (src !== null) {
412
+ src.close();
413
+ src = null;
414
+ }
415
+ if (src === undefined) {
416
+ src = null;
417
+ }
418
+ },
419
+ collection: new TaskCollectionView(),
420
+ form: new TaskFormView()
421
+ });
422
+
423
+ // HACK: This should be moved into a Backbone View or Router
424
+
425
+ // Function to activate the tab
426
+ function activateTab() {
427
+ var activeTab = $('[href=' + window.location.hash.replace('/', '') + ']');
428
+ if (activeTab) activeTab.tab('show');
429
+ }
430
+
431
+ // Trigger when the page loads
432
+ activateTab();
433
+
434
+ // Trigger when the hash changes (forward / back)
435
+ $(window).hashchange(function(e) {
436
+ activateTab();
437
+ });
438
+
439
+ // Change hash when a tab changes
440
+ $('a[data-toggle="tab"], a[data-toggle="pill"]').on('shown', function () {
441
+ window.location.hash = '/' + $(this).attr('href').replace('#', '');
442
+ });
443
+
444
+ });