├── fonts ├── glyphicons-halflings-regular.eot ├── glyphicons-halflings-regular.ttf ├── glyphicons-halflings-regular.woff └── glyphicons-halflings-regular.woff2 ├── js ├── lib │ ├── tooltip.js │ ├── ki.min.js │ ├── eventlistener.js │ ├── crouton.js │ ├── ki.extend.min.js │ ├── store.min.js │ └── bootstrap.min.js ├── main.js ├── run.js ├── timer.js ├── storage.js ├── gamepad.js └── actions.js ├── css ├── crouton.css ├── splitty.css ├── bootstrap-theme.min.css ├── bootstrap-theme.css └── bootstrap-theme.css.map ├── LICENSE ├── README.md └── index.html /fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylorant/splitty/HEAD/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylorant/splitty/HEAD/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylorant/splitty/HEAD/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ylorant/splitty/HEAD/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /js/lib/tooltip.js: -------------------------------------------------------------------------------- 1 | $.prototype.tooltip = function(i, c) 2 | { 3 | this.each(function(b) 4 | { 5 | $(b).on('mouseover', function() 6 | { 7 | b.querySelector(c).classList.add("active"); 8 | }).on('mouseout', function() 9 | { 10 | b.querySelector(c).classList.remove("active"); 11 | }); 12 | }); 13 | } -------------------------------------------------------------------------------- /js/lib/ki.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ki.js v1.1.0 - 2014-02-12 3 | * Copyright (c) 2014 Denis Ciccale (@tdecs) 4 | * Released under MIT license 5 | */ 6 | !function(a,b,c,d){function e(c){b.push.apply(this,c&&c.nodeType?[c]:""+c===c?a.querySelectorAll(c):d)}$=function(b){return/^f/.test(typeof b)?/c/.test(a.readyState)?b():$(a).on("DOMContentLoaded",b):new e(b)},$[c]=e[c]={length:0,on:function(a,b){return this.each(function(c){c.addEventListener(a,b)})},off:function(a,b){return this.each(function(c){c.removeEventListener(a,b)})},each:function(a,c){return b.forEach.call(this,a,c),this},splice:b.splice}}(document,[],"prototype"); -------------------------------------------------------------------------------- /js/lib/eventlistener.js: -------------------------------------------------------------------------------- 1 | var EventListener = function() { 2 | this.events = []; // Empty list of events/actions 3 | } 4 | 5 | EventListener.prototype.on = function(event, fn) { 6 | this.events[event] = this.events[event] || []; 7 | this.events[event].push(fn); 8 | } 9 | 10 | EventListener.prototype.off = function(event, fn) { 11 | if (this.events[event] && this.events[event].indexOf(fn) > -1) { 12 | this.events[event].splice(this.events[event].indexOf(fn), 1); 13 | } 14 | } 15 | 16 | EventListener.prototype.trigger = function(event) { 17 | if (this.events[event]) { 18 | var args = Array.from(arguments); 19 | this.events[event].forEach(function(fn) { 20 | fn.apply(null, args); 21 | }) 22 | } 23 | } -------------------------------------------------------------------------------- /css/crouton.css: -------------------------------------------------------------------------------- 1 | div.crouton-notification 2 | { 3 | width: 100%; 4 | height: 32px; 5 | 6 | color: #f3f3f3; 7 | 8 | line-height: 32px; 9 | font-size: 12px; 10 | font-weight: bold; 11 | text-align: center; 12 | padding: 0px 16px; 13 | overflow: hidden; 14 | transition: all ease-in-out 500ms; 15 | -webkit-transition: all ease-in-out 500ms; 16 | -moz-transition: all ease-in-out 500ms; 17 | } 18 | 19 | div.crouton-notification.closed 20 | { 21 | height: 0px; 22 | transition: all ease-in-out 500ms; 23 | -webkit-transition: all ease-in-out 500ms; 24 | -moz-transition: all ease-in-out 500ms; 25 | } 26 | 27 | div.crouton-confirm { background-color: #98CE00; } 28 | div.crouton-error { background-color: #FF413C; } 29 | div.crouton-info { background-color: #00DCFF; } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Yohann Lorant 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Splitty 2 | 3 | Splitty is a tiny speedrun-oriented timer made in Javascript and HTML5. 4 | 5 | ## Use it online 6 | 7 | Since this timer is written without any server-side language, you can use it directly from your browser from the Github Pages server at this address : 8 | 9 | [http://ylorant.github.io/splitty](http://ylorant.github.io/splitty) 10 | 11 | ## What it can do 12 | 13 | * Time speedruns, plays, pastas, whatever 14 | * Have split-times 15 | * Save your timers and splits 16 | * Save your personal best on them 17 | * Save your personal bests on each split ("gold" times) 18 | * Delay timer start (but it can't delay splits) 19 | * Import/Export timers in its own format. 20 | 21 | ## What it can't do (yet) 22 | 23 | * Import/Export to another timer's format. 24 | * Delay splitting on every split. 25 | * Split graph 26 | * Split icons 27 | 28 | ## Want to help / suggest an idea ? 29 | 30 | If you want to help by giving some of your time to improve this little project, 31 | you can clone this repository and then do some pull requests, they'll be implemented after verification :) 32 | 33 | If you want to help too but you don't know how to program but you have some neat ideas you find could help, 34 | feel free to open an [issue](https://github.com/ylorant/splitty/issues) describing your idea 35 | and label it as an **enhancement**. 36 | 37 | If you're using the timer and come across a bug, feel free to open an [issue](https://github.com/ylorant/splitty/issues) too 38 | describing in what the bug consist, what lead you to come across it, and if possible an exported file of your timer and/or screenshots. 39 | -------------------------------------------------------------------------------- /js/lib/crouton.js: -------------------------------------------------------------------------------- 1 | function Crouton(type, message, duration) 2 | { 3 | this.type = type; 4 | this.message = message; 5 | this.timeout = null; 6 | this.element = null; 7 | this.duration = 5000; 8 | 9 | if(duration != null) 10 | this.duration = duration; 11 | } 12 | 13 | CroutonType = 14 | { 15 | CONFIRM: 1, 16 | ERROR: 2, 17 | INFO: 3 18 | }; 19 | 20 | Crouton.DEFAULT_ELEMENT = 'body'; 21 | 22 | Crouton.prototype.show = function(element) 23 | { 24 | //If element has not been set (then it's a custom notification) we create it 25 | if(!this.element) 26 | { 27 | this.element = document.createElement('div'); 28 | this.element = $(this.element).html(this.message); 29 | } 30 | 31 | this.element.addClass('crouton-notification') 32 | .addClass('closed') 33 | .on('click', Crouton.prototype.dismiss.bind(this)); 34 | 35 | switch(this.type) 36 | { 37 | case CroutonType.CONFIRM: 38 | this.element.addClass('crouton-confirm'); 39 | break; 40 | case CroutonType.ERROR: 41 | this.element.addClass('crouton-error'); 42 | break; 43 | case CroutonType.INFO: 44 | this.element.addClass('crouton-info'); 45 | break; 46 | } 47 | 48 | if(!element) 49 | element = Crouton.DEFAULT_ELEMENT; 50 | 51 | $(element).append(this.element); 52 | setTimeout(function() 53 | { 54 | this.element.removeClass("closed"); 55 | }.bind(this), 50); 56 | 57 | this.timeout = setTimeout(Crouton.prototype.dismiss.bind(this), this.duration); 58 | } 59 | 60 | Crouton.prototype.dismiss = function() 61 | { 62 | if(this.timeout != null) 63 | clearTimeout(this.timeout); 64 | 65 | this.element.addClass("closed"); 66 | setTimeout(function() 67 | { 68 | this.element.remove(); 69 | }.bind(this), 500); 70 | } 71 | 72 | //Shortcut method to show quickly a notification 73 | Crouton.notify = function(type, message, duration) 74 | { 75 | var notif_type = CroutonType.INFO, 76 | notif_message = "", 77 | notif_duration = 5000; 78 | 79 | //Check if we got only the message 80 | if(typeof type == "string") 81 | { 82 | notif_message = type; 83 | if(message != null) // A duration has been entered 84 | notif_duration = message; 85 | } 86 | else 87 | { 88 | notif_type = type; 89 | notif_message = message; 90 | notif_duration = duration; 91 | } 92 | 93 | var notif = new Crouton(notif_type, notif_message, notif_duration); 94 | notif.show(); 95 | } -------------------------------------------------------------------------------- /js/lib/ki.extend.min.js: -------------------------------------------------------------------------------- 1 | !function(){$.each=function(a,b){for(var c=0,d=a.length;d>c;++c)b.call(a[c],c,a[c]);return this};var a=["addClass","removeClass","toggleClass"],b=["add","remove","toggle"];a.forEach(function(a,c){$.prototype[a]=function(a){return this.each(function(d){d.classList[b[c]](a)})}}),$.prototype.hasClass=function(a){return this[0].classList.contains(a)},$.prototype.append=function(a){return this.each(function(b){b.appendChild(a[0])})},$.prototype.prepend=function(a){return this.each(function(b){b.insertBefore(a[0],b.firstChild)})},$.prototype.hide=function(){return this.each(function(a){a.style.display="none"})},$.prototype.show=function(){return this.each(function(a){a.style.display=""})},$.prototype.attr=function(a,b){return b===[]._?this[0].getAttribute(a):this.each(function(c){c.setAttribute(a,b)})},$.prototype.removeAttr=function(a){return this.each(function(b){b.removeAttribute(a)})},$.prototype.hasAttr=function(a){return this[0].hasAttribute(a)},$.prototype.before=function(a){return this.each(function(b){""+a===a?b.insertAdjacentHTML("beforebegin",a):b.parentNode.insertBefore(a[0],b)})},$.prototype.after=function(a){return this.each(function(b){""+a===a?b.insertAdjacentHTML("afterend",a):b.parentNode.insertBefore(a[0],b.nextSibling)})},$.prototype.css=function(a,b){if("object"==typeof a){for(var c in a)this.each(function(b){b.style[c]=a[c]});return this}return b===[]._?this[0].style[a]:this.each(function(c){c.style[a]=b})},$.prototype.first=function(){return $(this[0])},$.prototype.last=function(){return $(this[this.length-1])},$.prototype.get=function(a){return $(this[a])},$.prototype.text=function(a){return a===[]._?this[0].textContent:this.each(function(b){b.textContent=a})},$.prototype.html=function(a){return a===[]._?this[0].innerHTML:this.each(function(b){b.innerHTML=a})},$.prototype.parent=function(){return this.length<2?$(this[0].parentNode):[]},$.prototype.remove=function(){return this.each(function(a){a.parentNode.removeChild(a)})},$.trim=function(a){return a.replace(/^\s+|\s+$/g,"")},$.prototype.trigger=function(a){if(document.createEvent){var b=document.createEvent("HTMLEvents");b.initEvent(a,!0,!1),this.each(function(a){a.dispatchEvent(b)})}else this.each(function(b){b.fireEvent("on"+a)})},$.prototype.is=function(a){var b=this[0].matches||this[0].matchesSelector||this[0].msMatchesSelector||this[0].mozMatchesSelector||this[0].webkitMatchesSelector||this[0].oMatchesSelector;if(b)return b.call(this[0],a);for(var c=this[0].parentNode.querySelectorAll(a),d=c.length;d--;)if(c[d]===this[0])return!0;return!1},"filter map".split(" ").forEach(function(a){$[a]=function(b,c){return b[a](c)}}),$.stop=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1},$.param=function(a,b){var c=[];for(var d in a){var e=b?b+"["+d+"]":d,f=a[d];c.push("object"==typeof f?$.param(f,e):encodeURIComponent(e)+"="+encodeURIComponent(f))}return c.join("&")},$.ajax=function(a,b,c){var d=new XMLHttpRequest,e=new $.Deferred,f="object"==typeof b?1:0,g=["GET","POST"];d.open(g[f],a,!0);var h=f?c:b;return"undefined"==typeof c&&"function"!=typeof b&&(h=function(){}),d.onerror=function(){e.reject(this),h(this,!0)},d.onreadystatechange=function(){4===this.readyState&&(this.status>=200&&this.status<400?(e.resolve(this.response),h(this.response,!0)):(e.reject(this),h(this,!0)))},f?(d.setRequestHeader("Content-type","application/x-www-form-urlencoded"),d.send($.param(b))):d.send(),d=null,e.promise()}}(); -------------------------------------------------------------------------------- /js/lib/store.min.js: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2010-2016 Marcus Westin */ 2 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.store = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;odocument.w=window'),u.close(),c=u.w.frames[0].document,t=c.createElement("div")}catch(l){t=i.createElement("div"),c=i.body}var f=function(e){return function(){var n=Array.prototype.slice.call(arguments,0);n.unshift(t),c.appendChild(t),t.addBehavior("#default#userData"),t.load(o);var i=e.apply(r,n);return c.removeChild(t),i}},d=new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]","g"),s=function(e){return e.replace(/^d/,"___$&").replace(d,"___")};r.set=f(function(e,t,n){return t=s(t),void 0===n?r.remove(t):(e.setAttribute(t,r.serialize(n)),e.save(o),n)}),r.get=f(function(e,t,n){t=s(t);var i=r.deserialize(e.getAttribute(t));return void 0===i?n:i}),r.remove=f(function(e,t){t=s(t),e.removeAttribute(t),e.save(o)}),r.clear=f(function(e){var t=e.XMLDocument.documentElement.attributes;e.load(o);for(var r=t.length-1;r>=0;r--)e.removeAttribute(t[r].name);e.save(o)}),r.getAll=function(e){var t={};return r.forEach(function(e,r){t[e]=r}),t},r.forEach=f(function(e,t){for(var n,i=e.XMLDocument.documentElement.attributes,o=0;n=i[o];++o)t(n.name,r.deserialize(e.getAttribute(n.name)))})}try{var v="__storejs__";r.set(v,v),r.get(v)!=v&&(r.disabled=!0),r.remove(v)}catch(l){r.disabled=!0}return r.enabled=!r.disabled,r}(); 5 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 6 | },{}]},{},[1])(1) 7 | }); -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | //// ki.js extensions //// 2 | 3 | // Search for a selector in parents of the current node 4 | $.prototype.parents = function(q) 5 | { 6 | var parents = []; 7 | var p = this[0].parentNode; 8 | while (p !== document) 9 | { 10 | if(typeof q == "undefined" || $(p).is(q)) 11 | parents.push(p); 12 | 13 | p = p.parentNode; 14 | } 15 | 16 | return parents; // returns an Array [] 17 | } 18 | 19 | $.prototype.clone = function() 20 | { 21 | return $(this[0].cloneNode(true)); 22 | } 23 | 24 | $.prototype.child = function(q) 25 | { 26 | return $(this[0].querySelector(q)); 27 | } 28 | 29 | $.prototype.val = function(v) 30 | { 31 | return this.each(function(i) 32 | { 33 | i.value = v; 34 | }); 35 | } 36 | 37 | //// end ki.js extensions //// 38 | 39 | /// Not ki.js but still useful functions /// 40 | 41 | var q = document.querySelector.bind(document); 42 | Element.prototype.q = Element.prototype.querySelector; 43 | 44 | /// end not ki.js but still useful functions /// 45 | 46 | var action_handler = null; 47 | var storage = null; 48 | 49 | window.current_timer = null; 50 | 51 | function msec_to_time(time, res) 52 | { 53 | var res_pow = Math.pow(10, res); 54 | time = Math.abs(time); 55 | 56 | var minutes = 0; 57 | var hours = 0; 58 | var seconds = parseInt(time / 1000, 10); 59 | var dseconds = parseInt(time / (1000 / res_pow), 10); 60 | 61 | if(seconds >= 60) 62 | minutes = parseInt(seconds / 60, 10); 63 | if(minutes >= 60) 64 | hours = parseInt(minutes / 60, 10); 65 | 66 | seconds %= 60; 67 | minutes %= 60; 68 | dseconds %= res_pow; 69 | 70 | return { hr: hours, mn: minutes, sec: seconds, ms: dseconds }; 71 | } 72 | 73 | function msec_to_string(time, use_markup, res, relative_time) 74 | { 75 | human_time = msec_to_time(time, res); 76 | var str = ""; 77 | 78 | if(time < 0) 79 | str += "-"; 80 | else if(relative_time) 81 | str += "+"; 82 | 83 | if(human_time.hr > 0) 84 | str += human_time.hr + ":" + (human_time.mn < 10 ? "0" : ""); 85 | if(human_time.mn > 0 || !relative_time) // when time is shown relatively, strip the minutes if there is not 86 | str += human_time.mn + ":" + (human_time.sec < 10 ? "0" : ""); 87 | 88 | if(res > 0) 89 | { 90 | // Pad the ms number to reflect the significative numbers shown 91 | var ms_length = human_time.ms.toString(10).length; 92 | var shown_ms = "0".repeat(res - ms_length) + human_time.ms.toString(10); 93 | 94 | if(use_markup) 95 | str += human_time.sec + "." + shown_ms + ""; 96 | else 97 | str += human_time.sec + "." + shown_ms; 98 | } 99 | else 100 | str += human_time.sec; 101 | 102 | return str; 103 | } 104 | 105 | function string_to_msec(str) 106 | { 107 | var time_str = str.split(':'); 108 | var time = 0; 109 | var multiplier = 1000; 110 | 111 | //Take account of milliseconds 112 | if(time_str[time_str.length - 1].indexOf('.') != -1) 113 | { 114 | var ms = time_str[time_str.length - 1].split('.'); 115 | time_str[time_str.length - 1] = ms[0]; 116 | time += parseInt(ms[1] + "000".substring(0, 3 - ms[1].length)); 117 | } 118 | 119 | for(var i = time_str.length - 1; i >= 0; i--) 120 | { 121 | time += parseInt(time_str[i]) * multiplier; 122 | multiplier *= 60; 123 | } 124 | 125 | return time; 126 | } 127 | 128 | //Onload event - init everything 129 | $(function() 130 | { 131 | // Initialize storage$ 132 | storage = new Storage(); 133 | storage.init(); 134 | 135 | action_handler = new Actions(); 136 | action_handler.init(); 137 | action_handler.load_page("main-menu"); 138 | 139 | //Initializing Crouton notif settings 140 | Crouton.DEFAULT_ELEMENT = "#page-content-wrapper"; 141 | 142 | $('.my-tooltip').tooltip('.my-tooltip-icon', '.my-tooltip-content'); 143 | }); -------------------------------------------------------------------------------- /js/run.js: -------------------------------------------------------------------------------- 1 | function Run(timer) 2 | { 3 | this.timer = timer; 4 | this.elapsed = 0; // Number of milliseconds elapsed 5 | this.start_time = null; // Instance of Date 6 | this.split_times = []; 7 | this.best_splits = []; 8 | this.current_split = 0; 9 | this.started = false; 10 | this.best_time_updated = false; 11 | 12 | // Fill best splits with timer's best splits 13 | for(var i in timer.splits) 14 | this.best_splits[i] = timer.splits[i].split_best; 15 | } 16 | 17 | Run.prototype.start = function() 18 | { 19 | this.start_time = new Date((new Date()).getTime() + this.timer.start_delay); 20 | this.started = true; 21 | 22 | this.timer.run_count++; 23 | this.timer.save(); 24 | 25 | // Register to update on global manager 26 | if(this.timer.timer_type == Timer.Type.RTA) 27 | Actions.get_manager().register_updates(this); 28 | } 29 | 30 | Run.prototype.split = function() 31 | { 32 | if(!this.started) 33 | return; 34 | 35 | this.split_times[this.current_split] = this.elapsed; 36 | Actions.get_manager().update(null, true); 37 | 38 | var duration = this.split_times[this.current_split]; 39 | if(this.current_split > 0) 40 | duration -= this.split_times[this.current_split - 1]; 41 | 42 | //Check for PB 43 | if(this.best_splits[this.current_split] == null || duration < this.best_splits[this.current_split]) 44 | { 45 | this.best_splits[this.current_split] = duration; 46 | this.best_time_updated = true; 47 | Actions.get_manager().update_sob(); 48 | } 49 | 50 | //Increase split counter 51 | this.current_split++; 52 | 53 | if(this.current_split == this.timer.splits.length) 54 | { 55 | this.stop(false); 56 | this.current_split--; 57 | } 58 | } 59 | 60 | Run.prototype.split_manual = function(split_time) 61 | { 62 | if(!this.started) 63 | return; 64 | 65 | this.split_times[this.current_split] = this.elapsed + split_time; 66 | this.elapsed += split_time; 67 | Actions.get_manager().update(null, true); 68 | 69 | //Check for PB 70 | if(this.timer.splits[this.current_split].split_best == null || split_time < this.timer.splits[this.current_split].split_best) 71 | { 72 | this.best_splits[this.current_split] = split_time; 73 | this.best_time_updated = true; 74 | Actions.get_manager().update_sob(); 75 | } 76 | 77 | this.current_split++; 78 | 79 | if(this.current_split == this.timer.splits.length) 80 | { 81 | this.stop(false); 82 | this.current_split--; // Put conveniently the split at the last split to avoid out of range errors. 83 | } 84 | } 85 | 86 | Run.prototype.prev_split = function() 87 | { 88 | if(this.current_split > 0) 89 | { 90 | // Forget the best split that may have been overriden 91 | this.best_splits[this.current_split] = this.timer.splits[this.current_split].split_best; 92 | 93 | this.current_split--; 94 | 95 | if(this.timer.timer_type == Timer.Type.MANUAL) 96 | { 97 | if(this.current_split == 0) 98 | this.elapsed = 0; 99 | else 100 | this.elapsed = this.split_times[this.current_split - 1]; 101 | } 102 | 103 | this.split_times[this.current_split] = null; 104 | } 105 | } 106 | 107 | Run.prototype.next_split = function() 108 | { 109 | this.split_times[this.current_split] = null; 110 | this.current_split++; 111 | 112 | if(this.current_split == this.timer.splits.length) 113 | this.stop(false); 114 | } 115 | 116 | Run.prototype.stop = function(do_update) 117 | { 118 | do_update = typeof do_update != "undefined" ? do_update : true; 119 | 120 | if(do_update) 121 | Actions.get_manager().update(); 122 | 123 | this.started = false; 124 | Actions.get_manager().unregister_updates(this); 125 | } 126 | 127 | Run.prototype.update = function() 128 | { 129 | var now = new Date(); 130 | this.elapsed = now.getTime() - this.start_time.getTime(); 131 | } 132 | 133 | Run.prototype.get_time = function(use_markup, final_time) 134 | { 135 | use_markup = use_markup || false; 136 | res = final_time ? Math.max(0, 3 - Math.floor(this.elapsed / 3600000).toString().length + 1) : 1; // For each more digit we have over 1 hour, we reduce the number of shown decimal places 137 | 138 | return msec_to_string(this.elapsed, use_markup, res); 139 | } -------------------------------------------------------------------------------- /js/lib/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/ 2 | "document"in self&&!("classList"in document.createElement("_"))&&function(e){"use strict";if("Element"in e){var t="classList",n="prototype",r=e.Element[n],a=Object,i=String[n].trim||function(){return this.replace(/^\s+|\s+$/g,"")},s=Array[n].indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(t in this&&this[t]===e)return t;return-1},l=function(e,t){this.name=e,this.code=DOMException[e],this.message=t},o=function(e,t){if(""===t)throw new l("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(t))throw new l("INVALID_CHARACTER_ERR","String contains an invalid character");return s.call(e,t)},c=function(e){for(var t=i.call(e.getAttribute("class")||""),n=t?t.split(/\s+/):[],r=0,a=n.length;a>r;r++)this.push(n[r]);this._updateClassName=function(){e.setAttribute("class",""+this)}},u=c[n]=[],d=function(){return new c(this)};if(l[n]=Error[n],u.item=function(e){return this[e]||null},u.contains=function(e){return e+="",-1!==o(this,e)},u.add=function(){var e,t=arguments,n=0,r=t.length,a=!1;do e=t[n]+"",-1===o(this,e)&&(this.push(e),a=!0);while(r>++n);a&&this._updateClassName()},u.remove=function(){var e,t=arguments,n=0,r=t.length,a=!1;do{e=t[n]+"";var i=o(this,e);-1!==i&&(this.splice(i,1),a=!0)}while(r>++n);a&&this._updateClassName()},u.toggle=function(e,t){e+="";var n=this.contains(e),r=n?t!==!0&&"remove":t!==!1&&"add";return r&&this[r](e),!n},u.toString=function(){return this.join(" ")},a.defineProperty){var g={get:d,enumerable:!0,configurable:!0};try{a.defineProperty(r,t,g)}catch(h){-2146823252===h.number&&(g.enumerable=!1,a.defineProperty(r,t,g))}}else a[n].__defineGetter__&&r.__defineGetter__(t,d)}}(self),/*! 3 | * Bootstrap without jQuery v0.6.1 for Bootstrap 3 4 | * By Daniel Davis under MIT License 5 | * https://github.com/tagawa/bootstrap-without-jquery 6 | */ 7 | function(){"use strict";function e(){var e,t=document.createElement("div"),n={transition:"transitionend",OTransition:"otransitionend",MozTransition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(e in n)if(n.hasOwnProperty(e)&&void 0!==t.style[e])return n[e];return!1}function t(e){var t={};e=e||window.event,t.evTarget=e.currentTarget||e.srcElement;var n=t.evTarget.getAttribute("data-target");return t.dataTarget=n?document.querySelector(n):!1,t}function n(e){var t=e.style.height;e.style.height="auto";var n=getComputedStyle(e).height;return e.style.height=t,e.offsetHeight,n}function r(e,t){if(document.createEvent){var n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!1),e.dispatchEvent(n)}else e.fireEvent("on"+t)}function a(e,t){e.classList.remove("collapse"),e.classList.add("collapsing"),t.classList.remove("collapsed"),t.setAttribute("aria-expanded",!0),e.style.height=n(e),d?e.addEventListener(d,function(){s(e)},!1):s(e)}function i(e,t){e.classList.remove("collapse"),e.classList.remove("in"),e.classList.add("collapsing"),t.classList.add("collapsed"),t.setAttribute("aria-expanded",!1),e.style.height=getComputedStyle(e).height,e.offsetHeight,e.style.height="0px"}function s(e){e.classList.remove("collapsing"),e.classList.add("collapse"),e.setAttribute("aria-expanded",!1),"0px"!==e.style.height&&(e.classList.add("in"),e.style.height="auto")}function l(e){e.preventDefault();var n=t(e),r=n.dataTarget;return r.classList.contains("in")?i(r,n.evTarget):a(r,n.evTarget),!1}function o(e){function n(){try{i.parentNode.removeChild(i),r(i,"closed.bs.alert")}catch(e){window.console.error("Unable to remove alert")}}e.preventDefault();var a=t(e),i=a.dataTarget;if(!i){var s=a.evTarget.parentNode;s.classList.contains("alert")?i=s:s.parentNode.classList.contains("alert")&&(i=s.parentNode)}return r(i,"close.bs.alert"),i.classList.remove("in"),d&&i.classList.contains("fade")?i.addEventListener(d,function(){n()},!1):n(),!1}function c(e){e=e||window.event;var t=e.currentTarget||e.srcElement;return t.parentElement.classList.toggle("open"),!1}function u(e){e=e||window.event;var t=e.currentTarget||e.srcElement;return t.parentElement.classList.remove("open"),e.relatedTarget&&"dropdown"!==e.relatedTarget.getAttribute("data-toggle")&&e.relatedTarget.click(),!1}for(var d=e(),g=document.querySelectorAll("[data-toggle=collapse]"),h=0,f=g.length;f>h;h++)g[h].onclick=l;for(var m=document.querySelectorAll("[data-dismiss=alert]"),v=0,p=m.length;p>v;v++)m[v].onclick=o;for(var y,E=document.querySelectorAll("[data-toggle=dropdown]"),w=0,b=E.length;b>w;w++)y=E[w],y.setAttribute("tabindex","0"),y.onclick=c,y.onblur=u}(); -------------------------------------------------------------------------------- /js/timer.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Timer.splits[] is the split list. Each split is an object composed like this: 4 | 5 | - name: The name of the split 6 | - pb_duration: The duration of the PB time 7 | - pb_split: The elapsed time since the beginning of the run 8 | - split_best: The Golden time. 9 | 10 | */ 11 | 12 | function Timer() 13 | { 14 | this.timer_name = ""; 15 | this.run_name = ""; 16 | this.hash = ""; 17 | this.start_delay = 0; 18 | this.run_count = 0; 19 | this.splits = []; 20 | this.timer_type = Timer.Type.RTA; 21 | } 22 | 23 | Timer.Type = { RTA: 0, MANUAL: 1 }; 24 | 25 | Timer.exists = function(timer_name) 26 | { 27 | if(!store.enabled) 28 | return false; 29 | 30 | var timers_names = Storage.get().get_names(); 31 | 32 | for(var i in timers_names) 33 | { 34 | if(timers_names[i] == timer_name) 35 | return true; 36 | } 37 | 38 | return false; 39 | } 40 | 41 | Timer.prototype.save = function() 42 | { 43 | if(Storage.enabled()) 44 | Storage.get().set_timer(this); 45 | } 46 | 47 | Timer.prototype.delete = function() 48 | { 49 | 50 | if(Storage.enabled()) 51 | Storage.get().delete_timer(this); 52 | } 53 | 54 | Timer.prototype.to_string = function() 55 | { 56 | return JSON.stringify(this); 57 | } 58 | 59 | Timer.prototype.save_splits = function(run) 60 | { 61 | if(run === null || run === undefined) 62 | return; 63 | 64 | for(var k in this.splits) 65 | { 66 | this.splits[k].pb_split = run.split_times[k]; 67 | 68 | this.splits[k].pb_duration = run.split_times[k]; 69 | if(k > 0) 70 | this.splits[k].pb_duration -= run.split_times[k - 1]; 71 | } 72 | 73 | this.save(); 74 | } 75 | 76 | Timer.prototype.save_bests = function(run) 77 | { 78 | if(run === null || run === undefined) 79 | return; 80 | 81 | for(var k in this.splits) 82 | { 83 | if(run.best_splits[k] !== null && (this.splits[k].split_best > run.best_splits[k] || this.splits[k].split_best === null)) 84 | this.splits[k].split_best = run.best_splits[k]; 85 | } 86 | 87 | run.best_time_updated = false; 88 | 89 | this.save(); 90 | } 91 | 92 | Timer.prototype.compute_split_lengths = function() 93 | { 94 | var previous_elapsed = 0; 95 | for(var i in this.splits) 96 | { 97 | // Fixing PB splits 98 | if(this.splits[i].pb_split != null) 99 | { 100 | this.splits[i].pb_duration = this.splits[i].pb_split - previous_elapsed; 101 | previous_elapsed = this.splits[i].pb_split; 102 | } 103 | else if(this.splits[i].pb_split == null) 104 | this.splits[i].pb_duration = null; 105 | } 106 | } 107 | 108 | Timer.load = function(timer_name) 109 | { 110 | var new_timer = new Timer(); 111 | 112 | if(Storage.enabled()) 113 | { 114 | // Load the raw data objects (not Timer objects) 115 | var timer_obj = Storage.get().get_timer(timer_name); 116 | 117 | // Hydrate the timer properties 118 | for(var k in timer_obj) 119 | new_timer[k] = timer_obj[k]; 120 | 121 | new_timer.compute_split_lengths(); 122 | 123 | // If we haven't got any gold for the split, compute it 124 | for(var i in new_timer.splits) 125 | { 126 | if(typeof new_timer.splits[i].pb != "undefined") 127 | delete new_timer.splits[i].pb; 128 | 129 | if(!new_timer.splits[i].split_best 130 | || new_timer.splits[i].split_best < 0 131 | || (new_timer.splits[i].pb_duration && new_timer.splits[i].split_best > new_timer.splits[i].pb_duration)) 132 | { 133 | new_timer.splits[i].split_best = new_timer.splits[i].pb_duration; 134 | } 135 | } 136 | 137 | new_timer.save(); 138 | } 139 | 140 | return new_timer; 141 | } 142 | 143 | Timer.import_json = function(json) 144 | { 145 | var new_timer = new Timer(); 146 | var obj = JSON.parse(json); 147 | 148 | for(var k in obj) 149 | new_timer[k] = obj[k]; 150 | 151 | new_timer.compute_split_lengths(); 152 | 153 | // If we haven't got any gold for the split, compute it 154 | for(var i in new_timer.splits) 155 | { 156 | if(typeof new_timer.splits[i].pb != "undefined") 157 | delete new_timer.splits[i].pb; 158 | 159 | if(!new_timer.splits[i].split_best 160 | || new_timer.splits[i].split_best < 0 161 | || (new_timer.splits[i].pb_duration && new_timer.splits[i].split_best > new_timer.splits[i].pb_duration)) 162 | { 163 | new_timer.splits[i].split_best = new_timer.splits[i].pb_duration; 164 | } 165 | } 166 | 167 | return new_timer; 168 | } -------------------------------------------------------------------------------- /js/storage.js: -------------------------------------------------------------------------------- 1 | function Storage() 2 | { 3 | Storage.self_ref = this; 4 | 5 | // Convert old storage if necessary 6 | this.convert_old(); 7 | } 8 | 9 | //// Static methods //// 10 | 11 | /* Generates a random string, used to create hash keys in storage */ 12 | Storage.random_string = function(size) 13 | { 14 | var text = ""; 15 | var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 16 | 17 | for( var i=0; i < size; i++ ) 18 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 19 | 20 | return text; 21 | } 22 | 23 | Storage.self_ref = null; 24 | 25 | /* Singleton get */ 26 | Storage.get = function() 27 | { 28 | return Storage.self_ref; 29 | } 30 | 31 | /* Checks if the storage can be accessed */ 32 | Storage.enabled = function() 33 | { 34 | return store.enabled; 35 | } 36 | 37 | //// Initialization routines //// 38 | 39 | /* Convert old storage to new format */ 40 | Storage.prototype.convert_old = function() 41 | { 42 | // Check if there is an old storage present, and we don't do anything if there isn't 43 | if(typeof localStorage['timer_names'] == "undefined") 44 | return; 45 | 46 | // We consider that we're in the old format already 47 | var old_timer_names = JSON.parse(localStorage['timer_names']); 48 | var new_timer_names = {}; 49 | var timers = {}; 50 | 51 | for(var i in old_timer_names) 52 | { 53 | var hash = Storage.random_string(32); 54 | new_timer_names[hash] = old_timer_names[i]; 55 | timers[hash] = JSON.parse(localStorage[old_timer_names[i]]); 56 | 57 | timers[hash].hash = hash; 58 | } 59 | 60 | // Now that we have all the timers it's time to rewrite the storage 61 | this.init(); 62 | store.set('timers', timers); 63 | store.set('timers.names', new_timer_names); 64 | } 65 | 66 | /* Initialize storage */ 67 | Storage.prototype.init = function() 68 | { 69 | if(!store.has("timers")) 70 | { 71 | store.clear(); 72 | store.set('timers', {}); 73 | store.set('timers.names', {}); 74 | store.set('settings', {}); 75 | } 76 | } 77 | 78 | //// Timer Manipulation //// 79 | 80 | /* Gets the hash from a timer name */ 81 | Storage.prototype.get_hash = function(timer_name) 82 | { 83 | var timer_names = store.get('timers.names'); 84 | for(var i in timer_names) 85 | { 86 | if(timer_names[i] == timer_name) 87 | return i; 88 | } 89 | 90 | return null; 91 | } 92 | 93 | /* Returns timer names */ 94 | Storage.prototype.get_names = function() 95 | { 96 | var timers_names = store.get('timers.names'); 97 | return timers_names; 98 | } 99 | 100 | /* Gets a timer from the storage */ 101 | Storage.prototype.get_timer = function(hash) 102 | { 103 | if(hash === null) 104 | return null; 105 | 106 | var timers = store.get('timers'); 107 | return timers[hash]; 108 | } 109 | 110 | /* Sets a timer into the storage */ 111 | Storage.prototype.set_timer = function(timer) 112 | { 113 | var hash = null; 114 | var timers = store.get('timers'); 115 | var timer_names = store.get('timers.names'); 116 | 117 | // Define timer hash if it hasn't already been defined 118 | if(typeof timer.hash != "string" || timer.hash.length === 0) 119 | { 120 | do 121 | { 122 | hash = Storage.random_string(32); 123 | } 124 | while(typeof timers[hash] != "undefined"); 125 | 126 | timer.hash = hash; 127 | } 128 | else 129 | hash = timer.hash; 130 | 131 | // Save the timer 132 | timers[hash] = timer; 133 | store.set("timers", timers); 134 | 135 | // Save the timer name 136 | timer_names[hash] = timer.timer_name; 137 | store.set("timers.names", timer_names); 138 | } 139 | 140 | /* Deletes a timer from the storage */ 141 | Storage.prototype.delete_timer = function(timer) 142 | { 143 | var hash = timer.hash; 144 | 145 | if(hash === null) 146 | return false; 147 | 148 | var timers = store.get("timers"); 149 | var timer_names = store.get("timers.names"); 150 | 151 | delete timers[hash]; 152 | delete timer_names[hash]; 153 | 154 | return true; 155 | } 156 | 157 | /* Sets a setting property */ 158 | Storage.prototype.set_settings_property = function(name, value) 159 | { 160 | var settings = store.get('settings'); 161 | settings[name] = value; 162 | store.set('settings', settings); 163 | } 164 | 165 | /* Gets a setting property */ 166 | Storage.prototype.get_settings_property = function(name, value) 167 | { 168 | var settings = store.get('settings'); 169 | 170 | if(typeof settings[name] != "undefined") 171 | return settings[name]; 172 | 173 | return null; 174 | } 175 | 176 | /* Gets all of the settings at once */ 177 | Storage.prototype.get_settings = function() 178 | { 179 | var settings = store.get('settings'); 180 | return settings; 181 | } 182 | 183 | /* Updates all the settings at once */ 184 | Storage.prototype.set_settings = function(settings) 185 | { 186 | store.set('settings', settings); 187 | } -------------------------------------------------------------------------------- /js/gamepad.js: -------------------------------------------------------------------------------- 1 | function Gamepad() 2 | { 3 | this.connected_gamepads = []; 4 | this.current_gamepad_id = null; 5 | this.current_gamepad = null; 6 | this.previous_gamepad_status = null; 7 | this.poll_interval_id = null; 8 | this.events = new EventListener(); 9 | this.do_poll = false; 10 | 11 | // Start global polling for new gamepads, 4 per second 12 | setInterval(this.poll_new_gamepads.bind(this), 250); 13 | } 14 | 15 | Gamepad.prototype.scan_gamepads = function() 16 | { 17 | var gamepads = navigator.getGamepads(); 18 | var connectedGamepads = []; 19 | 20 | for(var i in gamepads) 21 | { 22 | if(gamepads[i] && gamepads[i].connected) 23 | connectedGamepads.push(gamepads[i]); 24 | } 25 | 26 | return connectedGamepads; 27 | } 28 | 29 | Gamepad.prototype.set_gamepad = function(gamepad_id) 30 | { 31 | this.current_gamepad_status = null; 32 | this.current_gamepad_id = gamepad_id; 33 | 34 | if(this.connected_gamepads.indexOf(gamepad_id) != -1) 35 | this.poll_new_gamepads(); // Polling new gamepads will load the selected gamepad into this.current_gamepad 36 | else 37 | this.current_gamepad = null; // Gamepad is disconnected, disable it 38 | } 39 | 40 | Gamepad.prototype.poll_new_gamepads = function() 41 | { 42 | var gamepads = this.scan_gamepads(); 43 | 44 | //// Polling for new/disconnected gamepads //// 45 | 46 | // Generating gamepad ids table and checking changes compared to the already saved one 47 | var connected_gamepads = []; 48 | var gamepads_connected = []; 49 | var gamepads_disconnected = []; 50 | for(var i in gamepads) 51 | { 52 | connected_gamepads.push(gamepads[i].id); 53 | 54 | // Gamepad has been connected 55 | if(this.connected_gamepads.indexOf(gamepads[i].id) == -1) 56 | gamepads_connected.push(gamepads[i].id); 57 | 58 | if(gamepads[i].id == this.current_gamepad_id) 59 | this.current_gamepad = gamepads[i]; 60 | } 61 | 62 | // Checking previous gamepads to see if one has been disconnected 63 | for(var i in this.connected_gamepads) 64 | { 65 | if(connected_gamepads.indexOf(this.connected_gamepads[i]) == -1) 66 | gamepads_disconnected.push(this.connected_gamepads[i]); 67 | } 68 | 69 | this.connected_gamepads = connected_gamepads; 70 | 71 | for(var i in gamepads_connected) 72 | this.events.trigger('gamepadconnected', gamepads_connected[i]); 73 | 74 | for(var i in gamepads_disconnected) 75 | this.events.trigger('gamepaddisconnected', gamepads_disconnected[i]); 76 | } 77 | 78 | 79 | //// Polling selected gamepad for button changes //// 80 | Gamepad.prototype.poll_gamepad_buttons = function() 81 | { 82 | if(this.do_poll) 83 | window.requestAnimationFrame(this.poll_gamepad_buttons.bind(this)); 84 | 85 | if(this.current_gamepad != null) 86 | { 87 | // Force gamepad updates, for Chrome :( 88 | if(window.chrome) 89 | navigator.getGamepads(); 90 | 91 | // Perform checks only if we have a previous status 92 | if(this.current_gamepad_status != null && this.current_gamepad.timestamp != this.current_gamepad_status.timestamp) 93 | { 94 | // Checking axes 95 | for(var i in this.current_gamepad.axes) 96 | { 97 | if(this.current_gamepad_status.axes[i] != this.current_gamepad.axes[i]) 98 | { 99 | this.current_gamepad_status.axes[i] = this.current_gamepad.axes[i]; 100 | this.events.trigger('axischanged', i, this.current_gamepad.axes[i]); 101 | } 102 | } 103 | 104 | for(var i in this.current_gamepad.buttons) 105 | { 106 | if(this.current_gamepad.buttons[i].pressed != this.current_gamepad_status.buttons[i]) 107 | { 108 | this.current_gamepad_status.buttons[i] = this.current_gamepad.buttons[i].pressed; 109 | var ev = this.current_gamepad.buttons[i].pressed ? "buttonpressed" : "buttonreleased"; 110 | this.events.trigger(ev, i, this.current_gamepad.buttons[i]); 111 | } 112 | } 113 | 114 | this.current_gamepad_status.timestamp = this.current_gamepad.timestamp; 115 | } 116 | else if(this.current_gamepad_status == null) 117 | { 118 | this.current_gamepad_status = {axes: [], buttons: [], timestamp: null}; 119 | for(var i in this.current_gamepad.axes) 120 | this.current_gamepad_status.axes.push(0); 121 | 122 | for(var i in this.current_gamepad.buttons) 123 | this.current_gamepad_status.buttons.push(false); 124 | 125 | if(this.current_gamepad.timestamp) 126 | this.current_gamepad_status.timestamp = this.current_gamepad.timestamp; 127 | } 128 | } 129 | } 130 | 131 | Gamepad.prototype.start_polling = function() 132 | { 133 | this.current_gamepad_status = null; 134 | 135 | // Poll on animation frame, usually gives 60Hz 136 | this.do_poll = true; 137 | window.requestAnimationFrame(this.poll_gamepad_buttons.bind(this)); 138 | console.log("Starting polling"); 139 | } 140 | 141 | Gamepad.prototype.stop_polling = function() 142 | { 143 | this.do_poll = false; 144 | } 145 | 146 | Gamepad.get_name_from_id = function(gamepad_id) 147 | { 148 | var id_name = gamepad_id; 149 | 150 | // Firefox format match 151 | if(/^[0-9a-f]{4}-[0-9a-f]{4}-.+$/.test(gamepad_id)) 152 | { 153 | var id_parts = gamepad_id.split('-'); 154 | 155 | id_parts.shift(); 156 | id_parts.shift(); 157 | id_name = id_parts.join('-'); 158 | } 159 | 160 | return id_name; 161 | } -------------------------------------------------------------------------------- /css/splitty.css: -------------------------------------------------------------------------------- 1 | /* Base Styles */ 2 | 3 | #wrapper 4 | { 5 | padding-left: 0; 6 | -webkit-transition: all 0.5s ease; 7 | -moz-transition: all 0.5s ease; 8 | -o-transition: all 0.5s ease; 9 | transition: all 0.5s ease; 10 | } 11 | 12 | #wrapper.active 13 | { 14 | padding-left: 300px; 15 | } 16 | 17 | #timer-wrapper 18 | { 19 | z-index: 1000; 20 | position: fixed; 21 | left: 300px; 22 | width: 0; 23 | height: 100%; 24 | margin-left: -300px; 25 | overflow-y: auto; 26 | background: #121212; 27 | color: #999; 28 | -webkit-transition: all 0.5s ease; 29 | -moz-transition: all 0.5s ease; 30 | -o-transition: all 0.5s ease; 31 | transition: all 0.5s ease; 32 | } 33 | 34 | #wrapper 35 | { 36 | padding-left: 300px; 37 | } 38 | 39 | #wrapper.active 40 | { 41 | padding-left: 0; 42 | } 43 | 44 | #timer-wrapper 45 | { 46 | width: 300px; 47 | } 48 | 49 | 50 | #wrapper.active #timer-wrapper 51 | { 52 | width: 300px; 53 | } 54 | 55 | #page-content-wrapper 56 | { 57 | width: 100%; 58 | position: absolute; 59 | padding: 15px; 60 | } 61 | 62 | #wrapper.active #page-content-wrapper 63 | { 64 | position: absolute; 65 | margin-right: -300px; 66 | } 67 | 68 | /* Screen size is more than 768 pixels -> we show the timer by default */ 69 | @media(min-width:768px) { 70 | 71 | #wrapper.active #timer-wrapper 72 | { 73 | width: 0; 74 | } 75 | 76 | #page-content-wrapper 77 | { 78 | padding: 20px; 79 | position: relative; 80 | } 81 | 82 | #wrapper.active #page-content-wrapper 83 | { 84 | position: relative; 85 | margin-right: 0; 86 | } 87 | } 88 | 89 | .text-justify 90 | { 91 | text-align: justify; 92 | } 93 | 94 | /* Timer Styles */ 95 | 96 | #timer-wrapper 97 | { 98 | font-family: "Roboto"; 99 | } 100 | 101 | #timer-wrapper .timer-title 102 | { 103 | padding: 0px 20px; 104 | padding-right: 80px; 105 | height: 65px; 106 | width: 300px; 107 | position: relative; 108 | 109 | line-height: 65px; 110 | } 111 | 112 | #timer-wrapper .timer-title #run-count 113 | { 114 | display: block; 115 | position: absolute; 116 | height: 65px; 117 | width: 60px; 118 | right: 10px; 119 | top: 0px; 120 | 121 | text-align: right; 122 | line-height: 65px; 123 | } 124 | 125 | #timer-wrapper .timer-title span 126 | { 127 | display: inline-block; 128 | 129 | line-height: 1.3em; 130 | vertical-align: middle; 131 | } 132 | 133 | #timer-splits, 134 | #timer-wrapper .timer-title, 135 | #run-globals 136 | { 137 | width: 100%; 138 | padding: 0px 20px; 139 | border-collapse: separate; 140 | } 141 | 142 | #timer-splits 143 | { 144 | position: relative; 145 | top: 0px; 146 | } 147 | 148 | #timer-splits-container 149 | { 150 | min-height: 200px; 151 | overflow: hidden; 152 | 153 | border-top: 1px solid #444; 154 | border-bottom: 1px solid #444; 155 | } 156 | 157 | #timer-splits tr.current 158 | { 159 | position: relative; 160 | background: #222; 161 | color: white; 162 | } 163 | 164 | #timer-splits tr td 165 | { 166 | text-align: right; 167 | } 168 | 169 | #timer-splits tr td:first-child 170 | { 171 | text-align: left; 172 | } 173 | 174 | #timer-splits tr td.time 175 | { 176 | border-right: 10px solid transparent; 177 | } 178 | 179 | .time.ahead.split-ahead { color: #15BC33; } 180 | .time.ahead.split-late { color: #AEE711; } 181 | .time.late.split-ahead { color: #E75811; } 182 | .time.late.split-late { color: #E71111; } 183 | .time.split-gold { color: #FFDF00; } 184 | 185 | #timer-splits tr td:first-child, 186 | #run-globals tr td:first-child 187 | { 188 | width: 100%; 189 | } 190 | 191 | #run-globals 192 | { 193 | margin-top: 10px; 194 | } 195 | 196 | #run-globals tr td.time 197 | { 198 | border: 0px; 199 | text-align: right; 200 | } 201 | 202 | #run-globals #global-time 203 | { 204 | font-size: 48px; 205 | font-weight: bold; 206 | text-align: right; 207 | } 208 | 209 | #timer-split-handle 210 | { 211 | color: transparent; 212 | background-color: transparent; 213 | } 214 | 215 | #timer-split-handle:hover 216 | { 217 | background-color: #444; 218 | color: #888; 219 | } 220 | 221 | #timer-split-handle:active 222 | { 223 | background-color: transparent; 224 | color: transparent; 225 | } 226 | 227 | /* Timer handle, styled in timer style */ 228 | 229 | #timer-split-handle 230 | { 231 | position: relative; 232 | top: -1px; 233 | display: block; 234 | width: 100%; 235 | height: 3px; 236 | float: left; 237 | z-index: 10; 238 | 239 | cursor: ns-resize; 240 | font-size: 0px; 241 | line-height: 0px; 242 | text-align: center; 243 | font-weight: bold; 244 | 245 | transition: all 0.1s linear; 246 | transition-property: background-color, color, height, top, font-size, line-height; 247 | } 248 | 249 | #timer-split-handle:hover, 250 | #timer-split-handle:active 251 | { 252 | height: 10px; 253 | top: -5px; 254 | font-size: 16px; 255 | line-height: 11px; 256 | } 257 | 258 | /* Page Styles */ 259 | 260 | .page 261 | { 262 | display: none; 263 | } 264 | 265 | .page.active 266 | { 267 | display: block; 268 | } 269 | 270 | .inline-block 271 | { 272 | display: inline-block; 273 | } 274 | 275 | .button-bar 276 | { 277 | margin-top: 20px; 278 | } 279 | 280 | .button-bar .well 281 | { 282 | margin-right: 20px; 283 | } 284 | 285 | .button-bar .btn 286 | { 287 | display: inline-block; 288 | height: 125px; 289 | width: 125px; 290 | position: relative; 291 | 292 | line-height: 100px; 293 | } 294 | 295 | .button-bar .btn span 296 | { 297 | display: block; 298 | text-align: center; 299 | position: absolute; 300 | bottom: 6px; 301 | left: 12px; 302 | right: 12px; 303 | 304 | line-height: 1.42857; 305 | } 306 | 307 | .button-bar .btn i 308 | { 309 | font-size: 32px; 310 | } 311 | 312 | .split-gold { 313 | -webkit-animation: goldanimation 1s ease; 314 | -moz-animation: goldanimation 1s ease; 315 | -o-animation: goldanimation 1s ease; 316 | } 317 | 318 | @-webkit-keyframes goldanimation { 319 | 0% { background-color: rgba(255, 224, 0, 0); } 320 | 1% { background-color: rgba(255, 224, 0, 1); } 321 | 100% { background-color: rgba(255, 224, 0, 0); } 322 | } 323 | 324 | @-moz-keyframes goldanimation { 325 | 0% { background-color: rgba(255, 224, 0, 0); } 326 | 1% { background-color: rgba(255, 224, 0, 1); } 327 | 100% { background-color: rgba(255, 224, 0, 0); } 328 | } 329 | 330 | @-o-keyframes goldanimation { 331 | 0% { background-color: rgba(255, 224, 0, 0); } 332 | 1% { background-color: rgba(255, 224, 0, 1); } 333 | 100% { background-color: rgba(255, 224, 0, 0); } 334 | } 335 | 336 | h1, h2, h3 337 | { 338 | padding-bottom: 10px; 339 | border-bottom: 1px solid #eee; 340 | } 341 | 342 | div.crouton-notification 343 | { 344 | position: absolute; 345 | top: 0px; 346 | left: 0px; 347 | 348 | z-index: 10000; 349 | } 350 | 351 | div.crouton-confirm { background-color: #dff0d8; color: #3c763d; border-color: #d6e9c6; } 352 | div.crouton-error { background-color: #f2dede; color: #a94442; border-color: #ebccd1; } 353 | div.crouton-info { background-color: #d9edf7; color: #31708f; border-color: #bce8f1; } 354 | 355 | .my-tooltip 356 | { 357 | display: inline-block; 358 | position: relative; 359 | } 360 | 361 | .my-tooltip-content 362 | { 363 | display: none; 364 | } 365 | 366 | .my-tooltip-content.active 367 | { 368 | display: block; 369 | position: absolute; 370 | top: 6px; 371 | left: 12px; 372 | padding: 5px; 373 | width: 200px; 374 | font-size: 11px; 375 | line-height: 14px; 376 | white-space: normal; 377 | 378 | border-radius: 3px; 379 | color: white; 380 | background: rgba(0, 0, 0, 1); 381 | 382 | z-index: 10; 383 | } -------------------------------------------------------------------------------- /css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.2 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default:disabled,.btn-default[disabled]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary:disabled,.btn-primary[disabled]{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success:disabled,.btn-success[disabled]{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info:disabled,.btn-info[disabled]{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning:disabled,.btn-warning[disabled]{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger:disabled,.btn-danger[disabled]{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} -------------------------------------------------------------------------------- /css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.2 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default, 8 | .btn-primary, 9 | .btn-success, 10 | .btn-info, 11 | .btn-warning, 12 | .btn-danger { 13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 16 | } 17 | .btn-default:active, 18 | .btn-primary:active, 19 | .btn-success:active, 20 | .btn-info:active, 21 | .btn-warning:active, 22 | .btn-danger:active, 23 | .btn-default.active, 24 | .btn-primary.active, 25 | .btn-success.active, 26 | .btn-info.active, 27 | .btn-warning.active, 28 | .btn-danger.active { 29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 31 | } 32 | .btn-default .badge, 33 | .btn-primary .badge, 34 | .btn-success .badge, 35 | .btn-info .badge, 36 | .btn-warning .badge, 37 | .btn-danger .badge { 38 | text-shadow: none; 39 | } 40 | .btn:active, 41 | .btn.active { 42 | background-image: none; 43 | } 44 | .btn-default { 45 | text-shadow: 0 1px 0 #fff; 46 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 47 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 48 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 49 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 50 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 51 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 52 | background-repeat: repeat-x; 53 | border-color: #dbdbdb; 54 | border-color: #ccc; 55 | } 56 | .btn-default:hover, 57 | .btn-default:focus { 58 | background-color: #e0e0e0; 59 | background-position: 0 -15px; 60 | } 61 | .btn-default:active, 62 | .btn-default.active { 63 | background-color: #e0e0e0; 64 | border-color: #dbdbdb; 65 | } 66 | .btn-default.disabled, 67 | .btn-default:disabled, 68 | .btn-default[disabled] { 69 | background-color: #e0e0e0; 70 | background-image: none; 71 | } 72 | .btn-primary { 73 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); 74 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); 75 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); 76 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); 77 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); 78 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 79 | background-repeat: repeat-x; 80 | border-color: #245580; 81 | } 82 | .btn-primary:hover, 83 | .btn-primary:focus { 84 | background-color: #265a88; 85 | background-position: 0 -15px; 86 | } 87 | .btn-primary:active, 88 | .btn-primary.active { 89 | background-color: #265a88; 90 | border-color: #245580; 91 | } 92 | .btn-primary.disabled, 93 | .btn-primary:disabled, 94 | .btn-primary[disabled] { 95 | background-color: #265a88; 96 | background-image: none; 97 | } 98 | .btn-success { 99 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 100 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 101 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 102 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 103 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 104 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 105 | background-repeat: repeat-x; 106 | border-color: #3e8f3e; 107 | } 108 | .btn-success:hover, 109 | .btn-success:focus { 110 | background-color: #419641; 111 | background-position: 0 -15px; 112 | } 113 | .btn-success:active, 114 | .btn-success.active { 115 | background-color: #419641; 116 | border-color: #3e8f3e; 117 | } 118 | .btn-success.disabled, 119 | .btn-success:disabled, 120 | .btn-success[disabled] { 121 | background-color: #419641; 122 | background-image: none; 123 | } 124 | .btn-info { 125 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 126 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 127 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 128 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 129 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 130 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 131 | background-repeat: repeat-x; 132 | border-color: #28a4c9; 133 | } 134 | .btn-info:hover, 135 | .btn-info:focus { 136 | background-color: #2aabd2; 137 | background-position: 0 -15px; 138 | } 139 | .btn-info:active, 140 | .btn-info.active { 141 | background-color: #2aabd2; 142 | border-color: #28a4c9; 143 | } 144 | .btn-info.disabled, 145 | .btn-info:disabled, 146 | .btn-info[disabled] { 147 | background-color: #2aabd2; 148 | background-image: none; 149 | } 150 | .btn-warning { 151 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 152 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 153 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 154 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 155 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 156 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 157 | background-repeat: repeat-x; 158 | border-color: #e38d13; 159 | } 160 | .btn-warning:hover, 161 | .btn-warning:focus { 162 | background-color: #eb9316; 163 | background-position: 0 -15px; 164 | } 165 | .btn-warning:active, 166 | .btn-warning.active { 167 | background-color: #eb9316; 168 | border-color: #e38d13; 169 | } 170 | .btn-warning.disabled, 171 | .btn-warning:disabled, 172 | .btn-warning[disabled] { 173 | background-color: #eb9316; 174 | background-image: none; 175 | } 176 | .btn-danger { 177 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 178 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 179 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 180 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 181 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 182 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 183 | background-repeat: repeat-x; 184 | border-color: #b92c28; 185 | } 186 | .btn-danger:hover, 187 | .btn-danger:focus { 188 | background-color: #c12e2a; 189 | background-position: 0 -15px; 190 | } 191 | .btn-danger:active, 192 | .btn-danger.active { 193 | background-color: #c12e2a; 194 | border-color: #b92c28; 195 | } 196 | .btn-danger.disabled, 197 | .btn-danger:disabled, 198 | .btn-danger[disabled] { 199 | background-color: #c12e2a; 200 | background-image: none; 201 | } 202 | .thumbnail, 203 | .img-thumbnail { 204 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 205 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 206 | } 207 | .dropdown-menu > li > a:hover, 208 | .dropdown-menu > li > a:focus { 209 | background-color: #e8e8e8; 210 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 211 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 212 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 213 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 214 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 215 | background-repeat: repeat-x; 216 | } 217 | .dropdown-menu > .active > a, 218 | .dropdown-menu > .active > a:hover, 219 | .dropdown-menu > .active > a:focus { 220 | background-color: #2e6da4; 221 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 222 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 223 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 224 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 225 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 226 | background-repeat: repeat-x; 227 | } 228 | .navbar-default { 229 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 230 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 231 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 232 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 233 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 234 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 235 | background-repeat: repeat-x; 236 | border-radius: 4px; 237 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 238 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 239 | } 240 | .navbar-default .navbar-nav > .open > a, 241 | .navbar-default .navbar-nav > .active > a { 242 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 243 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 244 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); 245 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); 246 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); 247 | background-repeat: repeat-x; 248 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 249 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 250 | } 251 | .navbar-brand, 252 | .navbar-nav > li > a { 253 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 254 | } 255 | .navbar-inverse { 256 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 257 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 258 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 259 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 260 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 261 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 262 | background-repeat: repeat-x; 263 | } 264 | .navbar-inverse .navbar-nav > .open > a, 265 | .navbar-inverse .navbar-nav > .active > a { 266 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); 267 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); 268 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); 269 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); 270 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); 271 | background-repeat: repeat-x; 272 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 273 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 274 | } 275 | .navbar-inverse .navbar-brand, 276 | .navbar-inverse .navbar-nav > li > a { 277 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 278 | } 279 | .navbar-static-top, 280 | .navbar-fixed-top, 281 | .navbar-fixed-bottom { 282 | border-radius: 0; 283 | } 284 | @media (max-width: 767px) { 285 | .navbar .navbar-nav .open .dropdown-menu > .active > a, 286 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, 287 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { 288 | color: #fff; 289 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 290 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 291 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 292 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 293 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 294 | background-repeat: repeat-x; 295 | } 296 | } 297 | .alert { 298 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 299 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 300 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 301 | } 302 | .alert-success { 303 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 304 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 305 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 306 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 307 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 308 | background-repeat: repeat-x; 309 | border-color: #b2dba1; 310 | } 311 | .alert-info { 312 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 313 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 314 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 315 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 316 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 317 | background-repeat: repeat-x; 318 | border-color: #9acfea; 319 | } 320 | .alert-warning { 321 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 322 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 323 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 324 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 325 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 326 | background-repeat: repeat-x; 327 | border-color: #f5e79e; 328 | } 329 | .alert-danger { 330 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 331 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 332 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 333 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 334 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 335 | background-repeat: repeat-x; 336 | border-color: #dca7a7; 337 | } 338 | .progress { 339 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 340 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 342 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 344 | background-repeat: repeat-x; 345 | } 346 | .progress-bar { 347 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); 348 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); 349 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); 350 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); 351 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); 352 | background-repeat: repeat-x; 353 | } 354 | .progress-bar-success { 355 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 356 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 357 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 358 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 359 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 360 | background-repeat: repeat-x; 361 | } 362 | .progress-bar-info { 363 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 364 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 365 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 366 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 367 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 368 | background-repeat: repeat-x; 369 | } 370 | .progress-bar-warning { 371 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 372 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 373 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 374 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 375 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 376 | background-repeat: repeat-x; 377 | } 378 | .progress-bar-danger { 379 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 380 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 381 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 382 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 383 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 384 | background-repeat: repeat-x; 385 | } 386 | .progress-bar-striped { 387 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 388 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 389 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 390 | } 391 | .list-group { 392 | border-radius: 4px; 393 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 394 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 395 | } 396 | .list-group-item.active, 397 | .list-group-item.active:hover, 398 | .list-group-item.active:focus { 399 | text-shadow: 0 -1px 0 #286090; 400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); 401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); 403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); 405 | background-repeat: repeat-x; 406 | border-color: #2b669a; 407 | } 408 | .list-group-item.active .badge, 409 | .list-group-item.active:hover .badge, 410 | .list-group-item.active:focus .badge { 411 | text-shadow: none; 412 | } 413 | .panel { 414 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 415 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 416 | } 417 | .panel-default > .panel-heading { 418 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 419 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 420 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 421 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 422 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 423 | background-repeat: repeat-x; 424 | } 425 | .panel-primary > .panel-heading { 426 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 427 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 428 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 429 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 430 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 431 | background-repeat: repeat-x; 432 | } 433 | .panel-success > .panel-heading { 434 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 435 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 436 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 437 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 438 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 439 | background-repeat: repeat-x; 440 | } 441 | .panel-info > .panel-heading { 442 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 443 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 444 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 445 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 446 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 447 | background-repeat: repeat-x; 448 | } 449 | .panel-warning > .panel-heading { 450 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 451 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 453 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 455 | background-repeat: repeat-x; 456 | } 457 | .panel-danger > .panel-heading { 458 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 459 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 461 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 463 | background-repeat: repeat-x; 464 | } 465 | .well { 466 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 467 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 469 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 471 | background-repeat: repeat-x; 472 | border-color: #dcdcdc; 473 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 474 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 475 | } 476 | /*# sourceMappingURL=bootstrap-theme.css.map */ 477 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Splitty - Simple web time splitting software 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 |
37 |
38 | 0 39 | 40 | - 41 | 42 |
43 | 44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 |
--
52 |
53 |
•••
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
Previous segment-
Sum of Bests-
0:00.0
67 | 70 |
71 | 72 | 73 | 74 |
75 |
76 |
77 |
78 |

Splitty

79 |
80 |
81 |

82 | Splitty is a tiny full HTML5-JS speedrun timer that you can run in your browser. 83 | It'll allow to time your runs, create splits, 84 | measure your performance compared to previous runs in real time and more to come! 85 |

86 |

87 | To begin creating a new timer, click on the New Timer button.
88 | If you already have a saved one, click on the Load Timer button.
89 | If you just changed browsers and have a saved timer on your hard drive, you can import it using the Import timer button.
90 | Finally, you can change your global settings using the Settings button. 91 |

92 |
93 |
94 | 95 |
96 | 122 |
123 |
124 |
125 |

Import timer

126 |
127 |
128 | 129 |
130 | 131 |
132 |
133 |
134 |
135 |   Import 136 | Cancel 137 |
138 |
139 |
140 |
141 |

Load timer

142 |
143 |
144 | 145 |
146 | 149 |
150 |
151 | 152 |
153 |
154 |
155 |
156 |   Load 157 | Cancel 158 |
159 |
160 |
161 |
162 |

Edit timer

163 |
164 | 165 |
166 | 167 |
168 | 169 | 170 | 171 | 172 | 173 | The name shown in the list of your saved timers. It will not be shown in the timer view. 174 | 175 | 176 | 177 |
178 |
179 | 180 |
181 | 182 |
183 | 184 | 185 | 186 | 187 | 188 | The name that will be shown on the top of the timer. Usually, the game name with the run category. 189 | 190 | 191 | 192 |
193 |
194 | 195 |
196 | 197 |
198 |
199 | 200 | 201 |
202 |
203 |
204 | 205 |
206 | 207 |
208 | 209 | 210 | 211 | 212 | 213 | If set, this will be the time before the timer actually starts when the start button is pressed. 214 | 215 | 216 | 217 |
218 |
219 | 220 |
221 | Splits 222 | 223 | 224 | 246 | 247 |
248 |
249 | 250 |
251 |
252 | 253 |
254 |
255 | 256 |
257 | 268 |
269 | 270 | Add split 271 | 272 |
273 |
274 |
275 | Save 276 | Cancel 277 |
278 |
279 |
280 |
281 |
282 |

Timer control

283 |
284 |
285 |
286 |
287 |
288 | 294 | 300 | 306 | 312 | 338 |
339 |
340 |
341 |
342 |

343 | Now you've created or loaded your timer, and you're ready to play ! 344 | Just click on the Start button below to start the timer. 345 | Then, you have 3 possible actions: 346 |

347 | 348 |
    349 |
  • Tick time for a split (the most common thing you'll do)
  • 350 |
  • Navigate splits (if you split too much or if you have to skip a split)
  • 351 |
  • Reset the timer (if you restart your run)
  • 352 |
353 | 354 |

355 | With these actions, you should be able to manage your run quite easily. 356 | Of course, if you're running, aiming and clicking on buttons is quite hard. To address this issue, you can also 357 | use keyboard shortcuts to do these actions: 358 |

359 | 360 |
    361 |
  • Use Space to start/split
  • 362 |
  • Use Down arrow to skip
  • 363 |
  • Use Up arrow to go back
  • 364 |
  • Use Backspace to reset
  • 365 |
366 |
367 |
368 |
369 |
370 |

Settings

371 | 372 |

373 | Here are the settings that don't rely on the timer, and will work during all of your timer usage. 374 |

375 | 376 |

Gamepad settings

377 |

378 | You can use some buttons of your gamepad through Splitty to split during your runs if you're playing on emulator. 379 | This has been made because since the timer is in a browser, it's impossible to split while being on the emulator window. 380 | Of course, these buttons are configurable, and you can select which controller you'll be using, in case you have more than one. 381 |

382 | 383 |
384 |
385 |
386 |
387 | 390 |
391 | 392 | 393 | 396 |
397 | 398 |
399 |

Button bindings

400 |
401 | 402 |
403 | 404 | 405 | 406 | 407 |
408 |
409 |
410 | 411 |
412 | 413 | 414 | 415 | 416 |
417 |
418 |
419 |
420 |
421 |
422 | 423 |
424 | Save 425 | Discard 426 |
427 |
428 |
429 |
430 |
431 | 432 | 433 |
434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | -------------------------------------------------------------------------------- /js/actions.js: -------------------------------------------------------------------------------- 1 | function Actions() 2 | { 3 | Actions.instance = this; 4 | this.updates = []; 5 | this.key_down = false; 6 | this.table_pos = null; 7 | this.split_scroll_status = 0; 8 | 9 | // Temporary settings when editing them 10 | this.current_settings = null; 11 | } 12 | 13 | // Singleton implementation 14 | Actions.instance = null; 15 | Actions.get_manager = function() 16 | { 17 | return Actions.instance; 18 | } 19 | 20 | // Binders 21 | Actions.bind_element_action = function(el) 22 | { 23 | var callback = el.dataset.action; 24 | if(this[callback]) 25 | { 26 | var event = 'click'; 27 | 28 | // Special events on actions for some element types 29 | switch(el.tagName.toLowerCase()) 30 | { 31 | case 'select': 32 | event = 'change'; 33 | break; 34 | } 35 | 36 | // Binding the event 37 | el.addEventListener(event, this[callback].bind(this, el)); 38 | } 39 | } 40 | 41 | Actions.bind_element_page = function(el) 42 | { 43 | var destination = el.dataset.page; 44 | el.addEventListener('click', this.load_page.bind(this, destination)); 45 | } 46 | 47 | // Sets events and triggers on buttons 48 | Actions.prototype.init = function() 49 | { 50 | $("[data-action]").each(Actions.bind_element_action, this); 51 | $("[data-page]").each(Actions.bind_element_page, this); 52 | 53 | $(document).on("keydown", this.handle_keydown.bind(this)); 54 | $(document).on("keyup", this.handle_keyup.bind(this)); 55 | 56 | this.table_pos = q("#timer-splits-container").getBoundingClientRect(); 57 | var drag_handle_evt = (function(event) 58 | { 59 | var y = typeof(event.y) == "undefined" ? event.clientY : event.y; 60 | 61 | var height = Math.max(this.table_pos.height, y - (this.table_pos.top + document.body.scrollTop)); 62 | $("#timer-splits-container").css("height", height + "px" ); 63 | 64 | this.save_handle_position(height); 65 | }).bind(this); 66 | 67 | // Split size handle 68 | $("#timer-split-handle").on("mousedown",(function(e) 69 | { 70 | $(document).on("mousemove", drag_handle_evt); 71 | e.preventDefault(); 72 | }).bind(this)); 73 | 74 | $(document).on("mouseup", (function() 75 | { 76 | $(document).off("mousemove", drag_handle_evt); 77 | }).bind(this)); 78 | 79 | $("#timer-split-handle").on("dblclick",(function(ev) 80 | { 81 | var auto_height = q("#timer-splits").clientHeight; 82 | $("#timer-splits-container").css("height", auto_height + "px"); 83 | this.save_handle_position(auto_height); 84 | }).bind(this)); 85 | 86 | //Initializing split zone height 87 | this.load_handle_position(); 88 | 89 | //Initializing scroll 90 | var wheel_event = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel" 91 | document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel" 92 | "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox 93 | 94 | $("#timer-splits-container").on(wheel_event, this.on_scroll_splits.bind(this)); 95 | 96 | //Load the gamepad manager 97 | this.gamepad = new Gamepad(); 98 | this.gamepad.events.on('gamepadconnected', this.update_gamepads.bind(this)); 99 | this.gamepad.events.on('gamepaddisconnected', this.update_gamepads.bind(this)); 100 | this.gamepad.events.on('buttonpressed', this.handle_gamepad_press.bind(this)); 101 | this.gamepad.events.on('buttonpressed', this.settings_gamepad_handle_press.bind(this)); 102 | 103 | // Listen for gamepad button presses for splitting and things only when the page is on timer control 104 | $(document).on('pagechanged', function() 105 | { 106 | if(this.get_page() == "timer-control" && this.current_settings["use_gamepad"] === true) 107 | this.gamepad.start_polling(); 108 | else 109 | this.gamepad.stop_polling(); 110 | }.bind(this)); 111 | 112 | this.refresh_timer_list(); 113 | this.refresh_settings(); 114 | } 115 | 116 | Actions.prototype.on_scroll_splits = function(ev) 117 | { 118 | var top = parseInt($("#timer-splits").css('top')) || 0; 119 | 120 | var current_split_height = $("#timer-splits tr")[this.split_scroll_status].clientHeight; 121 | 122 | if(ev.deltaY < 0 && this.split_scroll_status > 0) 123 | this.split_scroll_status--; 124 | else if(ev.deltaY > 0 && this.split_scroll_status < $("#timer-splits tr").length - 1) 125 | this.split_scroll_status++; 126 | 127 | $("#timer-splits").css('top', "-" + $("#timer-splits tr")[this.split_scroll_status].offsetTop + 'px'); 128 | 129 | if(this.split_scroll_status == 0) 130 | $("#timer-splits").css('top', "0px"); 131 | 132 | ev.preventDefault(); 133 | } 134 | 135 | Actions.prototype.save_handle_position = function(new_height) 136 | { 137 | if(Storage.enabled()) 138 | { 139 | this.current_settings['handle_position'] = new_height; 140 | Storage.get().set_settings_property('handle_position', new_height); 141 | } 142 | } 143 | 144 | Actions.prototype.load_handle_position = function() 145 | { 146 | if(Storage.enabled()) 147 | { 148 | var handle_position = Storage.get().get_settings_property('handle_position'); 149 | 150 | if(handle_position) 151 | $("#timer-splits-container").css("height", handle_position + "px" ); 152 | } 153 | } 154 | 155 | // Allows a child object to register for periodic timer updates 156 | Actions.prototype.register_updates = function(object) 157 | { 158 | if(this.updates.indexOf(object) != -1) 159 | return false; 160 | 161 | this.updates.push(object); 162 | 163 | // Start the animationFrame loop if this is the first object we register updates for 164 | if(this.updates.length == 1) 165 | window.requestAnimationFrame(this.update.bind(this)); 166 | 167 | return true; 168 | } 169 | 170 | Actions.prototype.unregister_updates = function(object) 171 | { 172 | var index = this.updates.indexOf(object); 173 | 174 | if(index == -1) 175 | return false; 176 | 177 | this.updates.splice(index, 1); 178 | 179 | return true; 180 | } 181 | 182 | Actions.prototype.update = function(elapsed, set_split_time) 183 | { 184 | set_split_time = typeof set_split_time != "undefined" ? set_split_time : false; 185 | 186 | if(this.updates.length > 0) 187 | window.requestAnimationFrame(this.update.bind(this)); 188 | else if(elapsed) // Allow manual calls, having elapsed at null, to continue 189 | return; 190 | 191 | for(var update in this.updates) 192 | this.updates[update].update(); 193 | 194 | //Update the gui here 195 | if(window.current_run) 196 | { 197 | var rel_split = null; 198 | if(window.current_timer.splits[window.current_run.current_split].pb_split) 199 | rel_split = window.current_run.elapsed - window.current_timer.splits[window.current_run.current_split].pb_split; 200 | 201 | final_time = !window.current_run.started; // Are we showing the final time or not 202 | $("#global-time").html(window.current_run.get_time(true, final_time)); 203 | 204 | if(rel_split && (rel_split > 0 || set_split_time)) 205 | { 206 | var el = $($("#timer-splits tr")[window.current_run.current_split].querySelector(".time")); 207 | el.html(msec_to_string(rel_split, true, 1, true)); 208 | } 209 | 210 | if(set_split_time) 211 | { 212 | var el = $($("#timer-splits tr")[window.current_run.current_split].querySelector(".time")); 213 | $($("#timer-splits tr")[window.current_run.current_split].querySelector(".ref")).html(msec_to_string(window.current_run.elapsed, true, 0)); 214 | 215 | var difference = window.current_run.split_times[window.current_run.current_split] - window.current_timer.splits[window.current_run.current_split].pb_duration; 216 | 217 | if(window.current_run.current_split > 0) 218 | difference -= window.current_run.split_times[window.current_run.current_split - 1]; 219 | 220 | 221 | var split_time = window.current_run.split_times[window.current_run.current_split]; 222 | if(window.current_run.current_split > 0) 223 | split_time -= window.current_run.split_times[window.current_run.current_split - 1]; 224 | 225 | var classes = ""; 226 | 227 | var classes = "time"; 228 | if(rel_split > 0) 229 | classes += " late"; 230 | else if(rel_split < 0) 231 | classes += " ahead"; 232 | 233 | if(window.current_timer.splits[window.current_run.current_split].split_best == null || split_time < window.current_timer.splits[window.current_run.current_split].split_best) 234 | classes = "time split-gold"; 235 | else if(split_time < window.current_timer.splits[window.current_run.current_split].pb_duration) 236 | classes += " split-ahead"; 237 | else 238 | classes += " split-late"; 239 | 240 | var rel_human = msec_to_time(difference, 1); 241 | var rel_str = ""; 242 | 243 | if(window.current_timer.splits[window.current_run.current_split].pb_duration != null) 244 | rel_str = difference > 0 ? "+" : "-"; 245 | 246 | if(rel_human.hr > 0) 247 | rel_str += rel_human.hr + ":" + (rel_human.mn < 10 ? "0" : ""); 248 | if(rel_human.mn > 0) 249 | rel_str += rel_human.mn + ":" + (rel_human.sec < 10 ? "0" : "") + rel_human.sec; 250 | else 251 | rel_str += rel_human.sec + "." + "" + rel_human.ms + ""; 252 | 253 | el[0].className = classes; 254 | q("#previous-segment").className = classes; 255 | $("#previous-segment").html(rel_str); 256 | } 257 | } 258 | } 259 | 260 | Actions.prototype.update_sob = function() 261 | { 262 | var sum_of_bests = 0; 263 | 264 | console.log("Refreshing Sum of Bests"); 265 | for(var i in window.current_timer.splits) 266 | { 267 | var best_split = window.current_timer.splits[i].split_best; 268 | if(window.current_run) 269 | best_split = window.current_run.best_splits[i]; 270 | 271 | console.log("Split '" + window.current_timer.splits[i].name + "': " + best_split); 272 | if(sum_of_bests != null && best_split) 273 | sum_of_bests += best_split; 274 | else 275 | sum_of_bests = null; 276 | } 277 | 278 | if(sum_of_bests != null) 279 | $("#sum-of-bests").html(msec_to_string(sum_of_bests)); 280 | else 281 | $("#sum-of-bests").html("-"); 282 | } 283 | 284 | Actions.prototype.load_page = function(page) 285 | { 286 | var selector = "#page-" + page; 287 | if($(selector)) 288 | { 289 | $(".page").removeClass("active"); 290 | $(selector).addClass("active"); 291 | $(document).trigger('pagechanged', page); 292 | } 293 | } 294 | 295 | Actions.prototype.get_page = function() 296 | { 297 | return $(".page.active").attr("id").substr(5); 298 | } 299 | 300 | Actions.prototype.load_timer = function(timer) 301 | { 302 | window.current_timer = timer; 303 | 304 | //Setting timer title 305 | $("#run-title").text(timer.run_name); 306 | $("#run-count").text(timer.run_count); 307 | 308 | //Setting global time 309 | $("#global-time").html(msec_to_string(-1 * timer.start_delay, true, 1)); 310 | 311 | $("#timer-splits tr").remove(); 312 | for(var i in timer.splits) 313 | { 314 | var new_line = document.createElement("tr"); 315 | var new_cell_name = document.createElement("td"); 316 | var new_cell_time = document.createElement("td"); 317 | var new_cell_ref = document.createElement("td"); 318 | 319 | new_cell_name.innerHTML = timer.splits[i].name; 320 | new_cell_time.classList.add("time"); 321 | new_cell_ref.classList.add("ref"); 322 | 323 | if(timer.splits[i].pb_split) 324 | { 325 | var htime = msec_to_string(timer.splits[i].pb_split, false, 0); 326 | new_cell_ref.innerHTML = htime; 327 | } 328 | else 329 | new_cell_ref.innerHTML = "-"; 330 | 331 | new_line.appendChild(new_cell_name); 332 | new_line.appendChild(new_cell_time); 333 | new_line.appendChild(new_cell_ref); 334 | 335 | $("#timer-splits").append($(new_line)); 336 | 337 | //Export button 338 | $("#control-button-export").attr('href', 'data:text/plain;charset=utf8,' + encodeURIComponent(timer.to_string())); 339 | $("#control-button-export").attr('download', timer.run_name.replace('/', '-') + ".json"); 340 | } 341 | 342 | this.update_sob(); 343 | } 344 | 345 | Actions.prototype.load_empty_timer = function() 346 | { 347 | $("#run-title").text("-"); 348 | $("#run-count").text("0"); 349 | 350 | //Setting global time 351 | $("#global-time").html("0:00.0"); 352 | 353 | $("#timer-splits tr").remove(); 354 | 355 | var new_line = document.createElement("tr"); 356 | var new_cell_name = document.createElement("td"); 357 | var new_cell_time = document.createElement("td"); 358 | var new_cell_ref = document.createElement("td"); 359 | 360 | new_cell_name.innerHTML = "-"; 361 | new_cell_time.innerHTML = ""; 362 | new_cell_ref.innerHTML = "-"; 363 | new_cell_time.classList.add("time"); 364 | new_cell_ref.classList.add("ref"); 365 | 366 | new_line.appendChild(new_cell_name); 367 | new_line.appendChild(new_cell_time); 368 | new_line.appendChild(new_cell_ref); 369 | 370 | $("#timer-splits").append($(new_line)); 371 | } 372 | 373 | Actions.prototype.refresh_timer_list = function() 374 | { 375 | if(Storage.enabled()) 376 | { 377 | $("#form-load-timer-timer-name option").remove(); 378 | 379 | var new_line = document.createElement("option"); 380 | new_line.value = ""; 381 | new_line.innerHTML = "---"; 382 | 383 | q("#form-load-timer-timer-name").appendChild(new_line); 384 | 385 | var names = Storage.get().get_names(); 386 | for(var k in names) 387 | { 388 | var option = $(new_line).clone(); 389 | option[0].value = k; 390 | option.text(names[k]); 391 | 392 | $("#form-load-timer-timer-name").append(option); 393 | } 394 | } 395 | } 396 | 397 | Actions.prototype.reset_timer_edit_form = function() 398 | { 399 | //Emptying all global info 400 | if(window.current_timer != null) 401 | { 402 | $("#form-edit-timer-name").val(window.current_timer.timer_name); 403 | $("#form-edit-game-name").val(window.current_timer.run_name); 404 | } 405 | else 406 | { 407 | $("#form-edit-timer-name").val(""); 408 | $("#form-edit-game-name").val(""); 409 | $("#form-edit-start-delay").val(""); 410 | } 411 | 412 | q("#form-edit-timer-type-manual").checked = false; 413 | q("#form-edit-timer-type-rta").checked = true; 414 | 415 | //Emptying splits 416 | var split_template = $("#edit-timer-split-template"); 417 | $(".timer-split").remove(); 418 | $("#form-edit-timer-split-list").prepend(split_template); 419 | } 420 | 421 | Actions.prototype.refresh_settings = function() 422 | { 423 | // Default settings array 424 | var default_settings = { 425 | 'handle_position': 0, 426 | 'use_gamepad': false, 427 | 'gamepad_id': null, 428 | 'gamepad_button_split': null, 429 | 'gamepad_button_reset':null 430 | }; 431 | 432 | // Merging default settings and saved ones, the saved ones being primary 433 | var saved_settings = Storage.get().get_settings(); 434 | this.current_settings = Object.assign(default_settings, saved_settings); 435 | 436 | if(this.current_settings['use_gamepad']) 437 | $("#settings-gamepad-use").attr('checked', true); 438 | 439 | this.gamepad.set_gamepad(this.current_settings.gamepad_id); 440 | 441 | this.update_gamepads(); 442 | } 443 | 444 | Actions.prototype.update_gamepads = function(event, id) 445 | { 446 | if(event) // Show message only if triggered from event 447 | { 448 | switch(event) 449 | { 450 | case 'gamepadconnected': 451 | Crouton.notify("Gamepad connected: " + Gamepad.get_name_from_id(id)); 452 | break; 453 | case 'gamepaddisconnected': 454 | Crouton.notify(CroutonType.ERROR, "Gamepad disconnected: " + Gamepad.get_name_from_id(id)); 455 | break; 456 | } 457 | } 458 | 459 | var gamepads = this.gamepad.connected_gamepads; 460 | var selected_gamepad_id = null; 461 | 462 | // Get currently selected gamepad and delete the list 463 | if(this.current_settings.gamepad_id) 464 | selected_gamepad_id = this.current_settings.gamepad_id; 465 | 466 | $("#settings-gamepad-id option").each(function(el) 467 | { 468 | q("#settings-gamepad-id").removeChild(el); 469 | }); 470 | 471 | var default_option = document.createElement("option"); 472 | default_option.setAttribute('value', ""); 473 | default_option.innerHTML = ""; 474 | q("#settings-gamepad-id").appendChild(default_option); 475 | 476 | for(var i in gamepads) 477 | { 478 | var pad = gamepads[i]; 479 | var pad_name = Gamepad.get_name_from_id(pad); 480 | 481 | var option = document.createElement("option"); 482 | option.setAttribute('value', pad); 483 | option.innerHTML = pad_name; 484 | option.selected = selected_gamepad_id === pad; 485 | q("#settings-gamepad-id").appendChild(option); 486 | } 487 | 488 | // If there's a gamepad selected, restore the buttons from the current settings 489 | var button_setting = null; 490 | $(".settings-gamepad-button").each(function(el) 491 | { 492 | if(q("#settings-gamepad-id option:checked").value) 493 | button_setting = "Button " + this.current_settings["gamepad_button_" + el.dataset.buttontype]; 494 | else 495 | button_setting = ""; 496 | el.value = button_setting; 497 | }.bind(this)); 498 | } 499 | 500 | // Registered actions, linked to buttons and everything, so-called "controllers" 501 | 502 | //// Timer edition //// 503 | 504 | Actions.prototype.edit_timer_add_split = function() 505 | { 506 | var split_template = $("#edit-timer-split-template").clone(); 507 | split_template.removeClass("hidden"); 508 | split_template.removeAttr("id"); 509 | 510 | $("#form-edit-timer-split-list .timer-split:last-of-type").after(split_template); 511 | 512 | $(".timer-split:last-of-type [data-action]").each(Actions.bind_element_action, this); 513 | } 514 | 515 | Actions.prototype.edit_timer_remove_split = function(el) 516 | { 517 | var parent = $(el).parents(".timer-split"); 518 | $(parent[0]).remove(); 519 | } 520 | 521 | Actions.prototype.edit_timer_move_up = function(el) 522 | { 523 | var parent = $(el).parents(".timer-split"); 524 | var previous_sibling = parent[0].previousSibling; 525 | 526 | if(!$(previous_sibling).is("#edit-timer-split-template")) 527 | $(previous_sibling).before(parent); 528 | } 529 | 530 | Actions.prototype.edit_timer_move_down = function(el) 531 | { 532 | var parent = $(el).parents(".timer-split"); 533 | var next_sibling = parent[0].nextSibling; 534 | 535 | if($(next_sibling).is('.timer-split')) 536 | $(next_sibling).after(parent); 537 | } 538 | 539 | Actions.prototype.edit_timer_submit = function() 540 | { 541 | var new_timer = null; 542 | 543 | // Check if editing timer or not 544 | if(window.current_timer) 545 | { 546 | new_timer = window.current_timer; 547 | new_timer.splits = []; // Empty splits to avoid duplication 548 | } 549 | else 550 | new_timer = new Timer(); 551 | 552 | // If nothing has been entered as start delay, just set 0 553 | if(q("#form-edit-start-delay").value.length == 0) 554 | q("#form-edit-start-delay").value = "0"; 555 | 556 | new_timer.timer_name = q("#form-edit-timer-name").value; 557 | new_timer.run_name = q("#form-edit-game-name").value; 558 | new_timer.start_delay = string_to_msec(q("#form-edit-start-delay").value); 559 | 560 | if(q("#form-edit-timer-type-rta").checked == true) 561 | new_timer.timer_type = Timer.Type.RTA; 562 | else if(q("#form-edit-timer-type-manual").checked == true) 563 | new_timer.timer_type = Timer.Type.MANUAL; 564 | 565 | var pb_elapsed = null; 566 | $("#form-edit-timer-split-list .timer-split").each(function(el) 567 | { 568 | if(!$(el).is(".hidden")) 569 | { 570 | var split_name = el.q(".split-name").value; 571 | var split_reference = el.q(".split-reference").value; 572 | var pb = null; 573 | var pb_duration = null; 574 | 575 | //Parsing PB time 576 | if (split_reference.length > 0) 577 | { 578 | pb = string_to_msec(split_reference); 579 | pb_duration = pb - pb_elapsed; 580 | pb_elapsed = pb; 581 | } 582 | 583 | //Creating the split 584 | if(split_name.length > 0) 585 | new_timer.splits.push({ name: split_name, pb_split: pb, pb_duration: pb_duration, split_best: pb_duration }); 586 | } 587 | }); 588 | 589 | if(new_timer.splits.length == 0) 590 | { 591 | alert("You have to create at least one split in the timer to be able to save it."); 592 | return false; 593 | } 594 | 595 | new_timer.save(); 596 | this.refresh_timer_list(); 597 | this.reset_timer_edit_form(); 598 | this.load_timer(new_timer); 599 | this.load_page('timer-control'); 600 | } 601 | 602 | Actions.prototype.edit_timer_cancel = function() 603 | { 604 | 605 | var confirmation = confirm("Stop editing ? Unsaved changes will be lost !"); 606 | if(confirmation) 607 | { 608 | $("#edit-timer-form input").each(function(el) 609 | { 610 | el.value = ""; 611 | }); 612 | 613 | $("#edit-timer-form .timer-split").each(function(el) 614 | { 615 | if(!$(el).is("#edit-timer-split-template")) 616 | { 617 | $(el).remove(); 618 | } 619 | }) 620 | 621 | this.edit_timer_add_split(); 622 | 623 | if(!window.current_timer) 624 | this.load_page("main-menu"); 625 | else 626 | this.load_page("timer-control"); 627 | } 628 | } 629 | 630 | Actions.prototype.load_timer_submit = function() 631 | { 632 | var select = q("#form-load-timer-timer-name"); 633 | var selected_timer = select.options[select.selectedIndex].value; 634 | 635 | var timer = Timer.load(selected_timer); 636 | this.load_timer(timer); 637 | this.load_page('timer-control'); 638 | } 639 | 640 | Actions.prototype.delete_timer = function() 641 | { 642 | var select = q("#form-load-timer-timer-name"); 643 | var selected_timer = select.options[select.selectedIndex].value; 644 | 645 | var confirm = window.confirm("Are you sure you want to delete this timer ?"); 646 | 647 | if(confirm) 648 | { 649 | var timer = Timer.load(selected_timer); 650 | timer.delete(); 651 | this.refresh_timer_list(); 652 | } 653 | 654 | return false; 655 | } 656 | 657 | Actions.prototype.import_timer_submit = function() 658 | { 659 | var file = q("#form-import-timer-file").files[0]; 660 | if (window.File && window.FileReader && window.FileList && window.Blob) 661 | { 662 | var r = new FileReader(); 663 | var that = this; 664 | 665 | r.onload = (function(e) 666 | { 667 | var contents = e.target.result; 668 | var timer = Timer.import_json(contents); 669 | this.load_timer(timer); 670 | this.timer_edit_timer(); 671 | }).bind(this); 672 | 673 | r.readAsText(file); 674 | } 675 | } 676 | 677 | //// Keybindings //// 678 | 679 | Actions.prototype.handle_keydown = function(ev) 680 | { 681 | if(!this.key_down) 682 | { 683 | if(this.get_page() == "timer-control") 684 | { 685 | switch(ev.keyCode) 686 | { 687 | case 32: //Space : start/split 688 | ev.preventDefault(); 689 | this.timer_start_split(); 690 | break; 691 | case 40: // Down : skip 692 | this.timer_split_skip(); 693 | ev.preventDefault(); 694 | break; 695 | case 38: // Up : go back 696 | this.timer_split_prev(); 697 | ev.preventDefault(); 698 | break; 699 | case 8: // Backspace, stop/reset 700 | this.timer_stop_reset(); 701 | ev.preventDefault(); 702 | break; 703 | } 704 | } 705 | } 706 | } 707 | 708 | Actions.prototype.handle_keyup = function(ev) 709 | { 710 | this.key_down = false; 711 | } 712 | 713 | Actions.prototype.handle_gamepad_press = function(ev, button_index, button) 714 | { 715 | // Actions only work in the timer control view 716 | if(this.get_page() == "timer-control") 717 | { 718 | switch(button_index) 719 | { 720 | case this.current_settings["gamepad_button_split"]: 721 | this.timer_start_split(); 722 | break; 723 | 724 | case this.current_settings["gamepad_button_reset"]: 725 | this.timer_stop_reset(); 726 | break; 727 | } 728 | } 729 | } 730 | 731 | //// Timer actions //// 732 | 733 | Actions.prototype.timer_start_split = function() 734 | { 735 | if(window.current_run && window.current_run.started) // A timer run is already started, we split 736 | { 737 | if(window.current_timer.timer_type == Timer.Type.RTA) 738 | { 739 | // Do not do anything if the timer is still below 0 740 | if(window.current_run.elapsed < 0) 741 | return; 742 | 743 | $("#timer-splits tr")[window.current_run.current_split].classList.remove("current"); 744 | window.current_run.split(); 745 | } 746 | else if(window.current_timer.timer_type == Timer.Type.MANUAL) 747 | { 748 | var split_time = prompt('Time for split "' + window.current_timer.splits[window.current_run.current_split].name + '"'); 749 | 750 | if(split_time) 751 | { 752 | $("#timer-splits tr")[window.current_run.current_split].classList.remove("current"); 753 | window.current_run.split_manual(string_to_msec(split_time)); 754 | } 755 | } 756 | } 757 | else // No timer run has been started, we create and start one 758 | { 759 | if(window.current_timer) 760 | { 761 | // If there is still a run going (previous run ended), do a reset 762 | if(window.current_run) 763 | this.timer_stop_reset(); 764 | else 765 | this.load_timer(window.current_timer); 766 | 767 | window.current_run = new Run(window.current_timer); 768 | window.current_run.start(); 769 | 770 | $("#run-count").text(window.current_timer.run_count); 771 | 772 | $("#control-button-play span").text("Split"); 773 | $("#control-button-play i").removeClass("glyphicon-play").addClass("glyphicon-ok"); 774 | 775 | $("#control-button-reset span").text("Stop"); 776 | $("#control-button-reset i").removeClass("glyphicon-refresh").addClass("glyphicon-stop"); 777 | 778 | 779 | $("#timer-splits").css('top', "0px"); 780 | } 781 | 782 | $("#control-button-skip").removeClass("disabled"); 783 | $("#control-button-back").removeClass("disabled"); 784 | } 785 | 786 | if(window.current_run.started) 787 | { 788 | $("#timer-splits tr")[window.current_run.current_split].classList.add("current"); 789 | 790 | //Move splits 791 | var container_height = q("#timer-splits-container").clientHeight; 792 | var split_tr = $("#timer-splits tr")[window.current_run.current_split].offsetTop; 793 | 794 | var total_height = q("#timer-splits").clientHeight; 795 | 796 | if(split_tr > container_height / 2 && total_height > container_height) 797 | { 798 | this.split_scroll_status = window.current_run.current_split; 799 | 800 | while((split_tr - (container_height / 2)) < $("#timer-splits tr")[this.split_scroll_status].offsetTop) 801 | this.split_scroll_status--; 802 | 803 | $("#timer-splits").css('top', "-" + $("#timer-splits tr")[this.split_scroll_status].offsetTop + "px"); 804 | } 805 | } 806 | else 807 | { 808 | this.update(); 809 | $("#control-button-play span").text("Start"); 810 | $("#control-button-play i").removeClass("glyphicon-ok").removeClass("glyphicon-stop").addClass("glyphicon-play"); 811 | $("#control-button-reset span").text("Reset"); 812 | $("#control-button-reset i").removeClass("glyphicon-stop").addClass("glyphicon-refresh"); 813 | } 814 | 815 | if(window.current_run.current_split + 1 == window.current_timer.splits.length && window.current_timer.timer_type == Timer.Type.RTA) 816 | { 817 | $("#control-button-play span").text("Stop"); 818 | $("#control-button-play i").removeClass("glyphicon-play").addClass("glyphicon-stop"); 819 | } 820 | } 821 | 822 | Actions.prototype.timer_split_prev = function() 823 | { 824 | $("#timer-splits tr")[window.current_run.current_split].classList.remove("current"); 825 | window.current_run.prev_split(); 826 | $("#timer-splits tr")[window.current_run.current_split].classList.add("current"); 827 | 828 | this.update(); 829 | 830 | //Removing current split 831 | $($("#timer-splits tr")[window.current_run.current_split].querySelector(".ref")).html(msec_to_string(window.current_timer.splits[window.current_run.current_split].pb_split)); 832 | $($("#timer-splits tr")[window.current_run.current_split].querySelector(".time")).html(""); 833 | } 834 | 835 | Actions.prototype.timer_split_skip = function() 836 | { 837 | if(window.current_run && window.current_run.started) 838 | { 839 | $("#timer-splits tr")[window.current_run.current_split].classList.remove("current"); 840 | $($("#timer-splits tr")[window.current_run.current_split].querySelector(".time")).html("-"); 841 | $($("#timer-splits tr")[window.current_run.current_split].querySelector(".ref")).html("-"); 842 | window.current_run.next_split(); 843 | 844 | if(window.current_run.started) 845 | $("#timer-splits tr")[window.current_run.current_split].classList.add("current"); 846 | else 847 | { 848 | $("#control-button-play span").text("Start"); 849 | $("#control-button-play i").removeClass("glyphicon-ok").removeClass("glyphicon-stop").addClass("glyphicon-play"); 850 | $("#control-button-reset span").text("Reset"); 851 | $("#control-button-reset i").removeClass("glyphicon-stop").addClass("glyphicon-refresh"); 852 | } 853 | 854 | if(window.current_run.current_split + 1 == window.current_timer.splits.length && window.current_timer.timer_type == Timer.Type.RTA) 855 | { 856 | $("#control-button-play span").text("Stop"); 857 | $("#control-button-play i").removeClass("glyphicon-play").addClass("glyphicon-stop"); 858 | } 859 | } 860 | } 861 | 862 | Actions.prototype.timer_stop_reset = function() 863 | { 864 | if(window.current_run && window.current_run.started) 865 | { 866 | window.current_run.stop(); 867 | this.update(); 868 | $("#control-button-reset span").text("Reset"); 869 | $("#control-button-reset i").removeClass("glyphicon-stop").addClass("glyphicon-refresh"); 870 | $("#control-button-play span").text("Restart"); 871 | $("#control-button-play i").removeClass("glyphicon-ok").removeClass("glyphicon-stop").addClass("glyphicon-play"); 872 | } 873 | else 874 | { 875 | if(window.current_run && window.current_run.best_time_updated) 876 | { 877 | var save_bests = confirm("There have been new best splits. Save them ?"); 878 | if(save_bests) 879 | window.current_timer.save_bests(window.current_run); 880 | } 881 | 882 | window.current_run = null; 883 | this.load_timer(window.current_timer); 884 | $("#control-button-play span").text("Start"); 885 | $("#timer-splits").css('top', "0px"); 886 | } 887 | } 888 | 889 | Actions.prototype.timer_save_splits = function() 890 | { 891 | window.current_timer.save_bests(window.current_run); // Save best splits 892 | window.current_timer.save_splits(window.current_run); 893 | Crouton.notify(CroutonType.CONFIRM, "Splits saved."); 894 | } 895 | 896 | Actions.prototype.timer_edit_timer = function() 897 | { 898 | //Additional security ensuring we don't do stupid things 899 | if(window.current_timer != null) 900 | { 901 | //Replacing timer data into edition fields 902 | $("#form-edit-timer-name").val(window.current_timer.timer_name); 903 | $("#form-edit-game-name").val(window.current_timer.run_name); 904 | $("#form-edit-start-delay").val(msec_to_string(window.current_timer.start_delay, false, 3)); 905 | 906 | q("#form-edit-timer-type-rta").checked = false; 907 | q("#form-edit-timer-type-manual").checked = false; 908 | 909 | switch(window.current_timer.timer_type) 910 | { 911 | case Timer.Type.RTA: 912 | q("#form-edit-timer-type-rta").checked = true; 913 | break; 914 | 915 | case Timer.Type.MANUAL: 916 | q("#form-edit-timer-type-manual").checked = true; 917 | break; 918 | } 919 | 920 | //putting back splits 921 | var split_template = $("#edit-timer-split-template"); 922 | $(".timer-split").remove(); 923 | $("#form-edit-timer-split-list").prepend(split_template); 924 | 925 | for(var i in window.current_timer.splits) 926 | { 927 | var new_split = split_template.clone(); 928 | new_split.removeClass("hidden"); 929 | new_split.removeAttr("id"); 930 | 931 | new_split.child('.split-name').val(window.current_timer.splits[i].name); 932 | new_split.child('.split-gold').val(msec_to_string(window.current_timer.splits[i].split_best, false, 3)); 933 | new_split.child('.split-reference').val(msec_to_string(window.current_timer.splits[i].pb_split, false, 3)); 934 | 935 | $('#form-edit-timer-split-list .timer-split:last-of-type').after(new_split); 936 | $(".timer-split:last-of-type [data-action]").each(Actions.bind_element_action, window); 937 | } 938 | 939 | 940 | this.load_page('edit-timer'); 941 | } 942 | } 943 | 944 | Actions.prototype.timer_close_timer = function() 945 | { 946 | window.current_run = null; 947 | window.current_timer = null; 948 | this.load_empty_timer(); 949 | this.load_page("main-menu"); 950 | } 951 | 952 | //// Settings page actions //// 953 | 954 | Actions.prototype.settings_discard = function() 955 | { 956 | // Stop capturing 957 | if(this.settings_current_capture_key) 958 | { 959 | var capture_button = q('.settings-gamepad-capture[data-buttontype="' + this.settings_current_capture_key + '"]'); 960 | this.settings_gamepad_capture_cancel(capture_button); 961 | } 962 | 963 | // Reload the settings from the storage 964 | this.refresh_settings(); 965 | 966 | if(window.current_timer) 967 | this.load_page("timer-control"); 968 | else 969 | this.load_page("main-menu"); 970 | } 971 | 972 | Actions.prototype.settings_save = function() 973 | { 974 | var use_gamepad = q("#settings-gamepad-use").checked; 975 | this.current_settings['use_gamepad'] = use_gamepad; 976 | 977 | Storage.get().set_settings(this.current_settings); 978 | 979 | if(window.current_timer) 980 | this.load_page("timer-control"); 981 | else 982 | this.load_page("main-menu"); 983 | } 984 | 985 | Actions.prototype.settings_gamepad_change = function() 986 | { 987 | var selected_gamepad_id = null; 988 | 989 | if(q("#settings-gamepad-id option:checked")) 990 | selected_gamepad_id = q("#settings-gamepad-id option:checked").value; 991 | 992 | this.gamepad.set_gamepad(selected_gamepad_id); 993 | this.current_settings.gamepad_id = selected_gamepad_id; 994 | 995 | // Disable or enable capture buttons if a gamepad has been selected 996 | $(".settings-gamepad-capture").each(function(el) 997 | { 998 | if(selected_gamepad_id) 999 | $(el).removeAttr('disabled'); 1000 | else 1001 | $(el).attr('disabled', true); 1002 | }); 1003 | } 1004 | 1005 | Actions.prototype.settings_current_capture_key = null; 1006 | 1007 | Actions.prototype.settings_gamepad_capture_toggle = function(el) 1008 | { 1009 | var captured_key = el.dataset.buttontype; 1010 | 1011 | // Key capture isn't enabled, start capturing 1012 | if(!this.settings_current_capture_key) 1013 | { 1014 | var input = q('.settings-gamepad-button[data-buttontype="' + captured_key + '"]'); 1015 | input.value = "Capturing..."; 1016 | 1017 | this.settings_current_capture_key = captured_key; 1018 | this.gamepad.start_polling(); 1019 | el.classList.remove('btn-primary'); 1020 | el.classList.add('btn-danger'); 1021 | el.value = "Cancel"; 1022 | } 1023 | else // Disable key capture 1024 | { 1025 | var input = q('.settings-gamepad-button[data-buttontype="' + captured_key + '"]'); 1026 | input.value = ""; 1027 | this.settings_gamepad_capture_cancel(el); 1028 | } 1029 | } 1030 | 1031 | Actions.prototype.settings_gamepad_capture_cancel = function(el) 1032 | { 1033 | this.settings_current_capture_key = null; 1034 | this.gamepad.stop_polling(); 1035 | el.classList.remove('btn-danger'); 1036 | el.classList.add('btn-primary'); 1037 | el.value = 'Capture'; 1038 | } 1039 | 1040 | Actions.prototype.settings_gamepad_handle_press = function(ev, button_index, button) 1041 | { 1042 | if(this.settings_current_capture_key) 1043 | { 1044 | // Updating the selected input 1045 | var input = q('.settings-gamepad-button[data-buttontype="' + this.settings_current_capture_key + '"]'); 1046 | input.value = "Button " + button_index; 1047 | 1048 | // Save button in current settings 1049 | this.current_settings["gamepad_button_" + this.settings_current_capture_key] = button_index; 1050 | 1051 | var el = q('.settings-gamepad-capture[data-buttontype="' + this.settings_current_capture_key + '"]'); 1052 | this.settings_gamepad_capture_cancel(el); 1053 | } 1054 | } -------------------------------------------------------------------------------- /css/bootstrap-theme.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","bootstrap-theme.css","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAcA;;;;;;EAME,0CAAA;ECgDA,6FAAA;EACQ,qFAAA;EC5DT;AFgBC;;;;;;;;;;;;EC2CA,0DAAA;EACQ,kDAAA;EC7CT;AFVD;;;;;;EAiBI,mBAAA;EECH;AFiCC;;EAEE,wBAAA;EE/BH;AFoCD;EGnDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EAgC2C,2BAAA;EAA2B,oBAAA;EEzBvE;AFLC;;EAEE,2BAAA;EACA,8BAAA;EEOH;AFJC;;EAEE,2BAAA;EACA,uBAAA;EEMH;AFHC;;;EAGE,2BAAA;EACA,wBAAA;EEKH;AFUD;EGpDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEgCD;AF9BC;;EAEE,2BAAA;EACA,8BAAA;EEgCH;AF7BC;;EAEE,2BAAA;EACA,uBAAA;EE+BH;AF5BC;;;EAGE,2BAAA;EACA,wBAAA;EE8BH;AFdD;EGrDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEyDD;AFvDC;;EAEE,2BAAA;EACA,8BAAA;EEyDH;AFtDC;;EAEE,2BAAA;EACA,uBAAA;EEwDH;AFrDC;;;EAGE,2BAAA;EACA,wBAAA;EEuDH;AFtCD;EGtDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEkFD;AFhFC;;EAEE,2BAAA;EACA,8BAAA;EEkFH;AF/EC;;EAEE,2BAAA;EACA,uBAAA;EEiFH;AF9EC;;;EAGE,2BAAA;EACA,wBAAA;EEgFH;AF9DD;EGvDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EE2GD;AFzGC;;EAEE,2BAAA;EACA,8BAAA;EE2GH;AFxGC;;EAEE,2BAAA;EACA,uBAAA;EE0GH;AFvGC;;;EAGE,2BAAA;EACA,wBAAA;EEyGH;AFtFD;EGxDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEoID;AFlIC;;EAEE,2BAAA;EACA,8BAAA;EEoIH;AFjIC;;EAEE,2BAAA;EACA,uBAAA;EEmIH;AFhIC;;;EAGE,2BAAA;EACA,wBAAA;EEkIH;AFxGD;;EChBE,oDAAA;EACQ,4CAAA;EC4HT;AFnGD;;EGzEI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHwEF,2BAAA;EEyGD;AFvGD;;;EG9EI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8EF,2BAAA;EE6GD;AFpGD;EG3FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EJ6GA,oBAAA;EC/CA,6FAAA;EACQ,qFAAA;EC0JT;AF/GD;;EG3FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,0DAAA;EACQ,kDAAA;ECoKT;AF5GD;;EAEE,gDAAA;EE8GD;AF1GD;EG9GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EF+OD;AFlHD;;EG9GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,yDAAA;EACQ,iDAAA;EC0LT;AF5HD;;EAYI,2CAAA;EEoHH;AF/GD;;;EAGE,kBAAA;EEiHD;AF5FD;EAfI;;;IAGE,aAAA;IG3IF,0EAAA;IACA,qEAAA;IACA,+FAAA;IAAA,wEAAA;IACA,6BAAA;IACA,wHAAA;ID0PD;EACF;AFxGD;EACE,+CAAA;ECzGA,4FAAA;EACQ,oFAAA;ECoNT;AFhGD;EGpKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EE4GD;AFvGD;EGrKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EEoHD;AF9GD;EGtKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EE4HD;AFrHD;EGvKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EEoID;AFrHD;EG/KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDuSH;AFlHD;EGzLI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED8SH;AFxHD;EG1LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDqTH;AF9HD;EG3LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED4TH;AFpID;EG5LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDmUH;AF1ID;EG7LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED0UH;AF7ID;EGhKI,+MAAA;EACA,0MAAA;EACA,uMAAA;EDgTH;AFzID;EACE,oBAAA;EC5JA,oDAAA;EACQ,4CAAA;ECwST;AF1ID;;;EAGE,+BAAA;EGjNE,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH+MF,uBAAA;EEgJD;AFrJD;;;EAQI,mBAAA;EEkJH;AFxID;ECjLE,mDAAA;EACQ,2CAAA;EC4TT;AFlID;EG1OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED+WH;AFxID;EG3OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDsXH;AF9ID;EG5OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED6XH;AFpJD;EG7OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDoYH;AF1JD;EG9OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED2YH;AFhKD;EG/OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDkZH;AFhKD;EGtPI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHoPF,uBAAA;ECzMA,2FAAA;EACQ,mFAAA;ECgXT","file":"bootstrap-theme.css","sourcesContent":["\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &:disabled,\n &[disabled] {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n",".btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default:disabled,\n.btn-default[disabled] {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary:disabled,\n.btn-primary[disabled] {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success:disabled,\n.btn-success[disabled] {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info:disabled,\n.btn-info[disabled] {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning:disabled,\n.btn-warning[disabled] {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger:disabled,\n.btn-danger[disabled] {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} --------------------------------------------------------------------------------