From 1aab4e6e6481c91795ad185d18363da30fbd9a46 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Tue, 19 Mar 2013 19:02:23 +0200 Subject: [PATCH] Init --- .gitignore | 4 + README.md | 14 ++ build.sh | 16 ++ calcalc.sublime-project | 8 + dynamicgif.less | 16 ++ elements.less | 136 +++++++++++++ index.html | 128 ++++++++++++ main.js | 422 ++++++++++++++++++++++++++++++++++++++++ main.less | 144 ++++++++++++++ 9 files changed, 888 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 build.sh create mode 100644 calcalc.sublime-project create mode 100644 dynamicgif.less create mode 100644 elements.less create mode 100644 index.html create mode 100644 main.js create mode 100644 main.less diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..746b799 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.sublime-workspace +*.css +temp +build diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2fe0b6 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# Calcalc + +http://www.liikkumisenohjaus.fi/kalorilaskuri + +## Dependencies + npm install -g nodefront + +## Development server + + nodefront serve -cl + +## Build + + build.sh diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..98159bc --- /dev/null +++ b/build.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +nodefront compile -r + +mkdir -p build +mkdir -p temp + +nodefront minify --out build/calcalc.min.css main.css + +nodefront minify --out build/calcalc.min.js main.js + +# cp main.js build/calcalc.min.js + +cp index.html build/calcalc.html + + diff --git a/calcalc.sublime-project b/calcalc.sublime-project new file mode 100644 index 0000000..24db303 --- /dev/null +++ b/calcalc.sublime-project @@ -0,0 +1,8 @@ +{ + "folders": + [ + { + "path": "." + } + ] +} diff --git a/dynamicgif.less b/dynamicgif.less new file mode 100644 index 0000000..e497e3b --- /dev/null +++ b/dynamicgif.less @@ -0,0 +1,16 @@ +.gifhex(@hex) { + @s: ~`(function(s){s=s.substring(1,7); return (s.length<6)? s[0]+s[0]+s[1]+s[1]+s[2]+s[2] : s;})("@{hex}")`; + @func: ~`(function(s,i){return parseInt(s[0+2*i]+s[1+2*i],16);})`; + @r: ~`@{func}("@{s}", 0)`; + @g: ~`@{func}("@{s}", 1)`; + @b: ~`@{func}("@{s}", 2)`; + .gifrgb(@r, @g, @b); + +} +.gifrgb(@r,@g,@b) { + @k: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + @g1: "url(data:image/gif;base64,R0lGODlhAQABAPAAA"; + @g2: "///yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==)"; + @b64img: ~`function(r,g,b){var k=@{k};return @{g1}+k.charAt(((0&3)<<4)|(r>>4))+k.charAt(((r&15)<<2)|(g>>6))+k.charAt(g&63)+k.charAt(b>>2)+k.charAt(((b&3)<<4)|(255>>4))+@{g2}}(@{r},@{g},@{b})`; + background-image: @b64img; +} \ No newline at end of file diff --git a/elements.less b/elements.less new file mode 100644 index 0000000..e0a3fb5 --- /dev/null +++ b/elements.less @@ -0,0 +1,136 @@ +/*--------------------------------------------------- + LESS Elements 0.6 + --------------------------------------------------- + A set of useful LESS mixins by Dmitry Fadeyev + Special thanks for mixin suggestions to: + Kris Van Herzeele, + Benoit Adam, + Portenart Emile-Victor, + Ryan Faerman + + More info at: http://lesselements.com +-----------------------------------------------------*/ + +.gradient(@color: #F5F5F5, @start: #EEE, @stop: #FFF) { + background: @color; + background: -webkit-gradient(linear, + left bottom, + left top, + color-stop(0, @start), + color-stop(1, @stop)); + background: -ms-linear-gradient(bottom, + @start, + @stop); + background: -moz-linear-gradient(center bottom, + @start 0%, + @stop 100%); +} +.bw-gradient(@color: #F5F5F5, @start: 0, @stop: 255) { + background: @color; + background: -webkit-gradient(linear, + left bottom, + left top, + color-stop(0, rgb(@start,@start,@start)), + color-stop(1, rgb(@stop,@stop,@stop))); + background: -ms-linear-gradient(bottom, + rgb(@start,@start,@start) 0%, + rgb(@start,@start,@start) 100%); + background: -moz-linear-gradient(center bottom, + rgb(@start,@start,@start) 0%, + rgb(@stop,@stop,@stop) 100%); +} +.bordered(@top-color: #EEE, @right-color: #EEE, @bottom-color: #EEE, @left-color: #EEE) { + border-top: solid 1px @top-color; + border-left: solid 1px @left-color; + border-right: solid 1px @right-color; + border-bottom: solid 1px @bottom-color; +} +.drop-shadow(@x-axis: 0, @y-axis: 1px, @blur: 2px, @alpha: 0.1) { + -webkit-box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha); + -moz-box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha); + box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha); +} +.rounded(@radius: 2px) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; + -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box; +} +.border-radius(@topright: 0, @bottomright: 0, @bottomleft: 0, @topleft: 0) { + -webkit-border-top-right-radius: @topright; + -webkit-border-bottom-right-radius: @bottomright; + -webkit-border-bottom-left-radius: @bottomleft; + -webkit-border-top-left-radius: @topleft; + -moz-border-radius-topright: @topright; + -moz-border-radius-bottomright: @bottomright; + -moz-border-radius-bottomleft: @bottomleft; + -moz-border-radius-topleft: @topleft; + border-top-right-radius: @topright; + border-bottom-right-radius: @bottomright; + border-bottom-left-radius: @bottomleft; + border-top-left-radius: @topleft; + -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box; +} +.opacity(@opacity: 0.5) { + -moz-opacity: @opacity; + -khtml-opacity: @opacity; + -webkit-opacity: @opacity; + opacity: @opacity; +} +.transition-duration(@duration: 0.2s) { + -moz-transition-duration: @duration; + -webkit-transition-duration: @duration; + transition-duration: @duration; +} +.rotation(@deg:5deg){ + -webkit-transform: rotate(@deg); + -moz-transform: rotate(@deg); + transform: rotate(@deg); +} +.scale(@ratio:1.5){ + -webkit-transform:scale(@ratio); + -moz-transform:scale(@ratio); + transform:scale(@ratio); +} +.transition(@duration:0.2s, @ease:ease-out) { + -webkit-transition: all @duration @ease; + -moz-transition: all @duration @ease; + transition: all @duration @ease; +} +.inner-shadow(@horizontal:0, @vertical:1px, @blur:2px, @alpha: 0.4) { + -webkit-box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha); + -moz-box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha); + box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha); +} +.box-shadow(@arguments) { + -webkit-box-shadow: @arguments; + -moz-box-shadow: @arguments; + box-shadow: @arguments; +} +.columns(@colwidth: 250px, @colcount: 0, @colgap: 50px, @columnRuleColor: #EEE, @columnRuleStyle: solid, @columnRuleWidth: 1px) { + -moz-column-width: @colwidth; + -moz-column-count: @colcount; + -moz-column-gap: @colgap; + -moz-column-rule-color: @columnRuleColor; + -moz-column-rule-style: @columnRuleStyle; + -moz-column-rule-width: @columnRuleWidth; + -webkit-column-width: @colwidth; + -webkit-column-count: @colcount; + -webkit-column-gap: @colgap; + -webkit-column-rule-color: @columnRuleColor; + -webkit-column-rule-style: @columnRuleStyle; + -webkit-column-rule-width: @columnRuleWidth; + column-width: @colwidth; + column-count: @colcount; + column-gap: @colgap; + column-rule-color: @columnRuleColor; + column-rule-style: @columnRuleStyle; + column-rule-width: @columnRuleWidth; +} +.translate(@x:0, @y:0) { + -moz-transform: translate(@x, @y); + -webkit-transform: translate(@x, @y); + -o-transform: translate(@x, @y); + -ms-transform: translate(@x, @y); + transform: translate(@x, @y); +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..ef285dd --- /dev/null +++ b/index.html @@ -0,0 +1,128 @@ + + + + + + Calcalc proto + + + + + + + + + + + + + + +
+
+
+

Polkupyöräily kalorilaskuri

+
+
+ + + + + + +
+
+ + Sukupuoli + + +
+ + +
+
+
+ Nopeus + +
+
+ Paino +
+
+ + kg +
+
Kirjoita kenttään numero.
+
+
+
+ +
+
+ + Reitti + + +
+
+
+ 1. + +
+
+ 2. + +
+
Ei reittiä osoitteiden välillä.
+
Toista osoitteista ei löytynyt.
+ Katso reitti Google Mapsissa +
+ +
+
+
+ Matka +
+
+ + km +
+
Kirjoita kenttään numero.
+
+
+
+ +
+
+

Kulutit matkallasi 0 kilokaloria.

+ +

+ + tai vaihtoehtoisesti voit kopioida sivun osoitteen ja jakaa sen. +

+ +

Kalorinkulutuksen laskeminen perustuu MET-arvoihin (Metabolinen ekvivalentti). Yksi MET on 1kcal/kg/tunti, joten kertomalla MET arvo painolla ja ajalla saadaan kalorien kulutus.

+ + +

Vastaukset tilastoidaan, jotta voidaan seurata sukupuolijakaumaa, keskimääräistä matkaa ja kalorinkulutusta. Tietoja ei yksilöidä.

+
+
+ + + +
+ + diff --git a/main.js b/main.js new file mode 100644 index 0000000..9287055 --- /dev/null +++ b/main.js @@ -0,0 +1,422 @@ +$.fn.show_message = function (message) { + return this.each(function () { + $(this).parent('div').parent().children('.' + message + '-msg').removeClass('hide'); + }); +}; + +$.fn.hide_message = function (message) { + return this.each(function () { + $(this).parent('div').parent().children('.' + message + '-msg').addClass('hide'); + }); +}; + +$.fn.valueChanged = function (callback) { + return this.each(function () { + var el = $(this), + prev = null, + focus = function () { + prev = el.attr('value'); + }, + change = function () { + var val = el.attr('value'); + if (val !== prev) { + prev = val; + callback.apply(this); + } + }; + el.bind('focus', focus).bind('blur', change).bind('keyup', change); + }); +}; + +var toFloat = function (str) { + return Number(str.replace(/,/g, '.')); +}; +var displayFloat = function (num) { + return num.toString().replace(/\./g, ','); +}; + +// Okay, this should have been done using eq. JQuery-BBQ +// But changing it now would require some hacks as we don't want to break +// existing urls. +// (places=place+1;place+2 vs. places[]=place+1&places[]=place+2) +var HashSave = { + settings: { + filter: null, + types: {}, + display: {} + }, + init: function (settings) { + $.extend(true, HashSave.settings, settings); + }, + save: function (dict) { + var hash = [], + encode = function (value) { + if (typeof value === 'string') { + return value.replace(/\ /g, '+'); + } + return value; + }; + + for (var key in dict) { + if ((1 + $.inArray(key, HashSave.settings.ignored)) || + HashSave.settings.filter === null || + HashSave.settings.filter(key)) { + continue; + } + + var val; + if ($.isArray(dict[key])) { + var r = []; + for (var i in dict[key]) { + r.push(encode(dict[key][i])); + } + val = r.join(';'); + } else { + val = encode(dict[key]); + } + hash.push(key + '=' + val); + } + + location.hash = '!' + hash.join('&'); + }, + load: function (dict) { + var hash = location.hash, + display = HashSave.settings.display, + decode = function (type, value) { + if (type === 'string') { + return value.replace(/\+/g, ' '); + } else if (type === 'Number') { + return toFloat(value); + } + return value; + }, + types = HashSave.settings.types; + + if (!hash || hash[1] !== '!') return false; + + hash = hash.slice(2).split('&'); + for (var i in hash) { + var a = hash[i].split('='), + key = a[0], value = a[1]; + + if ($.isArray(types[key])) { + value = value.split(';'); + for (var b in value) { + value[b] = decode(types[key][b], value[b]); + } + } else { + value = decode(types[key], value); + } + + console.log(value); + dict[key] = value; + if (key in display) { + display[key](); + } + } + + return true; + } +}; + +var CalCalc = { + mets: { + bike: { + text: 'Pyöräillen', + text2: 'pyöräilen', + travelMode: google.maps.TravelMode.BICYCLING, + googleLink: '&mra=ltm&lci=bike&z=14&oq=mann&t=h&dirflg=b', + data: { + 18: {activity: 'Hidas', met: 6.8, description: '16-19 km/h'}, + 21: {activity: 'Normaali', met: 8.0, description: '19-23 km/h'}, + 24: {activity: 'Nopea', met: 10.0, description: '23-26 km/h'} + } + }, + foot: { + text: 'Jalan', + text2: 'kuljen jalan', + travelMode: google.maps.TravelMode.WALKING, + googleLink: '&t=h&dirflg=w&mra=ltm&z=14', + data: { + 5: {activity: 'Kävely', met: 3.0, description: '70 m/min'}, + 12: {activity: 'Juoksu', met: 11.8, description: '200 m/min'} + } + } + }, + genders: { + 0: 'Puuttuu', + 1: 'Mies', + 2: 'Nainen' + }, + calories: [ + {name: 'omenaa', kcal: 46}, + {name: 'maitolasillista', kcal: 100}, + {name: 'jäätelötuuttia', kcal: 200}, + {name: 'korvapuustia', kcal: 380}, + {name: 'hampurilaista', kcal: 500}, + {name: 'pakastepizzaa', kcal: 900}, + {name: 'sipsipussia', kcal: 1800} + ], + vars: { + places: [null, null], + calories: 0, + weight: 70, // default weight + distance: 0, + method: ['bike', 21], + gender: 0 + }, + settings: { + reporting: false + }, + display: { + places: function () { + $("#place1").attr('value', CalCalc.vars.places[0]); + $("#place2").attr('value', CalCalc.vars.places[1]); + }, + calories: function () { + $('#calories').html(CalCalc.vars.calories); + }, + distance: function () { + $('#distance').attr('value', displayFloat(CalCalc.vars.distance)); + }, + weight: function () { + $('#weight').attr('value', displayFloat(CalCalc.vars.weight)); + }, + method: function () { + var el = $('#selectedSpeed'); + el.find('.activity').html(CalCalc.mets[CalCalc.vars.method[0]].text + ', ' + CalCalc.mets[CalCalc.vars.method[0]].data[CalCalc.vars.method[1]].activity); + el.find('.description').html(CalCalc.mets[CalCalc.vars.method[0]].data[CalCalc.vars.method[1]].description); + + $('#speed li').removeClass('active'); + $('#speed a[data-method=' + CalCalc.vars.method[0] + '][data-speed=' + CalCalc.vars.method[1] + ']').parent().addClass('active'); + }, + url: function (value) { + return value.replace(/\ /g, '+'); + } + }, + onReady: function () { + // Google Places Autocomplete + var autocompleteOptions = {componentRestrictions: {country: 'fi'}, types: ['geocode']}, + place1complete = new google.maps.places.Autocomplete($('#place1').get(0), autocompleteOptions), + place2complete = new google.maps.places.Autocomplete($('#place2').get(0), autocompleteOptions); + + // Activate inputs with prefilled data + $('#weight').attr('placeholder', CalCalc.vars.weight); + $('#distance').attr('placeholder', CalCalc.vars.distance); + var speedList = ''; + for (var method in CalCalc.mets) { + speedList += ''; + for (var speed in CalCalc.mets[method].data) { + speedList += '
  • ' + CalCalc.mets[method].data[speed].activity + '
  • '; + } + } + $('#speed').html(speedList); + CalCalc.display.method(); + + // Update variables if input value changes + $('#place1, #place2').bind('keyup', function (event) { + if (event.keyCode === 13) { + if (CalCalc.addressChange()) { + $(this).blur(); + $(this).addClass('focus'); + } + } + }); + $('#compute').click(CalCalc.addressChange); + $('#weight').valueChanged(CalCalc.weightChange); + $('#distance').valueChanged(CalCalc.distanceChange); + $('#speed').find('a').click(CalCalc.speedChange); + $('#gender').find('button').click(CalCalc.genderChange); + + HashSave.init({ + ignored: ['gender', 'calories'], + filter: function (key) { + // Skip distance if both places are set + // Skip places if either of places is null + return ((CalCalc.vars.places[0] !== null && CalCalc.vars.places[1] !== null) && key === 'distance') || + ((CalCalc.vars.places[0] === null || CalCalc.vars.places[1] === null) && key === 'places'); + }, + types: { + calories: 'Number', + weight: 'Number', + distance: 'Number', + method: ['string', 'Number'], + places: ['string', 'string'] + }, + display: CalCalc.display + }); + + + // Read hashbang + if (HashSave.load(CalCalc.vars)) { + if (CalCalc.vars.places[0] !== null && CalCalc.vars.places[1] !== null) { + CalCalc.updateRoute(); + } else { + CalCalc.update(); + } + } + + // Android - Dropdown fix - Bootstrap 2.1.1 + // https://github.com/twitter/bootstrap/issues/4550 + $('body').on('touchstart.dropdown', '.dropdown-menu', function (e) { + e.stopPropagation(); + }); + $('a.dropdown-toggle').on('touchstart', function (e) { + e.stopPropagation(); + }); + }, + update: function () { + var reportTimeout = null; + return function () { + if (CalCalc.vars.distance !== null && + CalCalc.vars.method[0] !== null && CalCalc.vars.method[1] !== null && + CalCalc.vars.weight !== null) { + + var duration = CalCalc.vars.distance / CalCalc.vars.method[1], // -> hours + met = CalCalc.mets[CalCalc.vars.method[0]].data[CalCalc.vars.method[1]].met; + + CalCalc.vars.calories = (met * CalCalc.vars.weight * duration).toFixed(0); + + CalCalc.display.calories(); + + var multi, + food, + fuu = 0; + + for (var i in CalCalc.calories) { + if (CalCalc.vars.calories > CalCalc.calories[i].kcal) { + fuu = i; + } + } + + food = CalCalc.calories[fuu].name; + multi = (CalCalc.vars.calories / CalCalc.calories[fuu].kcal).toFixed(2); + if (multi > 0) { + $('#control').html(', tämä vastaa ' + displayFloat(multi) + ' ' + food); + } else { + $('#control').html(''); + } + + + // 5 sec after latest update, report values + clearTimeout(reportTimeout); + reportTimeout = setTimeout(CalCalc.report, 5000); + + HashSave.save(CalCalc.vars); + } + }; + }(), + distanceChange: function () { + var el = $('#distance').removeClass('from-route'), + distance = toFloat(el.attr('value')); + + if (!isNaN(distance)) { + CalCalc.vars.places[0] = null; + CalCalc.vars.places[1] = null; + $('#place1').attr('value', '').hide_message('error'); + $('#place2').attr('value', '').hide_message('error').hide_message('zero-results').hide_message('google-maps'); + + CalCalc.vars.distance = distance || 0; + el.hide_message('error'); + CalCalc.update(); + } else { + CalCalc.vars.distance = null; + el.show_message('error'); + } + }, + weightChange: function () { + var el = $(this), + weight = toFloat(el.attr('value')); + + if (!isNaN(weight)) { + CalCalc.vars.weight = weight || toFloat(el.attr('placeholder')); + el.hide_message('error'); + CalCalc.update(); + } else { + // Parse error - weight not number + CalCalc.vars.weight = null; + el.show_message('error'); + } + }, + speedChange: function (e) { + e.preventDefault(); + + var data = $(this).data('speed'); + var data2 = $(this).data('method'); + if (data2 in CalCalc.mets && data in CalCalc.mets[data2].data) { + CalCalc.vars.method = [data2, data]; + CalCalc.display.method(); + if (!CalCalc.updateRoute()) { + CalCalc.update(); + } + } + }, + genderChange: function () { + var data = $(this).data('gender'); + if (data in CalCalc.genders) { + CalCalc.vars.gender = data; + } + }, + addressChange: function () { + CalCalc.vars.places[0] = $("#place1").attr('value'); + CalCalc.vars.places[1] = $("#place2").attr('value'); + CalCalc.updateRoute(); + + return CalCalc.vars.places[0] !== '' && CalCalc.vars.places[1] !== ''; + }, + updateRoute: function () { + var service = new google.maps.DirectionsService(); + return function () { + $("#place2").hide_message('zero-results').hide_message('not-found'); + if (!CalCalc.vars.places[0] || !CalCalc.vars.places[1]) return false; + + var request = { + destination: CalCalc.vars.places[1], + origin: CalCalc.vars.places[0], + travelMode: CalCalc.mets[CalCalc.vars.method[0]].travelMode, + region: 'fi' + }; + + service.route(request, function (result, status) { + // console.log(status); + if (status === google.maps.DirectionsStatus.ZERO_RESULTS) { + $("#place2").show_message('zero-results').hide_message('google-maps'); + } else if (status === google.maps.DirectionsStatus.NOT_FOUND) { + $("#place2").show_message('not-found').hide_message('google-maps'); + } else if (status === google.maps.DirectionsStatus.OK && result.routes.length !== 0 || result.routes[0].legs.length !== 0) { + $("#place2").show_message('google-maps'); + $(".google-maps-msg").attr('href', 'https://maps.google.com/maps?saddr=' + CalCalc.display.url(CalCalc.vars.places[0]) + '&daddr=' + CalCalc.display.url(CalCalc.vars.places[1]) + CalCalc.mets[CalCalc.vars.method[0]].googleLink); + + // console.log(result.routes[0].legs[0]); + CalCalc.vars.places[0] = result.routes[0].legs[0].start_address; + CalCalc.vars.places[1] = result.routes[0].legs[0].end_address; + CalCalc.display.places(); + + CalCalc.vars.distance = result.routes[0].legs[0].distance.value / 1000; // m -> km + $('#distance').addClass('from-route').hide_message('error');//.attr('value', displayFloat(CalCalc.vars.distance.toFixed(2))); + CalCalc.display.distance(); + CalCalc.update(); + } + + // Now that input value has been changed, focus can be returned to it. + $("#place1, #place2").filter(".focus").focus().removeClass('focus'); + }); + return true; + }; + }(), + report: function () { + if (CalCalc.vars.distance > 100 || !CalCalc.settings.reporting) return; + + _gaq.push(['_trackEvent', 'CalCalc', 'Weight', 'Weight', Number(CalCalc.vars.weight.toFixed(0)), true]); + _gaq.push(['_trackEvent', 'CalCalc', 'Distance', 'Distance', Number(CalCalc.vars.distance.toFixed(0)), true]); + _gaq.push(['_trackEvent', 'CalCalc', 'Kilocalories', 'Kilocalories', Number(CalCalc.vars.calories), true]); + _gaq.push(['_trackEvent', 'CalCalc', 'Gender', CalCalc.genders[CalCalc.vars.gender], undefined, true]); + } +}; + +$(document).ready(function () { + $('[rel=tooltip]').tooltip({ + container: 'body' + }); + CalCalc.onReady(); +}); diff --git a/main.less b/main.less new file mode 100644 index 0000000..05c5562 --- /dev/null +++ b/main.less @@ -0,0 +1,144 @@ +@import "dynamicgif.less"; +@import "elements.less"; + +.gradient (@origin: left, @start: #ffffff, @stop: #000000) { + background-color: @start; + background-image: -webkit-linear-gradient(@origin, @start, @stop); + background-image: -moz-linear-gradient(@origin, @start, @stop); + background-image: -o-linear-gradient(@origin, @start, @stop); + background-image: -ms-linear-gradient(@origin, @start, @stop); + background-image: linear-gradient(@origin, @start, @stop); +} + +@input_padding: 2%; +@input_width: 85.85%; +@addon_width: 9.85%; + +@large_input_width: 60.85%; +@large_addon_width: 34.85%; + +@result_input_width: 35%; +@result_addon_width: 20%; + +.calculator { + input { + width: @input_width; + padding-left: @input_padding; + padding-right: @input_padding; + } + + .add-on { + width: @addon_width; + padding-left: 0; + padding-right: 0; + } + + .input-append.large { + input { + font-size: 48px; + line-height: 55px; + height: 55px; + text-align: right; + width: @large_input_width; + } + .add-on { + height: 55px; + font-size: 48px; + line-height: 55px; + width: @large_addon_width; + } + } + + .result { + text-align: center; + font-size: 48px; + line-height: 64px; + margin-bottom: 0.5em; + } + + ul.sources { + margin-top: -0.7em; + } + + legend { + span { + font-size: 14px; + line-height: 20px; + } + } + + span.help { + color: #3D5DA8; + } + + .tooltip { + font-size: 14px; + } + + .from-route { + color: #888; + } + + .btn-large { + padding: 4px 19px; + line-height: 55px; + font-size: 24px; + } + + .btn-group { + & > .btn + .btn { + margin-left: 0; + border-left: 0; + } + [class*="span"] { float: left; } + .span6 { width: 49.9998%; } + .span4 { width: 33.332%; } + } + + .speed-btn { + height: 55px; + line-height: 27px; + text-align: left; + display: block; + span.activity { + display: block; + font-size: 15px; + } + span.description { + display: block; + font-size: 13px; + } + .caret { + margin-top: -27px; + float: right; + } + } + + .dropdown-menu { + li.active a { + color: #fff; + } + } + + .message { + display: block; + margin-bottom: 6px; + background: #DD4F4F; + color: #fff; + font-size: 12px; + padding: 1px 12px; + position: absolute; + .rounded(3px); + &::after { + position: absolute; + top: -6px; + left: 10px; + display: inline-block; + border-right: 6px solid transparent; + border-bottom: 6px solid #DD4F4F; + border-left: 6px solid transparent; + content: ''; + } + &.hide { display: none; } + } +}