=Q&&(!b.match(/^(get|post|head|put|delete|options)$/i)||
+!W.XMLHttpRequest))return new W.ActiveXObject("Microsoft.XMLHTTP");if(W.XMLHttpRequest)return new W.XMLHttpRequest;throw D("$httpBackend")("noxhr");}function Vd(){this.$get=["$browser","$window","$document",function(b,a,c){return ye(b,xe,b.defer,a.angular.callbacks,c[0])}]}function ye(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"),g=null;f.type="text/javascript";f.src=a;f.async=!0;g=function(a){$a(f,"load",g);$a(f,"error",g);e.body.removeChild(f);f=null;var k=-1,s="unknown";a&&("load"!==
+a.type||d[b].called||(a={type:"error"}),s=a.type,k="error"===a.type?404:200);c&&c(k,s)};sb(f,"load",g);sb(f,"error",g);8>=Q&&(f.onreadystatechange=function(){A(f.readyState)&&/loaded|complete/.test(f.readyState)&&(f.onreadystatechange=null,g({type:"load"}))});e.body.appendChild(f);return g}var g=-1;return function(e,m,h,l,n,p,q,s){function E(){B=g;R&&R();w&&w.abort()}function u(a,d,e,f,g){K&&c.cancel(K);R=w=null;0===d&&(d=e?200:"file"==ua(m).protocol?404:0);a(1223===d?204:d,e,f,g||"");b.$$completeOutstandingRequest(F)}
+var B;b.$$incOutstandingRequestCount();m=m||b.url();if("jsonp"==M(e)){var N="_"+(d.counter++).toString(36);d[N]=function(a){d[N].data=a;d[N].called=!0};var R=f(m.replace("JSON_CALLBACK","angular.callbacks."+N),N,function(a,b){u(l,a,d[N].data,"",b);d[N]=F})}else{var w=a(e);w.open(e,m,!0);r(n,function(a,b){z(a)&&w.setRequestHeader(b,a)});w.onreadystatechange=function(){if(w&&4==w.readyState){var a=null,b=null,c="";B!==g&&(a=w.getAllResponseHeaders(),b="response"in w?w.response:w.responseText);B===g&&
+10>Q||(c=w.statusText);u(l,B||w.status,b,a,c)}};q&&(w.withCredentials=!0);if(s)try{w.responseType=s}catch(ca){if("json"!==s)throw ca;}w.send(h||null)}if(0=k&&(n.resolve(q),l(p.$$intervalId),delete e[p.$$intervalId]);s||b.$apply()},g);e[p.$$intervalId]=n;return p}var e={};d.cancel=
+function(b){return b&&b.$$intervalId in e?(e[b.$$intervalId].reject("canceled"),a.clearInterval(b.$$intervalId),delete e[b.$$intervalId],!0):!1};return d}]}function bd(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),
+SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return 1===b?"one":"other"}}}}function Qb(b){b=b.split("/");for(var a=b.length;a--;)b[a]=
+lb(b[a]);return b.join("/")}function zc(b,a,c){b=ua(b,c);a.$$protocol=b.protocol;a.$$host=b.hostname;a.$$port=U(b.port)||ze[b.protocol]||null}function Ac(b,a,c){var d="/"!==b.charAt(0);d&&(b="/"+b);b=ua(b,c);a.$$path=decodeURIComponent(d&&"/"===b.pathname.charAt(0)?b.pathname.substring(1):b.pathname);a.$$search=ec(b.search);a.$$hash=decodeURIComponent(b.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function ra(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function eb(b){var a=
+b.indexOf("#");return-1==a?b:b.substr(0,a)}function Rb(b){return b.substr(0,eb(b).lastIndexOf("/")+1)}function Bc(b,a){this.$$html5=!0;a=a||"";var c=Rb(b);zc(b,this,b);this.$$parse=function(a){var e=ra(c,a);if(!A(e))throw Sb("ipthprfx",a,c);Ac(e,this,b);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Cb(this.$$search),b=this.$$hash?"#"+lb(this.$$hash):"";this.$$url=Qb(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$rewrite=function(d){var e;
+if((e=ra(b,d))!==t)return d=e,(e=ra(a,e))!==t?c+(ra("/",e)||e):b+d;if((e=ra(c,d))!==t)return c+e;if(c==d+"/")return c}}function Tb(b,a){var c=Rb(b);zc(b,this,b);this.$$parse=function(d){var e=ra(b,d)||ra(c,d),e="#"==e.charAt(0)?ra(a,e):this.$$html5?e:"";if(!A(e))throw Sb("ihshprfx",d,a);Ac(e,this,b);d=this.$$path;var f=/^\/[A-Z]:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));f.exec(e)||(d=(e=f.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var c=Cb(this.$$search),e=this.$$hash?
+"#"+lb(this.$$hash):"";this.$$url=Qb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$rewrite=function(a){if(eb(b)==eb(a))return a}}function Ub(b,a){this.$$html5=!0;Tb.apply(this,arguments);var c=Rb(b);this.$$rewrite=function(d){var e;if(b==eb(d))return d;if(e=ra(c,d))return b+a+e;if(c===d+"/")return c};this.$$compose=function(){var c=Cb(this.$$search),e=this.$$hash?"#"+lb(this.$$hash):"";this.$$url=Qb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+a+this.$$url}}function tb(b){return function(){return this[b]}}
+function Cc(b,a){return function(c){if(y(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Wd(){var b="",a=!1;this.hashPrefix=function(a){return z(a)?(b=a,this):b};this.html5Mode=function(b){return z(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,f){function g(a){c.$broadcast("$locationChangeSuccess",k.absUrl(),a)}var k,m,h=d.baseHref(),l=d.url(),n;a?(n=l.substring(0,l.indexOf("/",l.indexOf("//")+2))+(h||"/"),m=e.history?Bc:Ub):(n=
+eb(l),m=Tb);k=new m(n,"#"+b);k.$$parse(k.$$rewrite(l));var p=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(!a.ctrlKey&&!a.metaKey&&2!=a.which){for(var e=v(a.target);"a"!==M(e[0].nodeName);)if(e[0]===f[0]||!(e=e.parent())[0])return;var g=e.prop("href");T(g)&&"[object SVGAnimatedString]"===g.toString()&&(g=ua(g.animVal).href);if(!p.test(g)){if(m===Ub){var h=e.attr("href")||e.attr("xlink:href");if(h&&0>h.indexOf("://"))if(g="#"+b,"/"==h[0])g=n+g+h;else if("#"==h[0])g=n+g+(k.path()||"/")+h;
+else{var l=k.path().split("/"),h=h.split("/");2!==l.length||l[1]||(l.length=1);for(var q=0;qe?Dc(d[0],d[1],d[2],d[3],d[4],c,a):function(b,f){var g=0,k;do k=Dc(d[g++],d[g++],d[g++],d[g++],d[g++],c,a)(b,f),f=t,b=k;while(ga)for(b in h++,e)e.hasOwnProperty(b)&&!d.hasOwnProperty(b)&&(r--,delete e[b])}else e!==d&&(e=d,h++);return h},function(){n?(n=!1,b(d,d,c)):b(d,g,c);if(k)if(T(d))if(Pa(d)){g=Array(d.length);for(var a=0;at&&(v=4-t,O[v]||(O[v]=[]),C=P(d.exp)?"fn: "+(d.exp.name||d.exp.toString()):
+d.exp,C+="; newVal: "+na(f)+"; oldVal: "+na(k),O[v].push(C));else if(d===c){w=!1;break a}}catch(z){p.$$phase=null,e(z)}if(!(h=K.$$childHead||K!==this&&K.$$nextSibling))for(;K!==this&&!(h=K.$$nextSibling);)K=K.$parent}while(K=h);if((w||l.length)&&!t--)throw p.$$phase=null,a("infdig",b,na(O));}while(w||l.length);for(p.$$phase=null;r.length;)try{r.shift()()}catch(A){e(A)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this!==p&&(r(this.$$listenerCount,
+Bb(null,l,this)),a.$$childHead==this&&(a.$$childHead=this.$$nextSibling),a.$$childTail==this&&(a.$$childTail=this.$$prevSibling),this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling),this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling),this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=null,this.$$listeners={},this.$$watchers=this.$$asyncQueue=this.$$postDigestQueue=[],this.$destroy=this.$digest=this.$apply=F,this.$on=
+this.$watch=function(){return F})}},$eval:function(a,b){return f(a)(this,b)},$evalAsync:function(a){p.$$phase||p.$$asyncQueue.length||g.defer(function(){p.$$asyncQueue.length&&p.$digest()});this.$$asyncQueue.push({scope:this,expression:a})},$$postDigest:function(a){this.$$postDigestQueue.push(a)},$apply:function(a){try{return m("$apply"),this.$eval(a)}catch(b){e(b)}finally{p.$$phase=null;try{p.$digest()}catch(c){throw e(c),c;}}},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=
+c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){c[Ra(c,b)]=null;l(e,1,a)}},$emit:function(a,b){var c=[],d,f=this,g=!1,k={name:a,targetScope:f,stopPropagation:function(){g=!0},preventDefault:function(){k.defaultPrevented=!0},defaultPrevented:!1},h=[k].concat(Ba.call(arguments,1)),l,m;do{d=f.$$listeners[a]||c;k.currentScope=f;l=0;for(m=d.length;lc.msieDocumentMode)throw xa("iequirks");var e=ha(ga);e.isEnabled=function(){return b};e.trustAs=d.trustAs;e.getTrusted=d.getTrusted;e.valueOf=d.valueOf;b||(e.trustAs=e.getTrusted=function(a,b){return b},
+e.valueOf=Qa);e.parseAs=function(b,c){var d=a(c);return d.literal&&d.constant?d:function(a,c){return e.getTrusted(b,d(a,c))}};var f=e.parseAs,g=e.getTrusted,k=e.trustAs;r(ga,function(a,b){var c=M(b);e[Za("parse_as_"+c)]=function(b){return f(a,b)};e[Za("get_trusted_"+c)]=function(b){return g(a,b)};e[Za("trust_as_"+c)]=function(b){return k(a,b)}});return e}]}function ce(){this.$get=["$window","$document",function(b,a){var c={},d=U((/android (\d+)/.exec(M((b.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||
+{}).userAgent),f=a[0]||{},g=f.documentMode,k,m=/^(Moz|webkit|O|ms)(?=[A-Z])/,h=f.body&&f.body.style,l=!1,n=!1;if(h){for(var p in h)if(l=m.exec(p)){k=l[0];k=k.substr(0,1).toUpperCase()+k.substr(1);break}k||(k="WebkitOpacity"in h&&"webkit");l=!!("transition"in h||k+"Transition"in h);n=!!("animation"in h||k+"Animation"in h);!d||l&&n||(l=A(f.body.style.webkitTransition),n=A(f.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hashchange:"onhashchange"in b&&(!g||7<
+g),hasEvent:function(a){if("input"==a&&9==Q)return!1;if(y(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:Xa(),vendorPrefix:k,transitions:l,animations:n,android:d,msie:Q,msieDocumentMode:g}}]}function ee(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,k,m){var h=c.defer(),l=h.promise,n=z(m)&&!m;k=a.defer(function(){try{h.resolve(e())}catch(a){h.reject(a),d(a)}finally{delete f[l.$$timeoutId]}n||b.$apply()},k);l.$$timeoutId=k;f[k]=h;
+return l}var f={};e.cancel=function(b){return b&&b.$$timeoutId in f?(f[b.$$timeoutId].reject("canceled"),delete f[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return e}]}function ua(b,a){var c=b;Q&&(Y.setAttribute("href",c),c=Y.href);Y.setAttribute("href",c);return{href:Y.href,protocol:Y.protocol?Y.protocol.replace(/:$/,""):"",host:Y.host,search:Y.search?Y.search.replace(/^\?/,""):"",hash:Y.hash?Y.hash.replace(/^#/,""):"",hostname:Y.hostname,port:Y.port,pathname:"/"===Y.pathname.charAt(0)?Y.pathname:
+"/"+Y.pathname}}function Pb(b){b=A(b)?ua(b):b;return b.protocol===Hc.protocol&&b.host===Hc.host}function fe(){this.$get=ba(W)}function mc(b){function a(d,e){if(T(d)){var f={};r(d,function(b,c){f[c]=a(c,b)});return f}return b.factory(d+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Ic);a("date",Jc);a("filter",Ce);a("json",De);a("limitTo",Ee);a("lowercase",Fe);a("number",Kc);a("orderBy",Lc);a("uppercase",Ge)}function Ce(){return function(b,
+a,c){if(!I(b))return b;var d=typeof c,e=[];e.check=function(a){for(var b=0;bb;b=Math.abs(b);var g=b+"",k="",m=[],h=!1;if(-1!==g.indexOf("e")){var l=g.match(/([\d\.]+)e(-?)(\d+)/);l&&"-"==l[2]&&
+l[3]>e+1?(g="0",b=0):(k=g,h=!0)}if(h)0b)&&(k=b.toFixed(e));else{g=(g.split(Nc)[1]||"").length;y(e)&&(e=Math.min(Math.max(a.minFrac,g),a.maxFrac));b=+(Math.round(+(b.toString()+"e"+e)).toString()+"e"+-e);0===b&&(f=!1);b=(""+b).split(Nc);g=b[0];b=b[1]||"";var l=0,n=a.lgSize,p=a.gSize;if(g.length>=n+p)for(l=g.length-n,h=0;hb&&(d="-",b=-b);for(b=""+b;b.length-c)e+=c;0===e&&-12==c&&(e=12);return Xb(e,a,d)}}function vb(b,a){return function(c,d){var e=c["get"+b](),f=Ia(a?"SHORT"+b:b);return d[f][e]}}function Jc(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,g=0,k=b[8]?
+a.setUTCFullYear:a.setFullYear,m=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=U(b[9]+b[10]),g=U(b[9]+b[11]));k.call(a,U(b[1]),U(b[2])-1,U(b[3]));f=U(b[4]||0)-f;g=U(b[5]||0)-g;k=U(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));m.call(a,f,g,k,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e){var f="",g=[],k,m;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;A(c)&&(c=He.test(c)?U(c):a(c));ib(c)&&(c=new Date(c));
+if(!ta(c))return c;for(;e;)(m=Ie.exec(e))?(g=g.concat(Ba.call(m,1)),e=g.pop()):(g.push(e),e=null);r(g,function(a){k=Je[a];f+=k?k(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return f}}function De(){return function(b){return na(b,!0)}}function Ee(){return function(b,a){if(!I(b)&&!A(b))return b;a=Infinity===Math.abs(Number(a))?Number(a):U(a);if(A(b))return a?0<=a?b.slice(0,a):b.slice(a,b.length):"";var c=[],d,e;a>b.length?a=b.length:a<-b.length&&(a=-b.length);0 a||37<=a&&40>=a)||q()});if(e.hasEvent("paste"))a.on("paste cut",q)}a.on("change",n);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)};var s=c.ngPattern;s&&((e=s.match(/^\/(.*)\/([gim]*)$/))?(s=RegExp(e[1],e[2]),e=function(a){return sa(d,
+"pattern",d.$isEmpty(a)||s.test(a),a)}):e=function(c){var e=b.$eval(s);if(!e||!e.test)throw D("ngPattern")("noregexp",s,e,ia(a));return sa(d,"pattern",d.$isEmpty(c)||e.test(c),c)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var r=U(c.ngMinlength);e=function(a){return sa(d,"minlength",d.$isEmpty(a)||a.length>=r,a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var u=U(c.ngMaxlength);e=function(a){return sa(d,"maxlength",d.$isEmpty(a)||a.length<=u,a)};d.$parsers.push(e);
+d.$formatters.push(e)}}function Yb(b,a){b="ngClass"+b;return["$animate",function(c){function d(a,b){var c=[],d=0;a:for(;d Q?function(b){b=b.nodeName?b:b[0];return b.scopeName&&"HTML"!=b.scopeName?Ia(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var Xa=function(){if(z(Xa.isActive_))return Xa.isActive_;var b=!(!X.querySelector("[ng-csp]")&&!X.querySelector("[data-ng-csp]"));
+if(!b)try{new Function("")}catch(a){b=!0}return Xa.isActive_=b},Yc=/[A-Z]/g,ad={full:"1.2.25",major:1,minor:2,dot:25,codeName:"hypnotic-gesticulation"};S.expando="ng339";var ab=S.cache={},ne=1,sb=W.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},$a=W.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)};S._data=function(b){return this.cache[b[this.expando]]||{}};var ie=/([\:\-\_]+(.))/g,
+je=/^moz([A-Z])/,Hb=D("jqLite"),ke=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Ib=/<|?\w+;/,le=/<([\w:]+)/,me=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ea={option:[1,''," "],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};ea.optgroup=ea.option;ea.tbody=ea.tfoot=ea.colgroup=ea.caption=ea.thead;ea.th=
+ea.td;var La=S.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===X.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),S(W).on("load",a))},toString:function(){var b=[];r(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?v(this[b]):v(this[this.length+b])},length:0,push:Me,sort:[].sort,splice:[].splice},qb={};r("multiple selected checked disabled readOnly required open".split(" "),function(b){qb[M(b)]=b});var rc={};r("input select option textarea button form details".split(" "),
+function(b){rc[Ia(b)]=!0});r({data:Mb,removeData:Lb},function(b,a){S[a]=b});r({data:Mb,inheritedData:pb,scope:function(b){return v.data(b,"$scope")||pb(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return v.data(b,"$isolateScope")||v.data(b,"$isolateScopeNoTemplate")},controller:oc,injector:function(b){return pb(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Nb,css:function(b,a,c){a=Za(a);if(z(c))b.style[a]=c;else{var d;8>=Q&&(d=b.currentStyle&&b.currentStyle[a],
+""===d&&(d="auto"));d=d||b.style[a];8>=Q&&(d=""===d?t:d);return d}},attr:function(b,a,c){var d=M(a);if(qb[d])if(z(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||F).specified?d:t;else if(z(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?t:b},prop:function(b,a,c){if(z(c))b[a]=c;else return b[a]},text:function(){function b(b,d){var e=a[b.nodeType];if(y(d))return e?b[e]:"";b[e]=d}var a=[];9>Q?(a[1]=
+"innerText",a[3]="nodeValue"):a[1]=a[3]="textContent";b.$dv="";return b}(),val:function(b,a){if(y(a)){if("SELECT"===Ma(b)&&b.multiple){var c=[];r(b.options,function(a){a.selected&&c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(y(a))return b.innerHTML;for(var c=0,d=b.childNodes;c":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Re={n:"\n",f:"\f",r:"\r",
+t:"\t",v:"\v","'":"'",'"':'"'},Wb=function(a){this.options=a};Wb.prototype={constructor:Wb,lex:function(a){this.text=a;this.index=0;this.ch=t;this.lastCh=":";for(this.tokens=[];this.index=a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=z(c)?"s "+c+"-"+this.index+" ["+
+this.text.substring(c,d)+"]":" "+d;throw la("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index","<=",">="))a=this.binaryFn(a,c.fn,this.relational());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.fn,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.fn,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn(fb.ZERO,a.fn,
+this.unary()):(a=this.expect("!"))?this.unaryFn(a.fn,this.unary()):this.primary()},fieldAccess:function(a){var c=this,d=this.expect().text,e=Ec(d,this.options,this.text);return J(function(c,d,k){return e(k||a(c,d))},{assign:function(e,g,k){(k=a(e,k))||a.assign(e,k={});return ub(k,d,g,c.text,c.options)}})},objectIndex:function(a){var c=this,d=this.expression();this.consume("]");return J(function(e,f){var g=a(e,f),k=d(e,f),m;ka(k,c.text);if(!g)return t;(g=va(g[k],c.text))&&(g.then&&c.options.unwrapPromises)&&
+(m=g,"$$v"in g||(m.$$v=t,m.then(function(a){m.$$v=a})),g=g.$$v);return g},{assign:function(e,f,g){var k=ka(d(e,g),c.text);(g=va(a(e,g),c.text))||a.assign(e,g={});return g[k]=f}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());while(this.expect(","))}this.consume(")");var e=this;return function(f,g){for(var k=[],m=c?c(f,g):f,h=0;ha.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(Xb(Math[0<
+a?"floor":"ceil"](a/60),2)+Xb(Math.abs(a%60),2))}},Ie=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,He=/^\-?\d+$/;Jc.$inject=["$locale"];var Fe=ba(M),Ge=ba(Ia);Lc.$inject=["$parse"];var dd=ba({restrict:"E",compile:function(a,c){8>=Q&&(c.href||c.name||c.$set("href",""),a.append(X.createComment("IE fix")));if(!c.href&&!c.xlinkHref&&!c.name)return function(a,c){var f="[object SVGAnimatedString]"===za.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(f)||
+a.preventDefault()})}}}),Fb={};r(qb,function(a,c){if("multiple"!=a){var d=pa("ng-"+c);Fb[d]=function(){return{priority:100,link:function(a,f,g){a.$watch(g[d],function(a){g.$set(c,!!a)})}}}}});r(["src","srcset","href"],function(a){var c=pa("ng-"+a);Fb[c]=function(){return{priority:99,link:function(d,e,f){var g=a,k=a;"href"===a&&"[object SVGAnimatedString]"===za.call(e.prop("href"))&&(k="xlinkHref",f.$attr[k]="xlink:href",g=null);f.$observe(c,function(c){c?(f.$set(k,c),Q&&g&&e.prop(g,f[k])):"href"===
+a&&f.$set(k,null)})}}}});var yb={$addControl:F,$removeControl:F,$setValidity:F,$setDirty:F,$setPristine:F};Oc.$inject=["$element","$attrs","$scope","$animate"];var Rc=function(a){return["$timeout",function(c){return{name:"form",restrict:a?"EAC":"E",controller:Oc,compile:function(){return{pre:function(a,e,f,g){if(!f.action){var k=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};sb(e[0],"submit",k);e.on("$destroy",function(){c(function(){$a(e[0],"submit",k)},0,!1)})}var m=e.parent().controller("form"),
+h=f.name||f.ngForm;h&&ub(a,h,g,h);if(m)e.on("$destroy",function(){m.$removeControl(g);h&&ub(a,h,t,h);J(g,yb)})}}}}}]},ed=Rc(),rd=Rc(!0),Se=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,Te=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,Ue=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Sc={text:Ab,number:function(a,c,d,e,f,g){Ab(a,c,d,e,f,g);e.$parsers.push(function(a){var c=e.$isEmpty(a);if(c||Ue.test(a))return e.$setValidity("number",
+!0),""===a?null:c?a:parseFloat(a);e.$setValidity("number",!1);return t});Ke(e,"number",Ve,null,e.$$validityState);e.$formatters.push(function(a){return e.$isEmpty(a)?"":""+a});d.min&&(a=function(a){var c=parseFloat(d.min);return sa(e,"min",e.$isEmpty(a)||a>=c,a)},e.$parsers.push(a),e.$formatters.push(a));d.max&&(a=function(a){var c=parseFloat(d.max);return sa(e,"max",e.$isEmpty(a)||a<=c,a)},e.$parsers.push(a),e.$formatters.push(a));e.$formatters.push(function(a){return sa(e,"number",e.$isEmpty(a)||
+ib(a),a)})},url:function(a,c,d,e,f,g){Ab(a,c,d,e,f,g);a=function(a){return sa(e,"url",e.$isEmpty(a)||Se.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,f,g){Ab(a,c,d,e,f,g);a=function(a){return sa(e,"email",e.$isEmpty(a)||Te.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){y(d.name)&&c.attr("name",hb());c.on("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};
+d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var f=d.ngTrueValue,g=d.ngFalseValue;A(f)||(f=!0);A(g)||(g=!1);c.on("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return a!==f};e.$formatters.push(function(a){return a===f});e.$parsers.push(function(a){return a?f:g})},hidden:F,button:F,submit:F,reset:F,file:F},Ve=["badInput"],jc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",
+link:function(d,e,f,g){g&&(Sc[M(f.type)]||Sc.text)(d,e,f,g,c,a)}}}],wb="ng-valid",xb="ng-invalid",Oa="ng-pristine",zb="ng-dirty",We=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate",function(a,c,d,e,f,g){function k(a,c){c=c?"-"+mb(c,"-"):"";g.removeClass(e,(a?xb:wb)+c);g.addClass(e,(a?wb:xb)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=
+d.name;var m=f(d.ngModel),h=m.assign;if(!h)throw D("ngModel")("nonassign",d.ngModel,ia(e));this.$render=F;this.$isEmpty=function(a){return y(a)||""===a||null===a||a!==a};var l=e.inheritedData("$formController")||yb,n=0,p=this.$error={};e.addClass(Oa);k(!0);this.$setValidity=function(a,c){p[a]!==!c&&(c?(p[a]&&n--,n||(k(!0),this.$valid=!0,this.$invalid=!1)):(k(!1),this.$invalid=!0,this.$valid=!1,n++),p[a]=!c,k(c,a),l.$setValidity(a,c,this))};this.$setPristine=function(){this.$dirty=!1;this.$pristine=
+!0;g.removeClass(e,zb);g.addClass(e,Oa)};this.$setViewValue=function(d){this.$viewValue=d;this.$pristine&&(this.$dirty=!0,this.$pristine=!1,g.removeClass(e,Oa),g.addClass(e,zb),l.$setDirty());r(this.$parsers,function(a){d=a(d)});this.$modelValue!==d&&(this.$modelValue=d,h(a,d),r(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}}))};var q=this;a.$watch(function(){var c=m(a);if(q.$modelValue!==c){var d=q.$formatters,e=d.length;for(q.$modelValue=c;e--;)c=d[e](c);q.$viewValue!==c&&(q.$viewValue=
+c,q.$render())}return c})}],Gd=function(){return{require:["ngModel","^?form"],controller:We,link:function(a,c,d,e){var f=e[0],g=e[1]||yb;g.$addControl(f);a.$on("$destroy",function(){g.$removeControl(f)})}}},Id=ba({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),kc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var f=function(a){if(d.required&&e.$isEmpty(a))e.$setValidity("required",!1);else return e.$setValidity("required",
+!0),a};e.$formatters.push(f);e.$parsers.unshift(f);d.$observe("required",function(){f(e.$viewValue)})}}}},Hd=function(){return{require:"ngModel",link:function(a,c,d,e){var f=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){if(!y(a)){var c=[];a&&r(a.split(f),function(a){a&&c.push(aa(a))});return c}});e.$formatters.push(function(a){return I(a)?a.join(", "):t});e.$isEmpty=function(a){return!a||!a.length}}}},Xe=/^(true|false|\d+)$/,Jd=function(){return{priority:100,
+compile:function(a,c){return Xe.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",a)})}}}},jd=ya({compile:function(a){a.addClass("ng-binding");return function(a,d,e){d.data("$binding",e.ngBind);a.$watch(e.ngBind,function(a){d.text(a==t?"":a)})}}}),ld=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],
+kd=["$sce","$parse",function(a,c){return{compile:function(d){d.addClass("ng-binding");return function(d,f,g){f.data("$binding",g.ngBindHtml);var k=c(g.ngBindHtml);d.$watch(function(){return(k(d)||"").toString()},function(c){f.html(a.getTrustedHtml(k(d))||"")})}}}}],md=Yb("",!0),od=Yb("Odd",0),nd=Yb("Even",1),pd=ya({compile:function(a,c){c.$set("ngCloak",t);a.removeClass("ng-cloak")}}),qd=[function(){return{scope:!0,controller:"@",priority:500}}],lc={},Ye={blur:!0,focus:!0};r("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),
+function(a){var c=pa("ng-"+a);lc[c]=["$parse","$rootScope",function(d,e){return{compile:function(f,g){var k=d(g[c]);return function(c,d){d.on(a,function(d){var f=function(){k(c,{$event:d})};Ye[a]&&e.$$phase?c.$evalAsync(f):c.$apply(f)})}}}}]});var td=["$animate",function(a){return{transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,f,g){var k,m,h;c.$watch(e.ngIf,function(f){Ua(f)?m||(m=c.$new(),g(m,function(c){c[c.length++]=X.createComment(" end ngIf: "+e.ngIf+
+" ");k={clone:c};a.enter(c,d.parent(),d)})):(h&&(h.remove(),h=null),m&&(m.$destroy(),m=null),k&&(h=Eb(k.clone),a.leave(h,function(){h=null}),k=null))})}}}],ud=["$http","$templateCache","$anchorScroll","$animate","$sce",function(a,c,d,e,f){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:Va.noop,compile:function(g,k){var m=k.ngInclude||k.src,h=k.onload||"",l=k.autoscroll;return function(g,k,q,r,E){var u=0,t,v,R,w=function(){v&&(v.remove(),v=null);t&&(t.$destroy(),t=null);
+R&&(e.leave(R,function(){v=null}),v=R,R=null)};g.$watch(f.parseAsResourceUrl(m),function(f){var m=function(){!z(l)||l&&!g.$eval(l)||d()},q=++u;f?(a.get(f,{cache:c}).success(function(a){if(q===u){var c=g.$new();r.template=a;a=E(c,function(a){w();e.enter(a,null,k,m)});t=c;R=a;t.$emit("$includeContentLoaded");g.$eval(h)}}).error(function(){q===u&&w()}),g.$emit("$includeContentRequested")):(w(),r.template=null)})}}}}],Kd=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",
+link:function(c,d,e,f){d.html(f.template);a(d.contents())(c)}}}],vd=ya({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),wd=ya({terminal:!0,priority:1E3}),xd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,f,g){var k=g.count,m=g.$attr.when&&f.attr(g.$attr.when),h=g.offset||0,l=e.$eval(m)||{},n={},p=c.startSymbol(),q=c.endSymbol(),s=/^when(Minus)?(.+)$/;r(g,function(a,c){s.test(c)&&(l[M(c.replace("when","").replace("Minus","-"))]=
+f.attr(g.$attr[c]))});r(l,function(a,e){n[e]=c(a.replace(d,p+k+"-"+h+q))});e.$watch(function(){var c=parseFloat(e.$eval(k));if(isNaN(c))return"";c in l||(c=a.pluralCat(c-h));return n[c](e,f,!0)},function(a){f.text(a)})}}}],yd=["$parse","$animate",function(a,c){var d=D("ngRepeat");return{transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,link:function(e,f,g,k,m){var h=g.ngRepeat,l=h.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),n,p,q,s,t,u,B={$id:Ka};if(!l)throw d("iexp",
+h);g=l[1];k=l[2];(l=l[3])?(n=a(l),p=function(a,c,d){u&&(B[u]=a);B[t]=c;B.$index=d;return n(e,B)}):(q=function(a,c){return Ka(c)},s=function(a){return a});l=g.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!l)throw d("iidexp",g);t=l[3]||l[1];u=l[2];var z={};e.$watchCollection(k,function(a){var g,k,l=f[0],n,B={},C,x,H,A,F,D,y,I=[];if(Pa(a))D=a,F=p||q;else{F=p||s;D=[];for(H in a)a.hasOwnProperty(H)&&"$"!=H.charAt(0)&&D.push(H);D.sort()}C=D.length;k=I.length=D.length;for(g=0;gx;)t.pop().element.remove()}for(;y.length>L;)y.pop()[0].element.remove()}var h;if(!(h=s.match(d)))throw Ze("iexp",s,ia(f));var l=c(h[2]||h[1]),m=h[4]||h[6],n=h[5],p=c(h[3]||""),r=c(h[2]?h[1]:m),u=c(h[7]),v=h[8]?c(h[8]):null,y=[[{element:f,
+label:""}]];E&&(a(E)(e),E.removeClass("ng-scope"),E.remove());f.empty();f.on("change",function(){e.$apply(function(){var a,c=u(e)||[],d={},h,l,p,s,w,z,x;if(q)for(l=[],s=0,z=y.length;s@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}.ng-animate-block-transitions{transition:0s all!important;-webkit-transition:0s all!important;}.ng-hide-add-active,.ng-hide-remove{display:block!important;}');
+//# sourceMappingURL=angular.min.js.map
+
+/*!
+ * ionic.bundle.js is a concatenation of:
+ * ionic.js, angular.js, angular-animate.js,
+ * angular-sanitize.js, angular-ui-router.js,
+ * and ionic-angular.js
+ */
+
+/*
+ AngularJS v1.2.25
+ (c) 2010-2014 Google, Inc. http://angularjs.org
+ License: MIT
+*/
+(function(F,e,O){'use strict';e.module("ngAnimate",["ng"]).directive("ngAnimateChildren",function(){return function(G,s,g){g=g.ngAnimateChildren;e.isString(g)&&0===g.length?s.data("$$ngAnimateChildren",!0):G.$watch(g,function(e){s.data("$$ngAnimateChildren",!!e)})}}).factory("$$animateReflow",["$$rAF","$document",function(e,s){return function(g){return e(function(){g()})}}]).config(["$provide","$animateProvider",function(G,s){function g(e){for(var g=0;g=y&&b>=v&&e()}var h=g(b);a=b.data(q);if(-1!=h.getAttribute("class").indexOf(d)&&a){var m="";u(d.split(" "),function(a,b){m+=(0=c;e--)d.end&&d.end(f[e]);f.length=c}}"string"!==typeof a&&(a=null===a||"undefined"===typeof a?"":""+a);var b,l,f=[],n=a,h;for(f.last=function(){return f[f.length-1]};a;){h="";l=!0;if(f.last()&&y[f.last()])a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+f.last()+"[^>]*>","i"),function(a,b){b=b.replace(I,"$1").replace(J,"$1");d.chars&&d.chars(s(b));return""}),e("",f.last());else{if(0===a.indexOf("\x3c!--"))b=a.indexOf("--",4),0<=b&&a.lastIndexOf("--\x3e",b)===b&&(d.comment&&d.comment(a.substring(4,
+b)),a=a.substring(b+3),l=!1);else if(z.test(a)){if(b=a.match(z))a=a.replace(b[0],""),l=!1}else if(K.test(a)){if(b=a.match(A))a=a.substring(b[0].length),b[0].replace(A,e),l=!1}else L.test(a)&&((b=a.match(B))?(b[4]&&(a=a.substring(b[0].length),b[0].replace(B,c)),l=!1):(h+="<",a=a.substring(1)));l&&(b=a.indexOf("<"),h+=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),d.chars&&d.chars(s(h)))}if(a==n)throw M("badparse",a);n=a}e()}function s(a){if(!a)return"";var d=N.exec(a);a=d[1];var c=d[3];if(d=d[2])p.innerHTML=
+d.replace(//g,">")}function t(a,d){var c=!1,e=g.bind(a,a.push);return{start:function(a,l,f){a=g.lowercase(a);!c&&y[a]&&(c=a);c||!0!==D[a]||(e("<"),e(a),g.forEach(l,function(c,f){var k=
+g.lowercase(f),l="img"===a&&"src"===k||"background"===k;!0!==Q[k]||!0===E[k]&&!d(c,l)||(e(" "),e(f),e('="'),e(C(c)),e('"'))}),e(f?"/>":">"))},end:function(a){a=g.lowercase(a);c||!0!==D[a]||(e(""),e(a),e(">"));a==c&&(c=!1)},chars:function(a){c||e(C(a))}}}var M=g.$$minErr("$sanitize"),B=/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,A=/^<\/\s*([\w:-]+)[^>]*>/,H=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,L=/^,
+K=/^<\//,I=/\x3c!--(.*?)--\x3e/g,z=/]*?)>/i,J=/"]/,c=/^mailto:/;return function(e,b){function l(a){a&&k.push(F(a))}function f(a,c){k.push("');l(c);k.push(" ")}
+if(!e)return e;for(var n,h=e,k=[],m,p;n=h.match(d);)m=n[0],n[2]==n[3]&&(m="mailto:"+m),p=n.index,l(h.substr(0,p)),f(m,n[0].replace(c,"")),h=h.substring(p+n[0].length);l(h);return a(k.join(""))}}])})(window,window.angular);
+//# sourceMappingURL=angular-sanitize.min.js.map
+
+/*!
+ * ionic.bundle.js is a concatenation of:
+ * ionic.js, angular.js, angular-animate.js,
+ * angular-sanitize.js, angular-ui-router.js,
+ * and ionic-angular.js
+ */
+
+/**
+ * State-based routing for AngularJS
+ * @version v0.2.10
+ * @link http://angular-ui.github.com/
+ * @license MIT License, http://www.opensource.org/licenses/MIT
+ */
+"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ui.router"),function(a,b,c){"use strict";function d(a,b){return I(new(I(function(){},{prototype:a})),b)}function e(a){return H(arguments,function(b){b!==a&&H(b,function(b,c){a.hasOwnProperty(c)||(a[c]=b)})}),a}function f(a,b){var c=[];for(var d in a.path){if(a.path[d]!==b.path[d])break;c.push(a.path[d])}return c}function g(a,b){if(Array.prototype.indexOf)return a.indexOf(b,Number(arguments[2])||0);var c=a.length>>>0,d=Number(arguments[2])||0;for(d=0>d?Math.ceil(d):Math.floor(d),0>d&&(d+=c);c>d;d++)if(d in a&&a[d]===b)return d;return-1}function h(a,b,c,d){var e,h=f(c,d),i={},j=[];for(var k in h)if(h[k].params&&h[k].params.length){e=h[k].params;for(var l in e)g(j,e[l])>=0||(j.push(e[l]),i[e[l]]=a[e[l]])}return I({},i,b)}function i(a,b){var c={};return H(a,function(a){var d=b[a];c[a]=null!=d?String(d):null}),c}function j(a,b,c){if(!c){c=[];for(var d in a)c.push(d)}for(var e=0;e "));if(o[c]=d,E(a))m.push(c,[function(){return b.get(a)}],h);else{var e=b.annotate(a);H(e,function(a){a!==c&&g.hasOwnProperty(a)&&k(g[a],a)}),m.push(c,a,e)}n.pop(),o[c]=f}}function l(a){return F(a)&&a.then&&a.$$promises}if(!F(g))throw new Error("'invocables' must be an object");var m=[],n=[],o={};return H(g,k),g=n=o=null,function(d,f,g){function h(){--s||(t||e(r,f.$$values),p.$$values=r,p.$$promises=!0,o.resolve(r))}function k(a){p.$$failure=a,o.reject(a)}function n(c,e,f){function i(a){l.reject(a),k(a)}function j(){if(!C(p.$$failure))try{l.resolve(b.invoke(e,g,r)),l.promise.then(function(a){r[c]=a,h()},i)}catch(a){i(a)}}var l=a.defer(),m=0;H(f,function(a){q.hasOwnProperty(a)&&!d.hasOwnProperty(a)&&(m++,q[a].then(function(b){r[a]=b,--m||j()},i))}),m||j(),q[c]=l.promise}if(l(d)&&g===c&&(g=f,f=d,d=null),d){if(!F(d))throw new Error("'locals' must be an object")}else d=i;if(f){if(!l(f))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else f=j;var o=a.defer(),p=o.promise,q=p.$$promises={},r=I({},d),s=1+m.length/3,t=!1;if(C(f.$$failure))return k(f.$$failure),p;f.$$values?(t=e(r,f.$$values),h()):(I(q,f.$$promises),f.then(h,k));for(var u=0,v=m.length;v>u;u+=3)d.hasOwnProperty(m[u])?h():n(m[u],m[u+1],m[u+2]);return p}},this.resolve=function(a,b,c,d){return this.study(a)(b,c,d)}}function m(a,b,c){this.fromConfig=function(a,b,c){return C(a.template)?this.fromString(a.template,b):C(a.templateUrl)?this.fromUrl(a.templateUrl,b):C(a.templateProvider)?this.fromProvider(a.templateProvider,b,c):null},this.fromString=function(a,b){return D(a)?a(b):a},this.fromUrl=function(c,d){return D(c)&&(c=c(d)),null==c?null:a.get(c,{cache:b}).then(function(a){return a.data})},this.fromProvider=function(a,b,d){return c.invoke(a,null,d||{params:b})}}function n(a){function b(b){if(!/^\w+(-+\w+)*$/.test(b))throw new Error("Invalid parameter name '"+b+"' in pattern '"+a+"'");if(f[b])throw new Error("Duplicate parameter name '"+b+"' in pattern '"+a+"'");f[b]=!0,j.push(b)}function c(a){return a.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&")}var d,e=/([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,f={},g="^",h=0,i=this.segments=[],j=this.params=[];this.source=a;for(var k,l,m;(d=e.exec(a))&&(k=d[2]||d[3],l=d[4]||("*"==d[1]?".*":"[^/]*"),m=a.substring(h,d.index),!(m.indexOf("?")>=0));)g+=c(m)+"("+l+")",b(k),i.push(m),h=e.lastIndex;m=a.substring(h);var n=m.indexOf("?");if(n>=0){var o=this.sourceSearch=m.substring(n);m=m.substring(0,n),this.sourcePath=a.substring(0,h+n),H(o.substring(1).split(/[&?]/),b)}else this.sourcePath=a,this.sourceSearch="";g+=c(m)+"$",i.push(m),this.regexp=new RegExp(g),this.prefix=i[0]}function o(){this.compile=function(a){return new n(a)},this.isMatcher=function(a){return F(a)&&D(a.exec)&&D(a.format)&&D(a.concat)},this.$get=function(){return this}}function p(a){function b(a){var b=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(a.source);return null!=b?b[1].replace(/\\(.)/g,"$1"):""}function c(a,b){return a.replace(/\$(\$|\d{1,2})/,function(a,c){return b["$"===c?0:Number(c)]})}function d(a,b,c){if(!c)return!1;var d=a.invoke(b,b,{$match:c});return C(d)?d:!0}var e=[],f=null;this.rule=function(a){if(!D(a))throw new Error("'rule' must be a function");return e.push(a),this},this.otherwise=function(a){if(E(a)){var b=a;a=function(){return b}}else if(!D(a))throw new Error("'rule' must be a function");return f=a,this},this.when=function(e,f){var g,h=E(f);if(E(e)&&(e=a.compile(e)),!h&&!D(f)&&!G(f))throw new Error("invalid 'handler' in when()");var i={matcher:function(b,c){return h&&(g=a.compile(c),c=["$match",function(a){return g.format(a)}]),I(function(a,e){return d(a,c,b.exec(e.path(),e.search()))},{prefix:E(b.prefix)?b.prefix:""})},regex:function(a,e){if(a.global||a.sticky)throw new Error("when() RegExp must not be global or sticky");return h&&(g=e,e=["$match",function(a){return c(g,a)}]),I(function(b,c){return d(b,e,a.exec(c.path()))},{prefix:b(a)})}},j={matcher:a.isMatcher(e),regex:e instanceof RegExp};for(var k in j)if(j[k])return this.rule(i[k](e,f));throw new Error("invalid 'what' in when()")},this.$get=["$location","$rootScope","$injector",function(a,b,c){function d(b){function d(b){var d=b(c,a);return d?(E(d)&&a.replace().url(d),!0):!1}if(!b||!b.defaultPrevented){var g,h=e.length;for(g=0;h>g;g++)if(d(e[g]))return;f&&d(f)}}return b.$on("$locationChangeSuccess",d),{sync:function(){d()}}}]}function q(a,e,f){function g(a){return 0===a.indexOf(".")||0===a.indexOf("^")}function l(a,b){var d=E(a),e=d?a:a.name,f=g(e);if(f){if(!b)throw new Error("No reference point given for path '"+e+"'");for(var h=e.split("."),i=0,j=h.length,k=b;j>i;i++)if(""!==h[i]||0!==i){if("^"!==h[i])break;if(!k.parent)throw new Error("Path '"+e+"' not valid for state '"+b.name+"'");k=k.parent}else k=b;h=h.slice(i).join("."),e=k.name+(k.name&&h?".":"")+h}var l=w[e];return!l||!d&&(d||l!==a&&l.self!==a)?c:l}function m(a,b){x[a]||(x[a]=[]),x[a].push(b)}function n(b){b=d(b,{self:b,resolve:b.resolve||{},toString:function(){return this.name}});var c=b.name;if(!E(c)||c.indexOf("@")>=0)throw new Error("State must have a valid name");if(w.hasOwnProperty(c))throw new Error("State '"+c+"'' is already defined");var e=-1!==c.indexOf(".")?c.substring(0,c.lastIndexOf(".")):E(b.parent)?b.parent:"";if(e&&!w[e])return m(e,b.self);for(var f in z)D(z[f])&&(b[f]=z[f](b,z.$delegates[f]));if(w[c]=b,!b[y]&&b.url&&a.when(b.url,["$match","$stateParams",function(a,c){v.$current.navigable==b&&j(a,c)||v.transitionTo(b,a,{location:!1})}]),x[c])for(var g=0;g-1}function p(a){var b=a.split("."),c=v.$current.name.split(".");if("**"===b[0]&&(c=c.slice(c.indexOf(b[1])),c.unshift("**")),"**"===b[b.length-1]&&(c.splice(c.indexOf(b[b.length-2])+1,Number.MAX_VALUE),c.push("**")),b.length!=c.length)return!1;for(var d=0,e=b.length;e>d;d++)"*"===b[d]&&(c[d]="*");return c.join("")===b.join("")}function q(a,b){return E(a)&&!C(b)?z[a]:D(b)&&E(a)?(z[a]&&!z.$delegates[a]&&(z.$delegates[a]=z[a]),z[a]=b,this):this}function r(a,b){return F(a)?b=a:b.name=a,n(b),this}function s(a,e,g,m,n,q,r,s,x){function z(){r.url()!==M&&(r.url(M),r.replace())}function A(a,c,d,f,h){var i=d?c:k(a.params,c),j={$stateParams:i};h.resolve=n.resolve(a.resolve,j,h.resolve,a);var l=[h.resolve.then(function(a){h.globals=a})];return f&&l.push(f),H(a.views,function(c,d){var e=c.resolve&&c.resolve!==a.resolve?c.resolve:{};e.$template=[function(){return g.load(d,{view:c,locals:j,params:i,notify:!1})||""}],l.push(n.resolve(e,j,h.resolve,a).then(function(f){if(D(c.controllerProvider)||G(c.controllerProvider)){var g=b.extend({},e,j);f.$$controller=m.invoke(c.controllerProvider,null,g)}else f.$$controller=c.controller;f.$$state=a,f.$$controllerAs=c.controllerAs,h[d]=f}))}),e.all(l).then(function(){return h})}var B=e.reject(new Error("transition superseded")),F=e.reject(new Error("transition prevented")),K=e.reject(new Error("transition aborted")),L=e.reject(new Error("transition failed")),M=r.url(),N=x.baseHref();return u.locals={resolve:null,globals:{$stateParams:{}}},v={params:{},current:u.self,$current:u,transition:null},v.reload=function(){v.transitionTo(v.current,q,{reload:!0,inherit:!1,notify:!1})},v.go=function(a,b,c){return this.transitionTo(a,b,I({inherit:!0,relative:v.$current},c))},v.transitionTo=function(b,c,f){c=c||{},f=I({location:!0,inherit:!1,relative:null,notify:!0,reload:!1,$retry:!1},f||{});var g,k=v.$current,n=v.params,o=k.path,p=l(b,f.relative);if(!C(p)){var s={to:b,toParams:c,options:f};if(g=a.$broadcast("$stateNotFound",s,k.self,n),g.defaultPrevented)return z(),K;if(g.retry){if(f.$retry)return z(),L;var w=v.transition=e.when(g.retry);return w.then(function(){return w!==v.transition?B:(s.options.$retry=!0,v.transitionTo(s.to,s.toParams,s.options))},function(){return K}),z(),w}if(b=s.to,c=s.toParams,f=s.options,p=l(b,f.relative),!C(p)){if(f.relative)throw new Error("Could not resolve '"+b+"' from state '"+f.relative+"'");throw new Error("No such state '"+b+"'")}}if(p[y])throw new Error("Cannot transition to abstract state '"+b+"'");f.inherit&&(c=h(q,c||{},v.$current,p)),b=p;var x,D,E=b.path,G=u.locals,H=[];for(x=0,D=E[x];D&&D===o[x]&&j(c,n,D.ownParams)&&!f.reload;x++,D=E[x])G=H[x]=D.locals;if(t(b,k,G,f))return b.self.reloadOnSearch!==!1&&z(),v.transition=null,e.when(v.current);if(c=i(b.params,c||{}),f.notify&&(g=a.$broadcast("$stateChangeStart",b.self,c,k.self,n),g.defaultPrevented))return z(),F;for(var N=e.when(G),O=x;O=x;d--)g=o[d],g.self.onExit&&m.invoke(g.self.onExit,g.self,g.locals.globals),g.locals=null;for(d=x;d1||b.ctrlKey||b.metaKey||b.shiftKey||f.attr("target")||(c(function(){a.go(i.state,j,o)}),b.preventDefault())})}}}function y(a,b,c){return{restrict:"A",controller:["$scope","$element","$attrs",function(d,e,f){function g(){a.$current.self===i&&h()?e.addClass(l):e.removeClass(l)}function h(){return!k||j(k,b)}var i,k,l;l=c(f.uiSrefActive||"",!1)(d),this.$$setStateInfo=function(b,c){i=a.get(b,w(e)),k=c,g()},d.$on("$stateChangeSuccess",g)}]}}function z(a){return function(b){return a.is(b)}}function A(a){return function(b){return a.includes(b)}}function B(a,b){function e(a){this.locals=a.locals.globals,this.params=this.locals.$stateParams}function f(){this.locals=null,this.params=null}function g(c,g){if(null!=g.redirectTo){var h,j=g.redirectTo;if(E(j))h=j;else{if(!D(j))throw new Error("Invalid 'redirectTo' in when()");h=function(a,b){return j(a,b.path(),b.search())}}b.when(c,h)}else a.state(d(g,{parent:null,name:"route:"+encodeURIComponent(c),url:c,onEnter:e,onExit:f}));return i.push(g),this}function h(a,b,d){function e(a){return""!==a.name?a:c}var f={routes:i,params:d,current:c};return b.$on("$stateChangeStart",function(a,c,d,f){b.$broadcast("$routeChangeStart",e(c),e(f))}),b.$on("$stateChangeSuccess",function(a,c,d,g){f.current=e(c),b.$broadcast("$routeChangeSuccess",e(c),e(g)),J(d,f.params)}),b.$on("$stateChangeError",function(a,c,d,f,g,h){b.$broadcast("$routeChangeError",e(c),e(f),h)}),f}var i=[];e.$inject=["$$state"],this.when=g,this.$get=h,h.$inject=["$state","$rootScope","$routeParams"]}var C=b.isDefined,D=b.isFunction,E=b.isString,F=b.isObject,G=b.isArray,H=b.forEach,I=b.extend,J=b.copy;b.module("ui.router.util",["ng"]),b.module("ui.router.router",["ui.router.util"]),b.module("ui.router.state",["ui.router.router","ui.router.util"]),b.module("ui.router",["ui.router.state"]),b.module("ui.router.compat",["ui.router"]),l.$inject=["$q","$injector"],b.module("ui.router.util").service("$resolve",l),m.$inject=["$http","$templateCache","$injector"],b.module("ui.router.util").service("$templateFactory",m),n.prototype.concat=function(a){return new n(this.sourcePath+a+this.sourceSearch)},n.prototype.toString=function(){return this.source},n.prototype.exec=function(a,b){var c=this.regexp.exec(a);if(!c)return null;var d,e=this.params,f=e.length,g=this.segments.length-1,h={};if(g!==c.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");for(d=0;g>d;d++)h[e[d]]=c[d+1];for(;f>d;d++)h[e[d]]=b[e[d]];return h},n.prototype.parameters=function(){return this.params},n.prototype.format=function(a){var b=this.segments,c=this.params;if(!a)return b.join("");var d,e,f,g=b.length-1,h=c.length,i=b[0];for(d=0;g>d;d++)f=a[c[d]],null!=f&&(i+=encodeURIComponent(f)),i+=b[d+1];for(;h>d;d++)f=a[c[d]],null!=f&&(i+=(e?"&":"?")+c[d]+"="+encodeURIComponent(f),e=!0);return i},b.module("ui.router.util").provider("$urlMatcherFactory",o),p.$inject=["$urlMatcherFactoryProvider"],b.module("ui.router.router").provider("$urlRouter",p),q.$inject=["$urlRouterProvider","$urlMatcherFactoryProvider","$locationProvider"],b.module("ui.router.state").value("$stateParams",{}).provider("$state",q),r.$inject=[],b.module("ui.router.state").provider("$view",r),b.module("ui.router.state").provider("$uiViewScroll",s),t.$inject=["$state","$injector","$uiViewScroll"],u.$inject=["$compile","$controller","$state"],b.module("ui.router.state").directive("uiView",t),b.module("ui.router.state").directive("uiView",u),x.$inject=["$state","$timeout"],y.$inject=["$state","$stateParams","$interpolate"],b.module("ui.router.state").directive("uiSref",x).directive("uiSrefActive",y),z.$inject=["$state"],A.$inject=["$state"],b.module("ui.router.state").filter("isState",z).filter("includedByState",A),B.$inject=["$stateProvider","$urlRouterProvider"],b.module("ui.router.compat").provider("$route",B).directive("ngView",t)}(window,window.angular);
+/*!
+ * ionic.bundle.js is a concatenation of:
+ * ionic.js, angular.js, angular-animate.js,
+ * angular-sanitize.js, angular-ui-router.js,
+ * and ionic-angular.js
+ */
+
+/*!
+ * Copyright 2014 Drifty Co.
+ * http://drifty.com/
+ *
+ * Ionic, v1.0.0-beta.13
+ * A powerful HTML5 mobile app framework.
+ * http://ionicframework.com/
+ *
+ * By @maxlynch, @benjsperry, @adamdbradley <3
+ *
+ * Licensed under the MIT license. Please see LICENSE for more information.
+ *
+ */
+
+!function(){function e(e){if(e.$root!==e){var t=e.$parent;e.$$disconnected=!0,t.$$childHead===e&&(t.$$childHead=e.$$nextSibling),t.$$childTail===e&&(t.$$childTail=e.$$prevSibling),e.$$prevSibling&&(e.$$prevSibling.$$nextSibling=e.$$nextSibling),e.$$nextSibling&&(e.$$nextSibling.$$prevSibling=e.$$prevSibling),e.$$nextSibling=e.$$prevSibling=null}}function t(e){if(e.$root!==e&&e.$$disconnected){var t=e.$parent;e.$$disconnected=!1,e.$$prevSibling=t.$$childTail,t.$$childHead?(t.$$childTail.$$nextSibling=e,t.$$childTail=e):t.$$childHead=t.$$childTail=e}}function n(e){return["$log",function(t){function n(e){this.handle=e}var i=this,o=this._instances=[];this._registerInstance=function(e,t){return e.$$delegateHandle=t,o.push(e),function(){var t=o.indexOf(e);-1!==t&&o.splice(t,1)}},this.$getByHandle=function(e){return e?new n(e):i},e.forEach(function(e){n.prototype[e]=function(){var n,i,r=this.handle,a=arguments,s=0;return o.forEach(function(t){t.$$delegateHandle===r&&(s++,i=t[e].apply(t,a),1===s&&(n=i))}),s?n:t.warn('Delegate for handle "'+this.handle+'" could not find a corresponding element with delegate-handle="'+this.handle+'"! '+e+"() was not called!\nPossible cause: If you are calling "+e+'() immediately, and your element with delegate-handle="'+this.handle+'" is a child of your controller, then your element may not be compiled yet. Put a $timeout around your call to '+e+"() and try again.")},i[e]=function(){var t,n,i=arguments;return o.forEach(function(o,r){n=o[e].apply(o,i),0===r&&(t=n)}),t}})}]}function i(e,t){return[function(){return{priority:"99",link:function(n,i,o){o.$observe(e,function(e){e||i[0].removeAttribute(t)})}}}]}function o(e){return["$ionicGesture","$parse",function(t,n){var i=e.substr(2).toLowerCase();return function(o,r,a){var s=n(a[e]),c=function(e){o.$apply(function(){s(o,{$event:e})})},l=t.on(i,c,r);o.$on("$destroy",function(){t.off(l,i,c)})}}]}function r(){return["$ionicScrollDelegate",function(e){return{restrict:"E",link:function(t,n,i){function o(t){for(var i=3,o=t.target;i--&&o;){if(o.classList.contains("button")||o.tagName.match(/input|textarea|select/i)||o.isContentEditable)return;o=o.parentNode}var r=t.gesture&&t.gesture.touches[0]||t.detail.touches[0],a=n[0].getBoundingClientRect();ionic.DomUtil.rectContains(r.pageX,r.pageY,a.left,a.top-20,a.left+a.width,a.top+a.height)&&e.scrollTop(!0)}"true"!=i.noTapScroll&&(ionic.on("tap",o,n[0]),t.$on("$destroy",function(){ionic.off("tap",o,n[0])}))}}}]}function a(e){return[function(){return{restrict:"E",compile:function(t){function n(t,n,i){var o=(new ionic.views.HeaderBar({el:n[0],alignTitle:i.alignTitle||"center"}),n[0]);e?(t.$watch(function(){return o.className},function(e){var n=-1===e.indexOf("ng-hide"),i=-1!==e.indexOf("bar-subheader");t.$hasHeader=n&&!i,t.$hasSubheader=n&&i}),t.$on("$destroy",function(){delete t.$hasHeader,delete t.$hasSubheader})):(t.$watch(function(){return o.className},function(e){var n=-1===e.indexOf("ng-hide"),i=-1!==e.indexOf("bar-subfooter");t.$hasFooter=n&&!i,t.$hasSubfooter=n&&i}),t.$on("$destroy",function(){delete t.$hasFooter,delete t.$hasSubfooter}),t.$watch("$hasTabs",function(e){n.toggleClass("has-tabs",!!e)}))}t.addClass(e?"bar bar-header":"bar bar-footer");var i=t[0].parentNode;return i.querySelector(".tabs-top")&&t.addClass("has-tabs-top"),{pre:n}}}}]}function s(e){return e.clientHeight}function c(e){e.stopPropagation()}var l={method:function(e,t,n){var i=!1;return function(){return i||(i=!0,t(e)),n.apply(this,arguments)}},field:function(e,t,n,i,o){var r=!1,a=function(){return r||(r=!0,t(e)),o},s=function(n){return r||(r=!0,t(e)),o=n,n};Object.defineProperty(n,i,{get:a,set:s,enumerable:!0})}},u=angular.module("ionic",["ngAnimate","ngSanitize","ui.router"]),d=angular.extend,f=angular.forEach,h=angular.isDefined,p=angular.isString,v=angular.element;u.factory("$ionicActionSheet",["$rootScope","$compile","$animate","$timeout","$ionicTemplateLoader","$ionicPlatform","$ionicBody",function(e,t,n,i,o,r,a){function s(o){var s=e.$new(!0);angular.extend(s,{cancel:angular.noop,destructiveButtonClicked:angular.noop,buttonClicked:angular.noop,$deregisterBackButton:angular.noop,buttons:[],cancelOnStateChange:!0},o||{});var c=s.element=t(' ')(s),l=v(c[0].querySelector(".action-sheet-wrapper")),u=s.cancelOnStateChange?e.$on("$stateChangeSuccess",function(){s.cancel()}):angular.noop;return s.removeSheet=function(e){s.removed||(s.removed=!0,l.removeClass("action-sheet-up"),i(function(){a.removeClass("action-sheet-open")},400),s.$deregisterBackButton(),u(),n.removeClass(c,"active",function(){s.$destroy(),c.remove(),s.cancel.$scope=null,(e||angular.noop)()}))},s.showSheet=function(e){s.removed||(a.append(c).addClass("action-sheet-open"),n.addClass(c,"active",function(){s.removed||(e||angular.noop)()}),i(function(){s.removed||l.addClass("action-sheet-up")},20,!1))},s.$deregisterBackButton=r.registerBackButtonAction(function(){i(s.cancel)},C),s.cancel=function(){s.removeSheet(o.cancel)},s.buttonClicked=function(e){o.buttonClicked(e,o.buttons[e])===!0&&s.removeSheet()},s.destructiveButtonClicked=function(){o.destructiveButtonClicked()===!0&&s.removeSheet()},s.showSheet(),s.cancel.$scope=s,s.cancel}return{show:s}}]),v.prototype.addClass=function(e){var t,n,i,o,r,a;if(e&&"ng-scope"!=e&&"ng-isolate-scope"!=e)for(t=0;t'),a=0;return e[0].body.appendChild(r[0]),{retain:n,release:i,getElement:o,_element:r}}]),u.factory("$ionicBind",["$parse","$interpolate",function(e,t){var n=/^\s*([@=&])(\??)\s*(\w*)\s*$/;return function(i,o,r){f(r||{},function(r,a){var s,c,l=r.match(n)||[],u=l[3]||a,d=l[1];switch(d){case"@":if(!o[u])return;o.$observe(u,function(e){i[a]=e}),o[u]&&(i[a]=t(o[u])(i));break;case"=":if(!o[u])return;c=i.$watch(o[u],function(e){i[a]=e}),i.$on("$destroy",c);break;case"&":if(o[u]&&o[u].match(RegExp(a+"(.*?)")))throw new Error('& expression binding "'+a+'" looks like it will recursively call "'+o[u]+'" and cause a stack overflow! Please choose a different scopeName.');s=e(o[u]),i[a]=function(e){return s(i,e)}}})}}]),u.factory("$ionicBody",["$document",function(e){return{addClass:function(){for(var t=0;tthis.data.length-1)return this.afterSiblings[e-this.dataStartIndex];var t=this.getItem(e),n=this.data[e];return(t.index!==e||t.scope[this.keyExpr]!==n)&&(t.index=t.scope.$index=e,t.scope[this.keyExpr]=n,t.scope.$first=0===e,t.scope.$last=e===this.getLength()-1,t.scope.$middle=!(t.scope.$first||t.scope.$last),t.scope.$odd=!(t.scope.$even=0===(1&e)),o.$$phase||t.scope.$digest()),this.attachedItems[e]=t,t},destroyItem:function(e){e.element.remove(),e.scope.$destroy(),e.scope=null,e.element=null},detachItem:function(t){delete this.attachedItems[t.index],t.isOutside?r(t.element):this.backupItemsArray.length>=this.BACKUP_ITEMS_LENGTH?this.destroyItem(t):(this.backupItemsArray.push(t),r(t.element),e(t.scope))},getLength:function(){return this.dimensions&&this.dimensions.length||0},setData:function(e,t,n){this.data=e||[],this.beforeSiblings=t||[],this.afterSiblings=n||[],this.calculateDataDimensions(),this.afterSiblings.forEach(function(e){e.element.css({position:"absolute",top:"0",left:"0"}),r(e.element)})}},a}]),u.factory("$collectionRepeatManager",["$rootScope","$timeout",function(){function e(e){function t(){return n.viewportSize}var n=this;this.dataSource=e.dataSource,this.element=e.element,this.scrollView=e.scrollView,this.isVertical=!!this.scrollView.options.scrollingY,this.renderedItems={},this.dimensions=[],this.setCurrentIndex(0),this.scrollView.__$callback=this.scrollView.__callback,this.scrollView.__callback=angular.bind(this,this.renderScroll),this.isVertical?(this.scrollView.options.getContentHeight=t,this.scrollValue=function(){return this.scrollView.__scrollTop},this.scrollMaxValue=function(){return this.scrollView.__maxScrollTop},this.scrollSize=function(){return this.scrollView.__clientHeight},this.secondaryScrollSize=function(){return this.scrollView.__clientWidth},this.transformString=function(e,t){return"translate3d("+t+"px,"+e+"px,0)"},this.primaryDimension=function(e){return e.height},this.secondaryDimension=function(e){return e.width}):(this.scrollView.options.getContentWidth=t,this.scrollValue=function(){return this.scrollView.__scrollLeft},this.scrollMaxValue=function(){return this.scrollView.__maxScrollLeft},this.scrollSize=function(){return this.scrollView.__clientWidth},this.secondaryScrollSize=function(){return this.scrollView.__clientHeight},this.transformString=function(e,t){return"translate3d("+e+"px,"+t+"px,0)"},this.primaryDimension=function(e){return e.width},this.secondaryDimension=function(e){return e.height})}return e.prototype={destroy:function(){this.renderedItems={},this.render=angular.noop,this.calculateDimensions=angular.noop,this.dimensions=[]},calculateDimensions:function(){function e(e){var r={primarySize:this.primaryDimension(e),secondarySize:Math.min(this.secondaryDimension(e),o)};return t&&(i+=t.secondarySize,t.primaryPos===n&&i+r.secondarySize>o&&(i=0,n+=t.primarySize)),r.primaryPos=n,r.secondaryPos=i,t=r,r}var t,n=0,i=0,o=this.secondaryScrollSize();this.dataSource.beforeSiblings&&this.dataSource.beforeSiblings.forEach(e,this);var r=n+(t?t.primarySize:0);n=i=0,t=null;var a=this.dataSource.dimensions.map(e,this),s=n+(t?t.primarySize:0);return{beforeSize:r,totalSize:s,dimensions:a}},resize:function(){var e=this.calculateDimensions();this.dimensions=e.dimensions,this.viewportSize=e.totalSize,this.beforeSize=e.beforeSize,this.setCurrentIndex(0),this.render(!0),this.dataSource.setup()},setCurrentIndex:function(e){var t=(this.dimensions[e]||{}).primaryPos||0;this.currentIndex=e,this.hasPrevIndex=e>0,this.hasPrevIndex&&(this.previousPos=Math.max(t-this.dimensions[e-1].primarySize,this.dimensions[e-1].primaryPos)),this.hasNextIndex=e+1=this.nextPos||this.hasPrevIndex&&et;)e--;else for(;(n=this.dimensions[e+1])&&n.primaryPos=this.dataSource.getLength();if(o||e){for(n in this.renderedItems)this.removeItem(n);if(o)return}for(var r,a=this.scrollValue(),s=this.scrollSize(),c=s+a,l=this.getIndexForScrollValue(this.currentIndex,a),u=Math.max(l-1,0);u>0&&(r=this.dimensions[u])&&r.primaryPos===this.dimensions[l-1].primaryPos;)u--;for(n=u;(r=this.dimensions[n])&&r.primaryPos-r.primarySizef||f>d)&&this.removeItem(f);this.setCurrentIndex(l)},renderItem:function(e,t,n){var i=this.dataSource.attachItemAtIndex(e);i&&i.element?((i.primaryPos!==t||i.secondaryPos!==n)&&(i.element.css(ionic.CSS.TRANSFORM,this.transformString(t,n)),i.primaryPos=t,i.secondaryPos=n),this.renderedItems[e]=i):delete this.renderedItems[e]},removeItem:function(e){var t=this.renderedItems[e];t&&(t.primaryPos=t.secondaryPos=null,this.dataSource.detachItem(t),delete this.renderedItems[e])}},e}]),u.factory("$ionicGesture",[function(){return{on:function(e,t,n,i){return window.ionic.onGesture(e,t,n[0],i)},off:function(e,t,n){return window.ionic.offGesture(e,t,n)}}}]),u.provider("$ionicConfig",function(){var e={prefetchTemplates:!0};this.prefetchTemplates=function(t){return arguments.length&&(e.prefetchTemplates=t),e.prefetchTemplates},this.$get=function(){return e}});var g='',m="$ionicLoading instance.hide() has been deprecated. Use $ionicLoading.hide().",$="$ionicLoading instance.show() has been deprecated. Use $ionicLoading.show().",w="$ionicLoading instance.setContent() has been deprecated. Use $ionicLoading.show({ template: 'my content' }).";u.constant("$ionicLoadingConfig",{template:' '}).factory("$ionicLoading",["$ionicLoadingConfig","$ionicBody","$ionicTemplateLoader","$ionicBackdrop","$timeout","$q","$log","$compile","$ionicPlatform",function(e,t,n,i,o,r,a,s,c){function u(){return p||(p=n.compile({template:g,appendTo:t.get()}).then(function(e){var a=e;return e.show=function(e){var c=e.templateUrl?n.load(e.templateUrl):r.when(e.template||e.content||"");this.isShown||(this.hasBackdrop=!e.noBackdrop&&e.showBackdrop!==!1,this.hasBackdrop&&(i.retain(),i.getElement().addClass("backdrop-loading"))),e.duration&&(o.cancel(this.durationTimeout),this.durationTimeout=o(angular.bind(this,this.hide),+e.duration)),c.then(function(e){if(e){var n=a.element.children();n.html(e),s(n.contents())(a.scope)}a.isShown&&(a.element.addClass("visible"),ionic.requestAnimationFrame(function(){a.isShown&&(a.element.addClass("active"),t.addClass("loading-active"))}))}),this.isShown=!0},e.hide=function(){this.isShown&&(this.hasBackdrop&&(i.release(),i.getElement().removeClass("backdrop-loading")),a.element.removeClass("active"),t.removeClass("loading-active"),setTimeout(function(){!a.isShown&&a.element.removeClass("visible")},200)),o.cancel(this.durationTimeout),this.isShown=!1},e})),p}function f(t){t=d(e||{},t||{});var n=t.delay||t.showDelay||0;return b&&o.cancel(b),b=o(angular.noop,n),b.then(u).then(function(e){return v(),v=c.registerBackButtonAction(angular.noop,T),e.show(t)}),{hide:l.method(m,a.error,h),show:l.method($,a.error,function(){f(t)}),setContent:l.method(w,a.error,function(e){u().then(function(t){t.show({template:e})})})}}function h(){v(),o.cancel(b),u().then(function(e){e.hide()})}var p,v=angular.noop,b=r.when();return{show:f,hide:h,_getLoader:u}}]),u.factory("$ionicModal",["$rootScope","$ionicBody","$compile","$timeout","$ionicPlatform","$ionicTemplateLoader","$q","$log",function(e,t,n,i,o,r,a,s){var c=ionic.views.Modal.inherit({initialize:function(e){ionic.views.Modal.prototype.initialize.call(this,e),this.animation=e.animation||"slide-in-up"},show:function(e){var n=this;if(n.scope.$$destroyed)return void s.error("Cannot call "+n.viewType+".show() after remove(). Please create a new "+n.viewType+" instance.");var r=v(n.modalEl);return n.el.classList.remove("hide"),i(function(){t.addClass(n.viewType+"-open")},400),n.el.parentElement||(r.addClass(n.animation),t.append(n.el)),e&&n.positionView&&n.positionView(e,r),r.addClass("ng-enter active").removeClass("ng-leave ng-leave-active"),n._isShown=!0,n._deregisterBackButton=o.registerBackButtonAction(n.hardwareBackButtonClose?angular.bind(n,n.hide):angular.noop,S),n._isOpenPromise=a.defer(),ionic.views.Modal.prototype.show.call(n),i(function(){r.addClass("ng-enter-active"),ionic.trigger("resize"),n.scope.$parent&&n.scope.$parent.$broadcast(n.viewType+".shown",n),n.el.classList.add("active")},20),i(function(){n.$el.on("click",function(e){n.backdropClickToClose&&e.target===n.el&&n.hide()})},400)},hide:function(){var e=this,n=v(e.modalEl);return e.el.classList.remove("active"),n.addClass("ng-leave"),i(function(){n.addClass("ng-leave-active").removeClass("ng-enter ng-enter-active active")},20),e.$el.off("click"),e._isShown=!1,e.scope.$parent&&e.scope.$parent.$broadcast(e.viewType+".hidden",e),e._deregisterBackButton&&e._deregisterBackButton(),ionic.views.Modal.prototype.hide.call(e),i(function(){t.removeClass(e.viewType+"-open"),e.el.classList.add("hide")},e.hideDelay||320)},remove:function(){var e=this;return e.scope.$parent&&e.scope.$parent.$broadcast(e.viewType+".removed",e),e.hide().then(function(){e.scope.$destroy(),e.$el.remove()})},isShown:function(){return!!this._isShown}}),l=function(t,i){var o=i.scope&&i.scope.$new()||e.$new(!0);i.viewType=i.viewType||"modal",d(o,{$hasHeader:!1,$hasSubheader:!1,$hasFooter:!1,$hasSubfooter:!1,$hasTabs:!1,$hasTabsTop:!1});var r=n(""+t+" ")(o);i.$el=r,i.el=r[0],i.modalEl=i.el.querySelector("."+i.viewType);var a=new c(i);return a.scope=o,i.scope||(o[i.viewType]=a),a};return{fromTemplate:function(e,t){var n=l(e,t||{});return n},fromTemplateUrl:function(e,t,n){var i;return angular.isFunction(t)&&(i=t,t=n),r.load(e).then(function(e){var n=l(e,t||{});return i&&i(n),n})}}}]),u.service("$ionicNavBarDelegate",n(["back","align","showBackButton","showBar","setTitle","changeTitle","getTitle","getPreviousTitle"]));var b=100,y=150,S=200,C=300,k=400,T=500;u.constant("$ionicPlatformDefaults",{ios:{$ionicNavBarConfig:{transition:"nav-title-slide-ios",alignTitle:"center",backButtonIcon:"ion-ios7-arrow-back"},$ionicNavViewConfig:{transition:"slide-ios"},$ionicTabsConfig:{type:"",position:""}},android:{$ionicNavBarConfig:{transition:"nav-title-slide-full",alignTitle:"center",backButtonIcon:"ion-ios7-arrow-back"},$ionicNavViewConfig:{transition:"slide-full"},$ionicTabsConfig:{type:"tabs-striped",position:""}}}),u.config(["$ionicPlatformDefaults","$injector",function(e,t){var n=ionic.Platform.platform(),i=function(e){f(e,function(e,n){d(t.get(n),e)})};switch(n){case"ios":i(e.ios);break;case"android":i(e.android)}}]),u.provider("$ionicPlatform",function(){return{$get:["$q","$rootScope",function(e){var t={onHardwareBackButton:function(e){ionic.Platform.ready(function(){document.addEventListener("backbutton",e,!1)})},offHardwareBackButton:function(e){ionic.Platform.ready(function(){document.removeEventListener("backbutton",e)})},$backButtonActions:{},registerBackButtonAction:function(e,n,i){t._hasBackButtonHandler||(t.$backButtonActions={},t.onHardwareBackButton(t.hardwareBackButtonClick),t._hasBackButtonHandler=!0);var o={id:i?i:ionic.Utils.nextUid(),priority:n?n:0,fn:e};return t.$backButtonActions[o.id]=o,function(){delete t.$backButtonActions[o.id]}},hardwareBackButtonClick:function(e){var n,i;for(i in t.$backButtonActions)(!n||t.$backButtonActions[i].priority>=n.priority)&&(n=t.$backButtonActions[i]);return n?(n.fn(e),n):void 0},is:function(e){return ionic.Platform.is(e)},on:function(e,t){return ionic.Platform.ready(function(){document.addEventListener(e,t,!1)}),function(){ionic.Platform.ready(function(){document.removeEventListener(e,t)})}},ready:function(t){var n=e.defer();return ionic.Platform.ready(function(){n.resolve(),t&&t()}),n.promise}};return t}]}}),u.factory("$ionicPopover",["$ionicModal","$ionicPosition","$document","$window",function(e,t,n,i){function o(e,o){var a=angular.element(e.target||e),s=t.offset(a),c=o.prop("offsetWidth"),l=o.prop("offsetHeight"),u=n[0].body.clientWidth,d=i.innerHeight,f={left:s.left+s.width/2-c/2},h=v(o[0].querySelector(".popover-arrow"));f.leftu&&(f.left=u-c-r),s.top+s.height+l>d?(f.top=s.top-l,o.addClass("popover-bottom")):(f.top=s.top+s.height,o.removeClass("popover-bottom")),h.css({left:s.left+s.width/2-h.prop("offsetWidth")/2-f.left+"px"}),o.css({top:f.top+"px",left:f.left+"px",marginLeft:"0",opacity:"1"})}var r=6,a={viewType:"popover",hideDelay:1,animation:"none",positionView:o};return{fromTemplate:function(t,n){return e.fromTemplate(t,ionic.Utils.extend(n||{},a))},fromTemplateUrl:function(t,n){return e.fromTemplateUrl(t,n,ionic.Utils.extend(n||{},a))}}}]);var I='';u.factory("$ionicPopup",["$ionicTemplateLoader","$ionicBackdrop","$q","$timeout","$rootScope","$ionicBody","$compile","$ionicPlatform",function(e,t,n,i,o,r,a,s){function c(t){t=d({scope:null,title:"",buttons:[]},t||{});var o=e.compile({template:I,scope:t.scope&&t.scope.$new(),appendTo:r.get()}),s=t.templateUrl?e.load(t.templateUrl):n.when(t.template||t.content||"");return n.all([o,s]).then(function(e){var o=e[0],r=e[1],s=n.defer();o.responseDeferred=s;var c=v(o.element[0].querySelector(".popup-body"));return r?(c.html(r),a(c.contents())(o.scope)):c.remove(),d(o.scope,{title:t.title,buttons:t.buttons,subTitle:t.subTitle,$buttonTapped:function(e,t){var n=(e.onTap||angular.noop)(t);t=t.originalEvent||t,t.defaultPrevented||s.resolve(n)}}),o.show=function(){o.isShown||(o.isShown=!0,ionic.requestAnimationFrame(function(){o.isShown&&(o.element.removeClass("popup-hidden"),o.element.addClass("popup-showing active"),f(o.element))}))},o.hide=function(e){return e=e||angular.noop,o.isShown?(o.isShown=!1,o.element.removeClass("active"),o.element.addClass("popup-hidden"),void i(e,250)):e()},o.remove=function(){o.removed||(o.hide(function(){o.element.remove(),o.scope.$destroy()}),o.removed=!0)},o})}function l(){$[0]&&$[0].responseDeferred.resolve()}function u(e){function n(e){o.then(function(t){t.removed||t.responseDeferred.resolve(e)})}var o=w._createPopup(e),a=$[0];a&&a.hide();var c=i(angular.noop,a?m.stackPushDelay:0).then(function(){return o}).then(function(e){return a||(r.addClass("popup-open"),t.retain(),w._backButtonActionDone=s.registerBackButtonAction(l,k)),$.unshift(e),e.show(),e.responseDeferred.notify({close:c.close}),e.responseDeferred.promise.then(function(n){var o=$.indexOf(e);-1!==o&&$.splice(o,1),e.remove();var a=$[0];return a?a.show():(i(function(){r.removeClass("popup-open")},400),t.release(),(w._backButtonActionDone||angular.noop)()),n})});return c.close=n,c}function f(e){var t=e[0].querySelector("[autofocus]");t&&t.focus()}function h(e){return u(d({buttons:[{text:e.okText||"OK",type:e.okType||"button-positive",onTap:function(){return!0}}]},e||{}))}function p(e){return u(d({buttons:[{text:e.cancelText||"Cancel",type:e.cancelType||"button-default",onTap:function(){return!1}},{text:e.okText||"OK",type:e.okType||"button-positive",onTap:function(){return!0}}]},e||{}))}function g(e){var t=o.$new(!0);t.data={};var n="";return e.template&&/<[a-z][\s\S]*>/i.test(e.template)===!1&&(n=""+e.template+" ",delete e.template),u(d({template:n+' ',scope:t,buttons:[{text:e.cancelText||"Cancel",type:e.cancelType||"button-default",onTap:function(){}},{text:e.okText||"OK",type:e.okType||"button-positive",onTap:function(){return t.data.response||""}}]},e||{}))}var m={stackPushDelay:75},$=[],w={show:u,alert:h,confirm:p,prompt:g,_createPopup:c,_popupStack:$};return w}]),u.factory("$ionicPosition",["$document","$window",function(e,t){function n(e,n){return e.currentStyle?e.currentStyle[n]:t.getComputedStyle?t.getComputedStyle(e)[n]:e.style[n]}function i(e){return"static"===(n(e,"position")||"static")}var o=function(t){for(var n=e[0],o=t.offsetParent||n;o&&o!==n&&i(o);)o=o.offsetParent;return o||n};return{position:function(t){var n=this.offset(t),i={top:0,left:0},r=o(t[0]);r!=e[0]&&(i=this.offset(angular.element(r)),i.top+=r.clientTop-r.scrollTop,i.left+=r.clientLeft-r.scrollLeft);var a=t[0].getBoundingClientRect();return{width:a.width||t.prop("offsetWidth"),height:a.height||t.prop("offsetHeight"),top:n.top-i.top,left:n.left-i.left}},offset:function(n){var i=n[0].getBoundingClientRect();return{width:i.width||n.prop("offsetWidth"),height:i.height||n.prop("offsetHeight"),top:i.top+(t.pageYOffset||e[0].documentElement.scrollTop),left:i.left+(t.pageXOffset||e[0].documentElement.scrollLeft)}}}}]),u.service("$ionicScrollDelegate",n(["resize","scrollTop","scrollBottom","scrollTo","scrollBy","zoomTo","zoomBy","getScrollPosition","anchorScroll","getScrollView","rememberScrollPosition","forgetScrollPosition","scrollToRememberedPosition"])),u.service("$ionicSideMenuDelegate",n(["toggleLeft","toggleRight","getOpenRatio","isOpen","isOpenLeft","isOpenRight","canDragContent","edgeDragThreshold"])),u.service("$ionicSlideBoxDelegate",n(["update","slide","enableSlide","previous","next","stop","start","currentIndex","slidesCount"])),u.service("$ionicTabsDelegate",n(["select","selectedIndex"])),function(){var e=[];u.factory("$ionicTemplateCache",["$http","$templateCache","$timeout","$ionicConfig",function(t,n,i,o){function r(e){return a.length>500?!1:"undefined"==typeof e?c():(p(e)&&(e=[e]),f(e,function(e){a.push(e)}),void(s&&c()))}var a=e,s=!1,c=function(){if(o.prefetchTemplates!==!1&&(r._runCount++,s=!0,0!==a.length)){for(var e=0;5>e&&(template=a.pop());)p(template)&&t.get(template,{cache:n}),e++;a.length&&i(function(){c()},1e3)}};return r._runCount=0,r}]).config(["$stateProvider","$ionicConfigProvider",function(t,n){var i=t.state;t.state=function(o,r){if("object"==typeof r&&n.prefetchTemplates()!==!1){var a=r.prefetchTemplate!==!1;if(a&&p(r.templateUrl)&&e.push(r.templateUrl),angular.isObject(r.views))for(var s in r.views)a=r.views[s].prefetchTemplate!==!1,a&&p(r.views[s].templateUrl)&&e.push(r.views[s].templateUrl)}return i.call(t,o,r)}}]).run(["$ionicTemplateCache",function(e){e()}])}(),u.factory("$ionicTemplateLoader",["$compile","$controller","$http","$q","$rootScope","$templateCache",function(e,t,n,i,o,r){function a(e){return n.get(e,{cache:r}).then(function(e){return e.data&&e.data.trim()})}function s(n){n=d({template:"",templateUrl:"",scope:null,controller:null,locals:{},appendTo:null},n||{});var r=n.templateUrl?this.load(n.templateUrl):i.when(n.template);return r.then(function(i){var r,a=n.scope||o.$new(),s=v("").html(i).contents();return n.controller&&(r=t(n.controller,d(n.locals,{$scope:a})),s.children().data("$ngControllerController",r)),n.appendTo&&v(n.appendTo).append(s),e(s)(a),{element:s,scope:a}})}return{load:a,compile:s}}]),u.run(["$rootScope","$state","$location","$document","$animate","$ionicPlatform","$ionicViewService",function(e,t,n,i,o,r,a){function s(t){return e.$viewHistory.backView?e.$viewHistory.backView.go():ionic.Platform.exitApp(),t.preventDefault(),!1}e.$viewHistory={histories:{root:{historyId:"root",parentHistoryId:null,stack:[],cursor:-1}},views:{},backView:null,forwardView:null,currentView:null,disabledRegistrableTagNames:[]},a.disableRegisterByTagName&&(a.disableRegisterByTagName("ion-tabs"),a.disableRegisterByTagName("ion-side-menus")),e.$on("$stateChangeStart",function(){ionic.keyboard.hide()}),e.$on("viewState.changeHistory",function(i,o){if(o){var r=o.historyId?e.$viewHistory.histories[o.historyId]:null;if(r&&r.cursor>-1&&r.cursor
-1&&a.stack.length>0&&a.cursor=u.index;m--)g.stack[m].destroy(),g.stack.splice(m)}}else f.navAction="initialView";o.views[f.viewId]=this.createView({viewId:f.viewId,index:a.stack.length,historyId:a.historyId,backViewId:s&&s.viewId?s.viewId:null,forwardViewId:null,stateId:r,stateName:this.getCurrentStateName(),stateParams:this.getCurrentStateParams(),url:n.url()}),"moveBack"==f.navAction&&e.$emit("$viewHistory.viewBack",s.viewId,f.viewId),a.stack.push(o.views[f.viewId])}return d&&(d.disableAnimate&&(f.navDirection=null),d.disableBack&&(o.views[f.viewId].backViewId=null),this.nextViewOptions(null)),this.setNavViews(f.viewId),a.cursor=o.currentView.index,f},setNavViews:function(t){var n=e.$viewHistory;n.currentView=this._getViewById(t),n.backView=this._getBackView(n.currentView),n.forwardView=this._getForwardView(n.currentView),e.$broadcast("$viewHistory.historyChange",{showBack:n.backView&&n.backView.historyId===n.currentView.historyId})},registerHistory:function(e){e.$historyId=ionic.Utils.nextUid()},createView:function(e){var t=new l;return t.initialize(e)},getCurrentView:function(){return e.$viewHistory.currentView},getBackView:function(){return e.$viewHistory.backView},getForwardView:function(){return e.$viewHistory.forwardView},getNavDirection:function(){return e.$viewHistory.navDirection},getCurrentStateName:function(){return t&&t.current?t.current.name:null},isCurrentStateNavView:function(e){return t&&t.current&&t.current.views&&t.current.views[e]?!0:!1},getCurrentStateParams:function(){var e;if(t&&t.params)for(var n in t.params)t.params.hasOwnProperty(n)&&(e=e||{},e[n]=t.params[n]);return e},getCurrentStateId:function(){var e;if(t&&t.current&&t.current.name){if(e=t.current.name,t.params)for(var n in t.params)t.params.hasOwnProperty(n)&&t.params[n]&&(e+="_"+n+"="+t.params[n]);return e}return ionic.Utils.nextUid()},goToHistoryRoot:function(t){if(t){var n=e.$viewHistory.histories[t];if(n&&n.stack.length){if(e.$viewHistory.currentView&&e.$viewHistory.currentView.viewId===n.stack[0].viewId)return;e.$viewHistory.forcedNav={viewId:n.stack[0].viewId,navAction:"moveBack",navDirection:"back"},n.stack[0].go()}}},_getViewById:function(t){return t?e.$viewHistory.views[t]:null},_getBackView:function(e){return e?this._getViewById(e.backViewId):null},_getForwardView:function(e){return e?this._getViewById(e.forwardViewId):null},_getHistoryById:function(t){return t?e.$viewHistory.histories[t]:null},_getHistory:function(t){var n=this._getParentHistoryObj(t);return e.$viewHistory.histories[n.historyId]||(e.$viewHistory.histories[n.historyId]={historyId:n.historyId,parentHistoryId:this._getParentHistoryObj(n.scope.$parent).historyId,stack:[],cursor:-1}),e.$viewHistory.histories[n.historyId]},_getParentHistoryObj:function(t){for(var n=t;n;){if(n.hasOwnProperty("$historyId"))return{historyId:n.$historyId,scope:n};n=n.$parent}return{historyId:"root",scope:e}},nextViewOptions:function(e){return arguments.length?void(this._nextOpts=e):this._nextOpts},getRenderer:function(e,t,n){function i(e){for(var t="";!t&&e;)t=e.getAttribute("animation"),e=e.parentElement;return t?t:a.transition}function o(){d&&e[0].classList.add(d),"back"===c.navDirection?e[0].classList.add("reverse"):e[0].classList.remove("reverse")}var c,l,u=this,d=i(e[0]);return function(t){return{enter:function(n){return l&&t?(o(),n.addClass("ng-enter"),s.show(),void r.enter(n,e,null,function(){s.hide(),d&&e[0].classList.remove(d)})):(l||s.hide(),void e.append(n))},leave:function(){var n=e.contents();return l&&t?(o(),void r.leave(n,function(){n.remove()})):void n.remove()},register:function(e){return c=u.register(n,e),l=null!==d&&null!==c.navDirection,c}}}},disableRegisterByTagName:function(t){e.$viewHistory.disabledRegistrableTagNames.push(t.toUpperCase())},isTagNameRegistrable:function(t){var n,i,o=e.$viewHistory.disabledRegistrableTagNames;for(n=0;n')(e),v(a[a.length-1]).replaceWith(n)),i=r(' ')(e),ionic.requestAnimationFrame(function(){n&&o.leave(v(n));var r=n&&v(n)||null;o.enter(i,t,r,function(){c._headerBarView.align()}),f(a,function(e){e&&e.parentNode&&v(e).remove()}),e.$digest(),ionic.requestAnimationFrame(function(){i[0].classList.remove("invisible")})})}}]),u.factory("$$scrollValueCache",function(){return{}}).controller("$ionicScroll",["$scope","scrollViewOptions","$timeout","$window","$$scrollValueCache","$location","$rootScope","$document","$ionicScrollDelegate",function(e,t,n,i,o,r,a,s,c){var l=this;this.__timeout=n,this._scrollViewOptions=t;var u=this.element=t.el,d=this.$element=v(u),f=this.scrollView=new ionic.views.Scroll(t);(d.parent().length?d.parent():d).data("$$ionicScrollController",this);var h=c._registerInstance(this,t.delegateHandle);angular.isDefined(t.bouncing)||ionic.Platform.ready(function(){f.options.bouncing=!0,ionic.Platform.isAndroid()&&(f.options.bouncing=!1,f.options.deceleration=.95)});var p=angular.bind(f,f.resize);ionic.on("resize",p,i);var g=angular.noop,m=angular.noop,$=function(t){var n=(t.originalEvent||t).detail||{};e.$onScroll&&e.$onScroll({event:t,scrollTop:n.scrollTop||0,scrollLeft:n.scrollLeft||0})};d.on("scroll",$),e.$on("$destroy",function(){h(),f.__cleanup(),ionic.off("resize",p,i),i.removeEventListener("resize",p),m(),g(),l._rememberScrollId&&(o[l._rememberScrollId]=f.getValues()),t=null,l._scrollViewOptions=null,l.element=null,d.off("scroll",$),d=null,l.$element=null,l.scrollView=null,f=null}),m=e.$on("$viewContentLoaded",function(t,i){if(!t.defaultPrevented){t.preventDefault();var o=i&&i.viewId||e.$historyId;o&&n(function(){l.rememberScrollPosition(o),l.scrollToRememberedPosition(),g=a.$on("$viewHistory.viewBack",function(e,t){o===t&&l.forgetScrollPosition()})},0,!1)}}),n(function(){f&&f.run&&f.run()}),this._rememberScrollId=null,this.getScrollView=function(){return this.scrollView},this.getScrollPosition=function(){return this.scrollView.getValues()},this.resize=function(){return n(p).then(function(){d&&d.triggerHandler("scroll.resize")})},this.scrollTop=function(e){this.resize().then(function(){f.scrollTo(0,0,!!e)})},this.scrollBottom=function(e){this.resize().then(function(){var t=f.getScrollMax();f.scrollTo(t.left,t.top,!!e)})},this.scrollTo=function(e,t,n){this.resize().then(function(){f.scrollTo(e,t,!!n)})},this.zoomTo=function(e,t,n,i){this.resize().then(function(){f.zoomTo(e,!!t,n,i)})},this.zoomBy=function(e,t,n,i){this.resize().then(function(){f.zoomBy(e,!!t,n,i)})},this.scrollBy=function(e,t,n){this.resize().then(function(){f.scrollBy(e,t,!!n)})},this.anchorScroll=function(e){this.resize().then(function(){var t=r.hash(),n=t&&s[0].getElementById(t);if(!t||!n)return void f.scrollTo(0,0,!!e);var i=n,o=0,a=0,c=0;do null!==i&&(o+=i.offsetLeft),null!==i&&(a+=i.offsetTop),i=i.offsetParent,c++;while(i.attributes!=l.element.attributes&&i.offsetParent);f.scrollTo(o,a,!!e)})},this.rememberScrollPosition=function(e){if(!e)throw new Error("Must supply an id to remember the scroll by!");this._rememberScrollId=e},this.forgetScrollPosition=function(){delete o[this._rememberScrollId],this._rememberScrollId=null},this.scrollToRememberedPosition=function(e){var t=o[this._rememberScrollId];t&&this.resize().then(function(){f&&f.scrollTo&&f.scrollTo(+t.left,+t.top,e)})},this._setRefresher=function(e,t){var i=this.refresher=t,o=l.refresher.clientHeight||60;f.activatePullToRefresh(o,function(){i.classList.add("active"),e.$onPulling()},function(){n(function(){i.classList.remove("active"),i.classList.remove("refreshing"),i.classList.remove("refreshing-tail"),i.classList.add("invisible")},300)},function(){i.classList.add("refreshing"),e.$onRefresh()},function(){i.classList.remove("invisible")},function(){i.classList.add("invisible")},function(){i.classList.add("refreshing-tail")})}}]),u.controller("$ionicSideMenus",["$scope","$attrs","$ionicSideMenuDelegate","$ionicPlatform","$ionicBody",function(e,t,n,i,o){var r,a,s,c,l,u,d,f=this;f.$scope=e,f.initialize=function(e){f.left=e.left,f.right=e.right,f.setContent(e.content),f.dragThresholdX=e.dragThresholdX||10},f.setContent=function(e){e&&(f.content=e,f.content.onDrag=function(e){f._handleDrag(e)},f.content.endDrag=function(e){f._endDrag(e)})},f.isOpenLeft=function(){return f.getOpenAmount()>0},f.isOpenRight=function(){return f.getOpenAmount()<0},f.toggleLeft=function(e){if(!d&&f.left.isEnabled){var t=f.getOpenAmount();0===arguments.length&&(e=0>=t),f.content.enableAnimation(),f.openPercentage(e?100:0)}},f.toggleRight=function(e){if(!d&&f.right.isEnabled){var t=f.getOpenAmount();0===arguments.length&&(e=t>=0),f.content.enableAnimation(),f.openPercentage(e?-100:0)}},f.close=function(){f.openPercentage(0)},f.getOpenAmount=function(){return f.content&&f.content.getTranslateX()||0},f.getOpenRatio=function(){var e=f.getOpenAmount();return e>=0?e/f.left.width:e/f.right.width},f.isOpen=function(){return 0!==f.getOpenAmount()},f.getOpenPercentage=function(){return 100*f.getOpenRatio()},f.openPercentage=function(e){var t=e/100;if(f.left&&e>=0)f.openAmount(f.left.width*t);else if(f.right&&0>e){{f.right.width}f.openAmount(f.right.width*t)}o.enableClass(0!==e,"menu-open")},f.openAmount=function(e){var t=f.left&&f.left.width||0,n=f.right&&f.right.width||0;return(f.left&&f.left.isEnabled||!(e>0))&&(f.right&&f.right.isEnabled||!(0>e))?a&&e>t?void f.content.setTranslateX(t):r&&-n>e?void f.content.setTranslateX(-n):(f.content.setTranslateX(e),void(e>=0?(a=!0,r=!1,e>0&&(f.right&&f.right.pushDown&&f.right.pushDown(),f.left&&f.left.bringUp&&f.left.bringUp())):(r=!0,a=!1,f.right&&f.right.bringUp&&f.right.bringUp(),f.left&&f.left.pushDown&&f.left.pushDown()))):void f.content.setTranslateX(0)},f.snapToRest=function(e){f.content.enableAnimation(),s=!1;var t=f.getOpenRatio();if(0===t)return void f.openPercentage(0);var n=.3,i=e.gesture.velocityX,o=e.gesture.direction;f.openPercentage(t>0&&.5>t&&"right"==o&&n>i?0:t>.5&&"left"==o&&n>i?100:0>t&&t>-.5&&"left"==o&&n>i?0:.5>t&&"right"==o&&n>i?-100:"right"==o&&t>=0&&(t>=.5||i>n)?100:"left"==o&&0>=t&&(-.5>=t||i>n)?-100:0)},f.isAsideExposed=function(){return!!d},f.exposeAside=function(e){f.left&&f.left.isEnabled&&(f.close(),d=e,f.content.setMarginLeft(d?f.left.width:0),f.$scope.$emit("$ionicExposeAside",d))},f.activeAsideResizing=function(e){o.enableClass(e,"aside-resizing")},f._endDrag=function(e){d||(s&&f.snapToRest(e),c=null,l=null,u=null)},f._handleDrag=function(e){d||(c?l=e.gesture.touches[0].pageX:(c=e.gesture.touches[0].pageX,l=c),!s&&Math.abs(l-c)>f.dragThresholdX&&(c=l,s=!0,f.content.disableAnimation(),u=f.getOpenAmount()),s&&f.openAmount(u+(l-c)))},f.canDragContent=function(t){return arguments.length&&(e.dragContent=!!t),e.dragContent},f.edgeThreshold=25,f.edgeThresholdEnabled=!1,f.edgeDragThreshold=function(e){return arguments.length&&(angular.isNumber(e)&&e>0?(f.edgeThreshold=e,f.edgeThresholdEnabled=!0):f.edgeThresholdEnabled=!!e),f.edgeThresholdEnabled},f.isDraggableTarget=function(t){var n=f.edgeThresholdEnabled&&!f.isOpen(),i=t.gesture.startEvent&&t.gesture.startEvent.center&&t.gesture.startEvent.center.pageX,o=!n||i<=f.edgeThreshold||i>=f.content.element.offsetWidth-f.edgeThreshold;return(e.dragContent||f.isOpen())&&o&&!t.gesture.srcEvent.defaultPrevented&&!t.target.tagName.match(/input|textarea|select|object|embed/i)&&!t.target.isContentEditable&&!(t.target.dataset?t.target.dataset.preventScroll:"true"==t.target.getAttribute("data-prevent-scroll"))},e.sideMenuContentTranslateX=0;var h=angular.noop,p=angular.bind(f,f.close);e.$watch(function(){return 0!==f.getOpenAmount()},function(e){h(),e&&(h=i.registerBackButtonAction(p,y))});var v=n._registerInstance(f,t.delegateHandle);e.$on("$destroy",function(){v(),h()}),f.initialize({left:{width:275},right:{width:275}})}]),u.controller("$ionicTab",["$scope","$ionicViewService","$attrs","$location","$state",function(e,t,n,i,o){this.$scope=e,this.hrefMatchesState=function(){return n.href&&0===i.path().indexOf(n.href.replace(/^#/,"").replace(/\/$/,""))},this.srefMatchesState=function(){return n.uiSref&&o.includes(n.uiSref.split("(")[0])},this.navNameMatchesState=function(){return this.navViewName&&t.isCurrentStateNavView(this.navViewName)},this.tabMatchesState=function(){return this.hrefMatchesState()||this.srefMatchesState()||this.navNameMatchesState()}}]),u.controller("$ionicTabs",["$scope","$ionicViewService","$element",function(e,t){var n=null,i=this;i.tabs=[],i.selectedIndex=function(){return i.tabs.indexOf(n)},i.selectedTab=function(){return n},i.add=function(e){t.registerHistory(e),i.tabs.push(e),1===i.tabs.length&&i.select(e)},i.remove=function(e){var t=i.tabs.indexOf(e);if(-1!==t){if(e.$tabSelected)if(i.deselect(e),1===i.tabs.length);else{var n=t===i.tabs.length-1?t-1:t+1;i.select(i.tabs[n])}i.tabs.splice(t,1)}},i.deselect=function(e){e.$tabSelected&&(n=null,e.$tabSelected=!1,(e.onDeselect||angular.noop)())},i.select=function(o,r){var a;if(angular.isNumber(o)?(a=o,o=i.tabs[a]):a=i.tabs.indexOf(o),1===arguments.length&&(r=!(!o.navViewName&&!o.uiSref)),n&&n.$historyId==o.$historyId)r&&t.goToHistoryRoot(o.$historyId);else if(f(i.tabs,function(e){i.deselect(e)}),n=o,o.$tabSelected=!0,(o.onSelect||angular.noop)(),r){var s={type:"tab",tabIndex:a,historyId:o.$historyId,navViewName:o.navViewName,hasNavView:!!o.navViewName,title:o.title,url:o.href,uiSref:o.uiSref};e.$emit("viewState.changeHistory",s)}}}]),u.directive("ionActionSheet",["$document",function(e){return{restrict:"E",scope:!0,replace:!0,link:function(t,n){var i=function(e){27==e.which&&(t.cancel(),t.$apply())},o=function(e){e.target==n[0]&&(t.cancel(),t.$apply())};t.$on("$destroy",function(){n.remove(),e.unbind("keyup",i)}),e.bind("keyup",i),n.bind("click",o)},template:''}}]),u.directive("ionCheckbox",function(){return{restrict:"E",replace:!0,require:"?ngModel",transclude:!0,template:'
',compile:function(e,t){var n=e.find("input");f({name:t.name,"ng-value":t.ngValue,"ng-model":t.ngModel,"ng-checked":t.ngChecked,"ng-disabled":t.ngDisabled,"ng-true-value":t.ngTrueValue,"ng-false-value":t.ngFalseValue,"ng-change":t.ngChange},function(e,t){h(e)&&n.attr(t,e)})}}});var x="Cannot create a collection-repeat within a scrollView that is scrollable on both x and y axis. Choose either x direction or y direction.",B="collection-repeat expected attribute collection-item-height to be a an expression that returns a number (in pixels) or percentage.",V="collection-repeat expected attribute collection-item-width to be a an expression that returns a number (in pixels) or percentage.",E="collection-repeat expected expression in form of '_item_ in _collection_[ track by _id_]' but got '%'";u.directive("collectionRepeat",["$collectionRepeatManager","$collectionDataSource","$parse",function(e,t,n){return{priority:1e3,transclude:"element",terminal:!0,$$tlb:!0,require:"^$ionicScroll",controller:[function(){}],link:function(i,o,r,a,s){function c(e){var t=[],n=[],i=!0;f(I.children,function(e){if(ionic.DomUtil.elementIsDescendant(o[0],e,I))i=!1;else{if(e.hasAttribute("collection-repeat-ignore"))return;var r=e.offsetWidth,a=e.offsetHeight;if(r&&a){var s=v(e);(i?t:n).push({width:e.offsetWidth,height:e.offsetHeight,element:s,scope:s.isolateScope()||s.scope(),isOutside:!0})}}}),d.resize(),k.setData(e,t,n),T.resize()}function l(){c(i.$eval(S))}var u=v('');o.parent()[0].insertBefore(u[0],o[0]),u.append(o);var d=a.scrollView;if(d.options.scrollingX&&d.options.scrollingY)throw new Error(x);var h=!!d.options.scrollingY;if(h&&!r.collectionItemHeight)throw new Error(B);if(!h&&!r.collectionItemWidth)throw new Error(V);var g=n(r.collectionItemHeight||'"100%"'),m=n(r.collectionItemWidth||'"100%"'),$=function(e,t){var n=g(e,t);return p(n)&&n.indexOf("%")>-1?Math.floor(parseInt(n,10)/100*d.__clientHeight):n},w=function(e,t){var n=m(e,t);return p(n)&&n.indexOf("%")>-1?Math.floor(parseInt(n,10)/100*d.__clientWidth):n},b=r.collectionRepeat.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);if(!b)throw new Error(E.replace("%",r.collectionRepeat));var y=b[1],S=b[2],C=b[3],k=new t({scope:i,transcludeFn:s,transcludeParent:o.parent(),keyExpr:y,listExpr:S,trackByExpr:C,heightGetter:$,widthGetter:w}),T=new e({dataSource:k,element:a.$element,scrollView:a.scrollView});i.$watchCollection(S,function(e){if(e&&!angular.isArray(e))throw new Error("collection-repeat expects an array to repeat over, but instead got '"+typeof e+"'.");c(e)});var I=a.scrollView.__content;a.$element.on("scroll.resize",l),ionic.on("resize",l,window),i.$on("$destroy",function(){T.destroy(),k.destroy(),ionic.off("resize",l,window)})}}}]).directive({ngSrc:i("ngSrc","src"),ngSrcset:i("ngSrcset","srcset"),ngHref:i("ngHref","href")}),u.directive("ionContent",["$timeout","$controller","$ionicBind",function(e,t,n){return{restrict:"E",require:"^?ionNavView",scope:!0,priority:800,compile:function(e,i){function o(e,o,a){var s=e.$parent;if(e.$watch(function(){return(s.$hasHeader?" has-header":"")+(s.$hasSubheader?" has-subheader":"")+(s.$hasFooter?" has-footer":"")+(s.$hasSubfooter?" has-subfooter":"")+(s.$hasTabs?" has-tabs":"")+(s.$hasTabsTop?" has-tabs-top":"")},function(e,t){o.removeClass(t),o.addClass(e)}),e.$hasHeader=e.$hasSubheader=e.$hasFooter=e.$hasSubfooter=e.$hasTabs=e.$hasTabsTop=!1,n(e,a,{$onScroll:"&onScroll",$onScrollComplete:"&onScrollComplete",hasBouncing:"@",padding:"@",direction:"@",scrollbarX:"@",scrollbarY:"@",startX:"@",startY:"@",scrollEventInterval:"@"}),e.direction=e.direction||"y",angular.isDefined(a.padding)&&e.$watch(a.padding,function(e){(r||o).toggleClass("padding",!!e)}),"false"===a.scroll);else if("true"===i.overflowScroll)o.addClass("overflow-scroll");else{var c={el:o[0],delegateHandle:i.delegateHandle,locking:"true"===(i.locking||"true"),bouncing:e.$eval(e.hasBouncing),startX:e.$eval(e.startX)||0,startY:e.$eval(e.startY)||0,scrollbarX:e.$eval(e.scrollbarX)!==!1,scrollbarY:e.$eval(e.scrollbarY)!==!1,scrollingX:e.direction.indexOf("x")>=0,scrollingY:e.direction.indexOf("y")>=0,scrollEventInterval:parseInt(e.scrollEventInterval,10)||10,scrollingComplete:function(){e.$onScrollComplete({scrollTop:this.__scrollTop,scrollLeft:this.__scrollLeft})}};t("$ionicScroll",{$scope:e,scrollViewOptions:c}),e.$on("$destroy",function(){c.scrollingComplete=angular.noop,delete c.el,r=null,o=null,i.$$element=null})}}var r;return e.addClass("scroll-content ionic-scroll"),"false"!=i.scroll?(r=v('
'),r.append(e.contents()),e.append(r)):e.addClass("scroll-content-false"),{pre:o}}}}]),u.directive("exposeAsideWhen",["$window",function(e){return{restrict:"A",require:"^ionSideMenus",link:function(t,n,i,o){function r(){var t="large"==i.exposeAsideWhen?"(min-width:768px)":i.exposeAsideWhen;o.exposeAside(e.matchMedia(t).matches),o.activeAsideResizing(!1)}function a(){o.activeAsideResizing(!0),s()}var s=ionic.debounce(function(){t.$apply(function(){r()})},300,!1);r(),ionic.on("resize",a,e),t.$on("$destroy",function(){ionic.off("resize",a,e)})}}}]);var _="onHold onTap onTouch onRelease onDrag onDragUp onDragRight onDragDown onDragLeft onSwipe onSwipeUp onSwipeRight onSwipeDown onSwipeLeft".split(" ");_.forEach(function(e){u.directive(e,o(e))}),u.directive("ionNavBar",r()).directive("ionHeaderBar",r()).directive("ionHeaderBar",a(!0)).directive("ionFooterBar",a(!1)),u.directive("ionInfiniteScroll",["$timeout",function(e){function t(e,t,n){return n?t*(1-parseFloat(e,10)/100):t-parseFloat(e,10)}return{restrict:"E",require:["^$ionicScroll","ionInfiniteScroll"],template:'
',scope:!0,controller:["$scope","$attrs",function(e,n){this.isLoading=!1,this.scrollView=null,this.getMaxScroll=function(){var e=(n.distance||"2.5%").trim(),i=-1!==e.indexOf("%"),o=this.scrollView.getScrollMax();return{left:this.scrollView.options.scrollingX?t(e,o.left,i):-1,top:this.scrollView.options.scrollingY?t(e,o.top,i):-1}}}],link:function(t,n,i,o){function r(){if(!s.isLoading){var e=c.getValues(),t=s.getMaxScroll();(-1!==t.left&&e.left>=t.left||-1!==t.top&&e.top>=t.top)&&l()}}var a=o[0],s=o[1],c=s.scrollView=a.scrollView;t.icon=function(){return angular.isDefined(i.icon)?i.icon:"ion-loading-d"};var l=function(){n[0].classList.add("active"),s.isLoading=!0,t.$parent&&t.$parent.$apply(i.onInfinite||"")},u=function(){n[0].classList.remove("active"),e(function(){c.resize(),d()},0,!1),s.isLoading=!1};t.$on("scroll.infiniteScrollComplete",function(){u()}),t.$on("$destroy",function(){a&&a.$element&&a.$element.off("scroll",d)});var d=ionic.animationFrameThrottle(r);setTimeout(d),a.$element.on("scroll",d)}}}]);var A='
',P='
';u.directive("ionItem",["$animate","$compile",function(){return{restrict:"E",controller:["$scope","$element",function(e,t){this.$scope=e,this.$element=t}],scope:!0,compile:function(e,t){var n=angular.isDefined(t.href)||angular.isDefined(t.ngHref)||angular.isDefined(t.uiSref),i=n||/ion-(delete|option|reorder)-button/i.test(e.html());if(i){var o=v(n?A:P);o.append(e.contents()),e.append(o),e.addClass("item item-complex")}else e.addClass("item");return function(e,t,n){e.$href=function(){return n.href||n.ngHref},e.$target=function(){return n.target||"_self"}}}}}]);var D='
';u.directive("ionDeleteButton",["$animate",function(){return{restrict:"E",require:["^ionItem","^?ionList"],priority:Number.MAX_VALUE,compile:function(e,t){return t.$set("class",(t["class"]||"")+" button icon button-icon",!0),function(e,t,n,i){var o=i[0],r=i[1],a=v(D);a.append(t),o.$element.append(a).addClass("item-left-editable"),r&&r.showDelete()&&a.addClass("visible active")}}}}]),u.directive("itemFloatingLabel",function(){return{restrict:"C",link:function(e,t){var n=t[0],i=n.querySelector("input, textarea"),o=n.querySelector(".input-label");if(i&&o){var r=function(){i.value?o.classList.add("has-input"):o.classList.remove("has-input")};i.addEventListener("input",r);var a=angular.element(i).controller("ngModel");a&&(a.$render=function(){i.value=a.$viewValue||"",r()}),e.$on("$destroy",function(){i.removeEventListener("input",r)})}}}});var H='
';u.directive("ionOptionButton",["$compile",function(){function e(e){e.stopPropagation()}return{restrict:"E",require:"^ionItem",priority:Number.MAX_VALUE,compile:function(t,n){return n.$set("class",(n["class"]||"")+" button",!0),function(t,n,i,o){o.optionsContainer||(o.optionsContainer=v(H),o.$element.append(o.optionsContainer)),o.optionsContainer.append(n),n.on("click",e)}}}}]);var L='
';u.directive("ionReorderButton",["$animate","$parse",function(e,t){return{restrict:"E",require:["^ionItem","^?ionList"],priority:Number.MAX_VALUE,compile:function(e,n){return n.$set("class",(n["class"]||"")+" button icon button-icon",!0),e[0].setAttribute("data-prevent-scroll",!0),function(e,n,i,o){var r=o[0],a=o[1],s=t(i.onReorder);e.$onReorder=function(t,n){s(e,{$fromIndex:t,$toIndex:n})},i.ngClick||i.onClick||i.onclick||(n[0].onclick=function(e){return e.stopPropagation(),!1});var c=v(L);c.append(n),r.$element.append(c).addClass("item-right-editable"),a&&a.showReorder()&&c.addClass("visible active")}}}}]),u.directive("keyboardAttach",function(){return function(e,t){function n(e){if(!ionic.Platform.isAndroid()||ionic.Platform.isFullScreen){var n=e.keyboardHeight||e.detail.keyboardHeight;t.css("bottom",n+"px"),o=t.controller("$ionicScroll"),o&&(o.scrollView.__container.style.bottom=n+s(t[0])+"px")}}function i(){(!ionic.Platform.isAndroid()||ionic.Platform.isFullScreen)&&(t.css("bottom",""),o&&(o.scrollView.__container.style.bottom=""))}ionic.on("native.keyboardshow",n,window),ionic.on("native.keyboardhide",i,window),ionic.on("native.showkeyboard",n,window),ionic.on("native.hidekeyboard",i,window);var o;e.$on("$destroy",function(){ionic.off("native.keyboardshow",n,window),ionic.off("native.keyboardhide",i,window),ionic.off("native.showkeyboard",n,window),ionic.off("native.hidekeyboard",i,window)})}}),u.directive("ionList",["$animate","$timeout",function(e,t){return{restrict:"E",require:["ionList","^?$ionicScroll"],controller:"$ionicList",compile:function(e,n){var i=v('
').append(e.contents()).addClass(n.type);return e.append(i),function(e,i,o,r){function a(){function o(e,t){t()&&e.addClass("visible")||e.removeClass("active"),ionic.requestAnimationFrame(function(){t()&&e.addClass("active")||e.removeClass("visible")})}s.listView=new ionic.views.ListView({el:i[0],listEl:i.children()[0],scrollEl:c&&c.element,scrollView:c&&c.scrollView,onReorder:function(e,n,i){var o=v(e).scope();o&&o.$onReorder&&t(function(){o.$onReorder(n,i)})},canSwipe:function(){return s.canSwipeItems()}});h(n.canSwipe)&&e.$watch("!!("+n.canSwipe+")",function(e){s.canSwipeItems(e)}),h(n.showDelete)&&e.$watch("!!("+n.showDelete+")",function(e){s.showDelete(e)}),h(n.showReorder)&&e.$watch("!!("+n.showReorder+")",function(e){s.showReorder(e)}),e.$watch(function(){return s.showDelete()},function(e,t){if(e||t){e&&s.closeOptionButtons(),s.canSwipeItems(!e),i.children().toggleClass("list-left-editing",e),i.toggleClass("disable-pointer-events",e);var n=v(i[0].getElementsByClassName("item-delete"));o(n,s.showDelete)}}),e.$watch(function(){return s.showReorder()},function(e,t){if(e||t){e&&s.closeOptionButtons(),s.canSwipeItems(!e),i.children().toggleClass("list-right-editing",e),i.toggleClass("disable-pointer-events",e);var n=v(i[0].getElementsByClassName("item-reorder"));o(n,s.showReorder)}})}var s=r[0],c=r[1];t(a)}}}}]),u.directive("menuClose",["$ionicViewService",function(){return{restrict:"AC",require:"^ionSideMenus",link:function(e,t,n,i){t.bind("click",function(){i.close()})}}}]),u.directive("menuToggle",["$ionicViewService",function(){return{restrict:"AC",require:"^ionSideMenus",link:function(e,t,n,i){var o=n.menuToggle||"left";t.bind("click",function(){"left"===o?i.toggleLeft():"right"===o&&i.toggleRight()})}}}]),u.directive("ionModal",[function(){return{restrict:"E",transclude:!0,replace:!0,controller:[function(){}],template:'
'}}]),u.directive("ionModalView",function(){return{restrict:"E",compile:function(e){e.addClass("modal")}}}),u.directive("ionNavBackButton",["$animate","$rootScope","$sanitize","$ionicNavBarConfig","$ionicNgClick",function(e,t,n,i,o){var r=!1;return t.$on("$viewHistory.historyChange",function(e,t){r=!!t.showBack}),{restrict:"E",require:"^ionNavBar",compile:function(t){t.addClass("button back-button ng-hide");var a=!!(t.html()||"").match(/class=.*?ion-/);return function(t,s,c,l){a||-1!==s[0].className.indexOf("ion-")||s.addClass(i.backButtonIcon),h(c.ngClick)||o(t,s,l.back),t.$watch(function(){return h(c.fromTitle)&&(s[0].innerHTML='
'+n(t.oldTitle)+" "),!(!r||!t.backButtonShown)},ionic.animationFrameThrottle(function(t){t?e.removeClass(s,"ng-hide"):e.addClass(s,"ng-hide")}))}}}}]),u.constant("$ionicNavBarConfig",{transition:"nav-title-slide-ios7",alignTitle:"center",backButtonIcon:"ion-ios7-arrow-back"}),u.directive("ionNavBar",["$ionicViewService","$rootScope","$animate","$compile","$ionicNavBarConfig",function(e,t,n,i,o){return{restrict:"E",controller:"$ionicNavBar",scope:!0,compile:function(e,t){function n(e,t,n,i){i._headerBarView=new ionic.views.HeaderBar({el:t[0],alignTitle:n.alignTitle||o.alignTitle||"center"}),e.backButtonShown=!1,e.shouldAnimate=!0,e.isReverse=!1,e.isInvisible=!0,e.$on("$destroy",function(){e.$parent.$hasHeader=!1}),e.$watch(function(){return(e.isReverse?" reverse":"")+(e.isInvisible?" invisible":"")+(e.shouldAnimate?"":" no-animation")},function(e,n){t.removeClass(n),t.addClass(e)})}return e.addClass("bar bar-header nav-bar").append('
'),e.addClass(h(t.animation)?t.animation:o.transition),{pre:n}}}}]),u.directive("ionNavButtons",["$compile","$animate",function(e,t){return{require:"^ionNavBar",restrict:"E",compile:function(n){var i=n.contents().remove();
+return function(n,o,r,a){var s="right"===r.side?a.rightButtonsElement:a.leftButtonsElement,c=v("
").append(i);o.append(c),e(c)(n),ionic.requestAnimationFrame(function(){n.$$destroyed||t.enter(c,s)}),n.$on("$destroy",function(){t.leave(c)}),o.css("display","none")}}}}]),u.directive("navClear",["$ionicViewService","$state","$location","$window","$rootScope",function(e,t,n,i,o){return o.$on("$stateChangeError",function(){e.nextViewOptions(null)}),{priority:100,restrict:"AC",compile:function(){function t(t,n){function o(){r=t.$on("$stateChangeStart",function(){e.nextViewOptions({disableAnimate:!0,disableBack:!0}),r()}),i.setTimeout(r,300)}var r;n.on("click",o)}return{pre:t}}}}]),u.constant("$ionicNavViewConfig",{transition:"slide-left-right-ios7"}),u.directive("ionNavView",["$ionicViewService","$state","$compile","$controller","$animate",function(e,t,n,i,o){var r=!1,a={restrict:"E",terminal:!0,priority:2e3,transclude:!0,controller:function(){},compile:function(s,c,l){return function(s,c,u){function d(r){o.enabled()===!1&&(r=!1);var a=t.$current&&t.$current.locals[p];if(a!==h){var l=e.getRenderer(c,u,s);if(f&&(f.$destroy(),f=null),!a)return h=null,w.state=null,c.append(m);var d=v("
").html(a.$template).contents(),$=l().register(d);l(r).leave(),h=a,w.state=a.$$state,l(r).enter(d);var b=n(d);if(f=s.$new(),f.$navDirection=$.navDirection,a.$$controller){a.$scope=f;var y=i(a.$$controller,a);c.children().data("$ngControllerController",y)}b(f);var S=e._getViewById($.viewId)||{};f.$broadcast("$viewContentLoaded",S),g&&f.$eval(g),d=null}}var f,h,p=u[a.name]||u.name||"",g=u.onload||"",m=l(s);c.append(m);var $=c.parent().inheritedData("$uiView");p.indexOf("@")<0&&(p=p+"@"+($&&$.state?$.state.name:""));var w={name:p,state:null};c.data("$uiView",w);var b=function(){if(!r){r=!0;try{d(!0)}catch(e){throw r=!1,e}r=!1}};s.$on("$stateChangeSuccess",b),d(!1)}}};return a}]),u.config(["$provide",function(e){e.decorator("ngClickDirective",["$delegate",function(e){return e.shift(),e}])}]).factory("$ionicNgClick",["$parse",function(e){return function(t,n,i){var o=angular.isFunction(i)?i:e(i);n.on("click",function(e){t.$apply(function(){o(t,{$event:e})})}),n.onclick=function(){}}}]).directive("ngClick",["$ionicNgClick",function(e){return function(t,n,i){e(t,n,i.ngClick)}}]).directive("ionStopEvent",function(){return{restrict:"A",link:function(e,t,n){t.bind(n.ionStopEvent,c)}}}),u.directive("ionPane",function(){return{restrict:"E",link:function(e,t){t.addClass("pane")}}}),u.directive("ionPopover",[function(){return{restrict:"E",transclude:!0,replace:!0,controller:[function(){}],template:''}}]),u.directive("ionPopoverView",function(){return{restrict:"E",compile:function(e){e.append(angular.element('
')),e.addClass("popover")}}}),u.directive("ionRadio",function(){return{restrict:"E",replace:!0,require:"?ngModel",transclude:!0,template:'
',compile:function(e,t){t.icon&&e.children().eq(2).removeClass("ion-checkmark").addClass(t.icon);var n=e.find("input");return f({name:t.name,value:t.value,disabled:t.disabled,"ng-value":t.ngValue,"ng-model":t.ngModel,"ng-disabled":t.ngDisabled,"ng-change":t.ngChange},function(e,t){h(e)&&n.attr(t,e)}),function(e,t,n){e.getValue=function(){return e.ngValue||n.value}}}}}),u.directive("ionRefresher",["$ionicBind",function(e){return{restrict:"E",replace:!0,require:"^$ionicScroll",template:'',compile:function(t,n){return angular.isUndefined(n.pullingIcon)&&n.$set("pullingIcon","ion-ios7-arrow-down"),angular.isUndefined(n.refreshingIcon)&&n.$set("refreshingIcon","ion-loading-d"),function(t,n,i,o){e(t,i,{pullingIcon:"@",pullingText:"@",refreshingIcon:"@",refreshingText:"@",disablePullingRotation:"@",$onRefresh:"&onRefresh",$onPulling:"&onPulling"}),o._setRefresher(t,n[0]),t.$on("scroll.refreshComplete",function(){t.$evalAsync(function(){o.scrollView.finishPullToRefresh()})})}}}}]),u.directive("ionScroll",["$timeout","$controller","$ionicBind",function(e,t,n){return{restrict:"E",scope:!0,controller:function(){},compile:function(e){function i(e,i,r){var a,s;n(e,r,{direction:"@",paging:"@",$onScroll:"&onScroll",scroll:"@",scrollbarX:"@",scrollbarY:"@",zooming:"@",minZoom:"@",maxZoom:"@"}),e.direction=e.direction||"y",angular.isDefined(r.padding)&&e.$watch(r.padding,function(e){o.toggleClass("padding",!!e)}),e.$eval(e.paging)===!0&&o.addClass("scroll-paging"),e.direction||(e.direction="y");var c=e.$eval(e.paging)===!0,l={el:i[0],delegateHandle:r.delegateHandle,locking:"true"===(r.locking||"true"),bouncing:e.$eval(r.hasBouncing),paging:c,scrollbarX:e.$eval(e.scrollbarX)!==!1,scrollbarY:e.$eval(e.scrollbarY)!==!1,scrollingX:e.direction.indexOf("x")>=0,scrollingY:e.direction.indexOf("y")>=0,zooming:e.$eval(e.zooming)===!0,maxZoom:e.$eval(e.maxZoom)||3,minZoom:e.$eval(e.minZoom)||.5};c&&(l.speedMultiplier=.8,l.bouncing=!1),s=t("$ionicScroll",{$scope:e,scrollViewOptions:l}),a=e.$parent.scrollView=s.scrollView}e.addClass("scroll-view ionic-scroll");var o=v('
');return o.append(e.contents()),e.append(o),{pre:i}}}}]),u.directive("ionSideMenu",function(){return{restrict:"E",require:"^ionSideMenus",scope:!0,compile:function(e,t){return angular.isUndefined(t.isEnabled)&&t.$set("isEnabled","true"),angular.isUndefined(t.width)&&t.$set("width","275"),e.addClass("menu menu-"+t.side),function(e,n,i,o){e.side=i.side||"left";var r=o[e.side]=new ionic.views.SideMenu({width:t.width,el:n[0],isEnabled:!0});e.$watch(i.width,function(e){var t=+e;t&&t==e&&r.setWidth(+e)}),e.$watch(i.isEnabled,function(e){r.setIsEnabled(!!e)})}}}}),u.directive("ionSideMenuContent",["$timeout","$ionicGesture","$window",function(e,t,n){return{restrict:"EA",require:"^ionSideMenus",scope:!0,compile:function(i,o){function r(r,a,s,c){function l(e){0!==c.getOpenAmount()?(c.close(),e.gesture.srcEvent.preventDefault(),v=null,g=null):v||(v=ionic.tap.pointerCoord(e.gesture.srcEvent))}function u(e){c.isDraggableTarget(e)&&"x"==p(e)&&(c._handleDrag(e),e.gesture.srcEvent.preventDefault())}function d(e){"x"==p(e)&&e.gesture.srcEvent.preventDefault()}function f(e){c._endDrag(e),v=null,g=null}function p(e){if(g)return g;if(e&&e.gesture){if(v){var t=ionic.tap.pointerCoord(e.gesture.srcEvent),n=Math.abs(t.x-v.x),i=Math.abs(t.y-v.y),o=i>n?"y":"x";return Math.max(n,i)>30&&(g=o),o}v=ionic.tap.pointerCoord(e.gesture.srcEvent)}return"x"}var v=null,g=null;a.addClass("menu-content pane"),h(o.dragContent)?r.$watch(o.dragContent,function(e){c.canDragContent(e)}):c.canDragContent(!0),h(o.edgeDragThreshold)&&r.$watch(o.edgeDragThreshold,function(e){c.edgeDragThreshold(e)});var m={element:i[0],onDrag:function(){},endDrag:function(){},getTranslateX:function(){return r.sideMenuContentTranslateX||0},setTranslateX:ionic.animationFrameThrottle(function(t){var n=m.offsetX+t;a[0].style[ionic.CSS.TRANSFORM]="translate3d("+n+"px,0,0)",e(function(){r.sideMenuContentTranslateX=t})}),setMarginLeft:ionic.animationFrameThrottle(function(e){e?(e=parseInt(e,10),a[0].style[ionic.CSS.TRANSFORM]="translate3d("+e+"px,0,0)",a[0].style.width=n.innerWidth-e+"px",m.offsetX=e):(a[0].style[ionic.CSS.TRANSFORM]="translate3d(0,0,0)",a[0].style.width="",m.offsetX=0)}),enableAnimation:function(){r.animationEnabled=!0,a[0].classList.add("menu-animated")},disableAnimation:function(){r.animationEnabled=!1,a[0].classList.remove("menu-animated")},offsetX:0};c.setContent(m);var $={stop_browser_behavior:!1},w=t.on("tap",l,a,$),b=t.on("dragright",u,a,$),y=t.on("dragleft",u,a,$),S=t.on("dragup",d,a,$),C=t.on("dragdown",d,a,$),k=t.on("release",f,a,$);r.$on("$destroy",function(){t.off(y,"dragleft",u),t.off(b,"dragright",u),t.off(S,"dragup",d),t.off(C,"dragdown",d),t.off(k,"release",f),t.off(w,"tap",l)})}return{pre:r}}}}]),u.directive("ionSideMenus",["$ionicBody",function(e){return{restrict:"ECA",controller:"$ionicSideMenus",compile:function(t,n){function i(t){t.$on("$ionicExposeAside",function(n,i){t.$exposeAside||(t.$exposeAside={}),t.$exposeAside.active=i,e.enableClass(i,"aside-open")}),t.$on("$destroy",function(){e.removeClass("menu-open","aside-open")})}return n.$set("class",(n["class"]||"")+" view"),{pre:i}}}}]),u.directive("ionSlideBox",["$timeout","$compile","$ionicSlideBoxDelegate",function(e,t,n){return{restrict:"E",replace:!0,transclude:!0,scope:{autoPlay:"=",doesContinue:"@",slideInterval:"@",showPager:"@",pagerClick:"&",disableScroll:"@",onSlideChanged:"&",activeSlide:"=?"},controller:["$scope","$element","$attrs",function(t,i,o){var r=t.$eval(t.doesContinue)===!0,a=h(o.autoPlay)?!!t.autoPlay:!1,s=a?t.$eval(t.slideInterval)||4e3:0,c=new ionic.views.Slider({el:i[0],auto:s,continuous:r,startSlide:t.activeSlide,slidesChanged:function(){t.currentSlide=c.currentIndex(),e(function(){})},callback:function(n){t.currentSlide=n,t.onSlideChanged({index:t.currentSlide,$index:t.currentSlide}),t.$parent.$broadcast("slideBox.slideChanged",n),t.activeSlide=n,e(function(){})}});c.enableSlide(t.$eval(o.disableScroll)!==!0),t.$watch("activeSlide",function(e){angular.isDefined(e)&&c.slide(e)}),t.$on("slideBox.nextSlide",function(){c.next()}),t.$on("slideBox.prevSlide",function(){c.prev()}),t.$on("slideBox.setSlide",function(e,t){c.slide(t)}),this.__slider=c;var l=n._registerInstance(c,o.delegateHandle);t.$on("$destroy",l),this.slidesCount=function(){return c.slidesCount()},this.onPagerClick=function(e){t.pagerClick({index:e})},e(function(){c.load()})}],template:'',link:function(e,n){if(e.$eval(e.showPager)!==!1){var i=e.$new(),o=v(" ");n.append(o),t(o)(i)}}}}]).directive("ionSlide",function(){return{restrict:"E",require:"^ionSlideBox",compile:function(e){return e.addClass("slider-slide"),function(){}}}}).directive("ionPager",function(){return{restrict:"E",replace:!0,require:"^ionSlideBox",template:'',link:function(e,t,n,i){var o=function(e){for(var n=t[0].children,i=n.length,o=0;i>o;o++)o==e?n[o].classList.add("active"):n[o].classList.remove("active")};e.pagerClick=function(e){i.onPagerClick(e)},e.numSlides=function(){return new Array(i.slidesCount())},e.$watch("currentSlide",function(e){o(e)})}}}),u.constant("$ionicTabConfig",{type:""}),u.directive("ionTab",["$rootScope","$animate","$ionicBind","$compile",function(e,t,n,i){function o(e,t){return angular.isDefined(t)?" "+e+'="'+t+'"':""}return{restrict:"E",require:["^ionTabs","ionTab"],replace:!0,controller:"$ionicTab",scope:!0,compile:function(e,r){var a=" ",s=v('').append(e.contents().remove());return function(e,o,r,c){function l(){h.tabMatchesState()&&f.select(e,!1)}var u,d,f=c[0],h=c[1],p=s[0].querySelector("ion-nav-view")||s[0].querySelector("data-ion-nav-view"),g=p&&p.getAttribute("name");n(e,r,{animate:"=",onSelect:"&",onDeselect:"&",title:"@",uiSref:"@",href:"@"}),f.add(e),e.$on("$destroy",function(){e.$tabsDestroy||f.remove(e),m.isolateScope().$destroy(),m.remove()}),o[0].removeAttribute("title"),g&&(h.navViewName=e.navViewName=g),e.$on("$stateChangeSuccess",l),l();var m=v(a);m.data("$ionTabsController",f),m.data("$ionTabController",h),f.$tabsElement.append(i(m)(e)),e.$watch("$tabSelected",function(n){u&&u.$destroy(),u=null,d&&t.leave(d),d=null,n&&(u=e.$new(),d=s.clone(),t.enter(d,f.$element),i(d)(u))})}}}}]),u.directive("ionTabNav",[function(){return{restrict:"E",replace:!0,require:["^ionTabs","^ionTab"],template:'
{{badge}} ',scope:{title:"@",icon:"@",iconOn:"@",iconOff:"@",badge:"=",hidden:"@",badgeStyle:"@","class":"@"},compile:function(){return function(e,t,n,i){var o=i[0],r=i[1];t[0].removeAttribute("title"),e.selectTab=function(e){e.preventDefault(),o.select(r.$scope,!0)},n.ngClick||t.on("click",function(t){e.$apply(function(){e.selectTab(t)})}),e.isHidden=function(){return"true"===n.hidden||n.hidden===!0?!0:!1},e.getIconOn=function(){return e.iconOn||e.icon},e.getIconOff=function(){return e.iconOff||e.icon},e.isTabActive=function(){return o.selectedTab()===r.$scope}}}}}]),u.constant("$ionicTabsConfig",{position:"",type:""}),u.directive("ionTabs",["$ionicViewService","$ionicTabsDelegate","$ionicTabsConfig",function(e,t,n){return{restrict:"E",scope:!0,controller:"$ionicTabs",compile:function(e){function i(e,n,i,o){var r=t._registerInstance(o,i.delegateHandle);e.$on("$destroy",function(){e.$tabsDestroy=!0,r()}),o.$scope=e,o.$element=n,o.$tabsElement=v(n[0].querySelector(".tabs"));var a=n[0];e.$watch(function(){return a.className},function(t){var n=-1!==t.indexOf("tabs-top"),i=-1!==t.indexOf("tabs-item-hide");e.$hasTabs=!n&&!i,e.$hasTabsTop=n&&!i}),e.$on("$destroy",function(){delete e.$hasTabs,delete e.$hasTabsTop})}e.addClass("view");var o=v('
');return o.append(e.contents()),e.append(o),e.addClass(n.position),e.addClass(n.type),{pre:i}}}}]),u.directive("ionToggle",["$ionicGesture","$timeout",function(){return{restrict:"E",replace:!0,require:"?ngModel",transclude:!0,template:'
',compile:function(e,t){var n=e.find("input");return f({name:t.name,"ng-value":t.ngValue,"ng-model":t.ngModel,"ng-checked":t.ngChecked,"ng-disabled":t.ngDisabled,"ng-true-value":t.ngTrueValue,"ng-false-value":t.ngFalseValue,"ng-change":t.ngChange},function(e,t){h(e)&&n.attr(t,e)}),t.toggleClass&&e[0].getElementsByTagName("label")[0].classList.add(t.toggleClass),function(e,t){var n,i,o,r;n=t[0].getElementsByTagName("label")[0],i=n.children[0],o=n.children[1],r=o.children[0];var a=v(i).controller("ngModel");e.toggle=new ionic.views.Toggle({el:n,track:o,checkbox:i,handle:r,onChange:function(){a.$setViewValue(i.checked?!0:!1),e.$apply()}}),e.$on("$destroy",function(){e.toggle.destroy()})}}}}]),u.directive("ionView",["$ionicViewService","$rootScope","$animate",function(){return{restrict:"EA",priority:1e3,require:["^?ionNavBar","^?ionModal"],compile:function(e){return e.addClass("pane"),e[0].removeAttribute("title"),function(e,t,n,i){var o=i[0],r=i[1];if(o&&!r){if(angular.isDefined(n.title)){var a=n.title;o.changeTitle(a,e.$navDirection),n.$observe("title",function(e){o.setTitle(e)})}var s=angular.isDefined(n.hideBackButton)?n.hideBackButton:"false";e.$watch(s,function(e){o.showBackButton(!e)});var c=angular.isDefined(n.hideNavBar)?n.hideNavBar:"false";e.$watch(c,function(e){o.showBar(!e)})}}}}}])}();
\ No newline at end of file
diff --git a/client/lib/ionic/js/ionic.js b/client/lib/ionic/js/ionic.js
new file mode 100644
index 0000000..d89ada5
--- /dev/null
+++ b/client/lib/ionic/js/ionic.js
@@ -0,0 +1,7611 @@
+/*!
+ * Copyright 2014 Drifty Co.
+ * http://drifty.com/
+ *
+ * Ionic, v1.0.0-beta.13
+ * A powerful HTML5 mobile app framework.
+ * http://ionicframework.com/
+ *
+ * By @maxlynch, @benjsperry, @adamdbradley <3
+ *
+ * Licensed under the MIT license. Please see LICENSE for more information.
+ *
+ */
+
+(function() {
+
+// Create global ionic obj and its namespaces
+// build processes may have already created an ionic obj
+window.ionic = window.ionic || {};
+window.ionic.views = {};
+window.ionic.version = '1.0.0-beta.13';
+
+(function(window, document, ionic) {
+
+ var readyCallbacks = [];
+ var isDomReady = document.readyState === 'complete' || document.readyState === 'interactive';
+
+ function domReady() {
+ isDomReady = true;
+ for(var x=0; x
x2) return false;
+ if(y < y1 || y > y2) return false;
+ return true;
+ }
+ };
+
+ //Shortcuts
+ ionic.requestAnimationFrame = ionic.DomUtil.requestAnimationFrame;
+ ionic.cancelAnimationFrame = ionic.DomUtil.cancelAnimationFrame;
+ ionic.animationFrameThrottle = ionic.DomUtil.animationFrameThrottle;
+})(window, document, ionic);
+
+/**
+ * ion-events.js
+ *
+ * Author: Max Lynch
+ *
+ * Framework events handles various mobile browser events, and
+ * detects special events like tap/swipe/etc. and emits them
+ * as custom events that can be used in an app.
+ *
+ * Portions lovingly adapted from github.com/maker/ratchet and github.com/alexgibson/tap.js - thanks guys!
+ */
+
+(function(ionic) {
+
+ // Custom event polyfill
+ ionic.CustomEvent = (function() {
+ if( typeof window.CustomEvent === 'function' ) return CustomEvent;
+
+ var customEvent = function(event, params) {
+ var evt;
+ params = params || {
+ bubbles: false,
+ cancelable: false,
+ detail: undefined
+ };
+ try {
+ evt = document.createEvent("CustomEvent");
+ evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
+ } catch (error) {
+ // fallback for browsers that don't support createEvent('CustomEvent')
+ evt = document.createEvent("Event");
+ for (var param in params) {
+ evt[param] = params[param];
+ }
+ evt.initEvent(event, params.bubbles, params.cancelable);
+ }
+ return evt;
+ };
+ customEvent.prototype = window.Event.prototype;
+ return customEvent;
+ })();
+
+
+ /**
+ * @ngdoc utility
+ * @name ionic.EventController
+ * @module ionic
+ */
+ ionic.EventController = {
+ VIRTUALIZED_EVENTS: ['tap', 'swipe', 'swiperight', 'swipeleft', 'drag', 'hold', 'release'],
+
+ /**
+ * @ngdoc method
+ * @name ionic.EventController#trigger
+ * @alias ionic.trigger
+ * @param {string} eventType The event to trigger.
+ * @param {object} data The data for the event. Hint: pass in
+ * `{target: targetElement}`
+ * @param {boolean=} bubbles Whether the event should bubble up the DOM.
+ * @param {boolean=} cancelable Whether the event should be cancelable.
+ */
+ // Trigger a new event
+ trigger: function(eventType, data, bubbles, cancelable) {
+ var event = new ionic.CustomEvent(eventType, {
+ detail: data,
+ bubbles: !!bubbles,
+ cancelable: !!cancelable
+ });
+
+ // Make sure to trigger the event on the given target, or dispatch it from
+ // the window if we don't have an event target
+ data && data.target && data.target.dispatchEvent && data.target.dispatchEvent(event) || window.dispatchEvent(event);
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.EventController#on
+ * @alias ionic.on
+ * @description Listen to an event on an element.
+ * @param {string} type The event to listen for.
+ * @param {function} callback The listener to be called.
+ * @param {DOMElement} element The element to listen for the event on.
+ */
+ on: function(type, callback, element) {
+ var e = element || window;
+
+ // Bind a gesture if it's a virtual event
+ for(var i = 0, j = this.VIRTUALIZED_EVENTS.length; i < j; i++) {
+ if(type == this.VIRTUALIZED_EVENTS[i]) {
+ var gesture = new ionic.Gesture(element);
+ gesture.on(type, callback);
+ return gesture;
+ }
+ }
+
+ // Otherwise bind a normal event
+ e.addEventListener(type, callback);
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.EventController#off
+ * @alias ionic.off
+ * @description Remove an event listener.
+ * @param {string} type
+ * @param {function} callback
+ * @param {DOMElement} element
+ */
+ off: function(type, callback, element) {
+ element.removeEventListener(type, callback);
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.EventController#onGesture
+ * @alias ionic.onGesture
+ * @description Add an event listener for a gesture on an element.
+ *
+ * Available eventTypes (from [hammer.js](http://eightmedia.github.io/hammer.js/)):
+ *
+ * `hold`, `tap`, `doubletap`, `drag`, `dragstart`, `dragend`, `dragup`, `dragdown`,
+ * `dragleft`, `dragright`, `swipe`, `swipeup`, `swipedown`, `swipeleft`, `swiperight`,
+ * `transform`, `transformstart`, `transformend`, `rotate`, `pinch`, `pinchin`, `pinchout`,
+ * `touch`, `release`
+ *
+ * @param {string} eventType The gesture event to listen for.
+ * @param {function(e)} callback The function to call when the gesture
+ * happens.
+ * @param {DOMElement} element The angular element to listen for the event on.
+ */
+ onGesture: function(type, callback, element, options) {
+ var gesture = new ionic.Gesture(element, options);
+ gesture.on(type, callback);
+ return gesture;
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.EventController#offGesture
+ * @alias ionic.offGesture
+ * @description Remove an event listener for a gesture on an element.
+ * @param {string} eventType The gesture event.
+ * @param {function(e)} callback The listener that was added earlier.
+ * @param {DOMElement} element The element the listener was added on.
+ */
+ offGesture: function(gesture, type, callback) {
+ gesture.off(type, callback);
+ },
+
+ handlePopState: function(event) {}
+ };
+
+
+ // Map some convenient top-level functions for event handling
+ ionic.on = function() { ionic.EventController.on.apply(ionic.EventController, arguments); };
+ ionic.off = function() { ionic.EventController.off.apply(ionic.EventController, arguments); };
+ ionic.trigger = ionic.EventController.trigger;//function() { ionic.EventController.trigger.apply(ionic.EventController.trigger, arguments); };
+ ionic.onGesture = function() { return ionic.EventController.onGesture.apply(ionic.EventController.onGesture, arguments); };
+ ionic.offGesture = function() { return ionic.EventController.offGesture.apply(ionic.EventController.offGesture, arguments); };
+
+})(window.ionic);
+
+/**
+ * Simple gesture controllers with some common gestures that emit
+ * gesture events.
+ *
+ * Ported from github.com/EightMedia/hammer.js Gestures - thanks!
+ */
+(function(ionic) {
+
+ /**
+ * ionic.Gestures
+ * use this to create instances
+ * @param {HTMLElement} element
+ * @param {Object} options
+ * @returns {ionic.Gestures.Instance}
+ * @constructor
+ */
+ ionic.Gesture = function(element, options) {
+ return new ionic.Gestures.Instance(element, options || {});
+ };
+
+ ionic.Gestures = {};
+
+ // default settings
+ ionic.Gestures.defaults = {
+ // add css to the element to prevent the browser from doing
+ // its native behavior. this doesnt prevent the scrolling,
+ // but cancels the contextmenu, tap highlighting etc
+ // set to false to disable this
+ stop_browser_behavior: 'disable-user-behavior'
+ };
+
+ // detect touchevents
+ ionic.Gestures.HAS_POINTEREVENTS = window.navigator.pointerEnabled || window.navigator.msPointerEnabled;
+ ionic.Gestures.HAS_TOUCHEVENTS = ('ontouchstart' in window);
+
+ // dont use mouseevents on mobile devices
+ ionic.Gestures.MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android|silk/i;
+ ionic.Gestures.NO_MOUSEEVENTS = ionic.Gestures.HAS_TOUCHEVENTS && window.navigator.userAgent.match(ionic.Gestures.MOBILE_REGEX);
+
+ // eventtypes per touchevent (start, move, end)
+ // are filled by ionic.Gestures.event.determineEventTypes on setup
+ ionic.Gestures.EVENT_TYPES = {};
+
+ // direction defines
+ ionic.Gestures.DIRECTION_DOWN = 'down';
+ ionic.Gestures.DIRECTION_LEFT = 'left';
+ ionic.Gestures.DIRECTION_UP = 'up';
+ ionic.Gestures.DIRECTION_RIGHT = 'right';
+
+ // pointer type
+ ionic.Gestures.POINTER_MOUSE = 'mouse';
+ ionic.Gestures.POINTER_TOUCH = 'touch';
+ ionic.Gestures.POINTER_PEN = 'pen';
+
+ // touch event defines
+ ionic.Gestures.EVENT_START = 'start';
+ ionic.Gestures.EVENT_MOVE = 'move';
+ ionic.Gestures.EVENT_END = 'end';
+
+ // hammer document where the base events are added at
+ ionic.Gestures.DOCUMENT = window.document;
+
+ // plugins namespace
+ ionic.Gestures.plugins = {};
+
+ // if the window events are set...
+ ionic.Gestures.READY = false;
+
+ /**
+ * setup events to detect gestures on the document
+ */
+ function setup() {
+ if(ionic.Gestures.READY) {
+ return;
+ }
+
+ // find what eventtypes we add listeners to
+ ionic.Gestures.event.determineEventTypes();
+
+ // Register all gestures inside ionic.Gestures.gestures
+ for(var name in ionic.Gestures.gestures) {
+ if(ionic.Gestures.gestures.hasOwnProperty(name)) {
+ ionic.Gestures.detection.register(ionic.Gestures.gestures[name]);
+ }
+ }
+
+ // Add touch events on the document
+ ionic.Gestures.event.onTouch(ionic.Gestures.DOCUMENT, ionic.Gestures.EVENT_MOVE, ionic.Gestures.detection.detect);
+ ionic.Gestures.event.onTouch(ionic.Gestures.DOCUMENT, ionic.Gestures.EVENT_END, ionic.Gestures.detection.detect);
+
+ // ionic.Gestures is ready...!
+ ionic.Gestures.READY = true;
+ }
+
+ /**
+ * create new hammer instance
+ * all methods should return the instance itself, so it is chainable.
+ * @param {HTMLElement} element
+ * @param {Object} [options={}]
+ * @returns {ionic.Gestures.Instance}
+ * @name Gesture.Instance
+ * @constructor
+ */
+ ionic.Gestures.Instance = function(element, options) {
+ var self = this;
+
+ // A null element was passed into the instance, which means
+ // whatever lookup was done to find this element failed to find it
+ // so we can't listen for events on it.
+ if(element === null) {
+ void 0;
+ return;
+ }
+
+ // setup ionic.GesturesJS window events and register all gestures
+ // this also sets up the default options
+ setup();
+
+ this.element = element;
+
+ // start/stop detection option
+ this.enabled = true;
+
+ // merge options
+ this.options = ionic.Gestures.utils.extend(
+ ionic.Gestures.utils.extend({}, ionic.Gestures.defaults),
+ options || {});
+
+ // add some css to the element to prevent the browser from doing its native behavoir
+ if(this.options.stop_browser_behavior) {
+ ionic.Gestures.utils.stopDefaultBrowserBehavior(this.element, this.options.stop_browser_behavior);
+ }
+
+ // start detection on touchstart
+ ionic.Gestures.event.onTouch(element, ionic.Gestures.EVENT_START, function(ev) {
+ if(self.enabled) {
+ ionic.Gestures.detection.startDetect(self, ev);
+ }
+ });
+
+ // return instance
+ return this;
+ };
+
+
+ ionic.Gestures.Instance.prototype = {
+ /**
+ * bind events to the instance
+ * @param {String} gesture
+ * @param {Function} handler
+ * @returns {ionic.Gestures.Instance}
+ */
+ on: function onEvent(gesture, handler){
+ var gestures = gesture.split(' ');
+ for(var t=0; t 0 && eventType == ionic.Gestures.EVENT_END) {
+ eventType = ionic.Gestures.EVENT_MOVE;
+ }
+ // no touches, force the end event
+ else if(!count_touches) {
+ eventType = ionic.Gestures.EVENT_END;
+ }
+
+ // store the last move event
+ if(count_touches || last_move_event === null) {
+ last_move_event = ev;
+ }
+
+ // trigger the handler
+ handler.call(ionic.Gestures.detection, self.collectEventData(element, eventType, self.getTouchList(last_move_event, eventType), ev));
+
+ // remove pointerevent from list
+ if(ionic.Gestures.HAS_POINTEREVENTS && eventType == ionic.Gestures.EVENT_END) {
+ count_touches = ionic.Gestures.PointerEvent.updatePointer(eventType, ev);
+ }
+ }
+
+ //debug(sourceEventType +" "+ eventType);
+
+ // on the end we reset everything
+ if(!count_touches) {
+ last_move_event = null;
+ enable_detect = false;
+ touch_triggered = false;
+ ionic.Gestures.PointerEvent.reset();
+ }
+ });
+ },
+
+
+ /**
+ * we have different events for each device/browser
+ * determine what we need and set them in the ionic.Gestures.EVENT_TYPES constant
+ */
+ determineEventTypes: function determineEventTypes() {
+ // determine the eventtype we want to set
+ var types;
+
+ // pointerEvents magic
+ if(ionic.Gestures.HAS_POINTEREVENTS) {
+ types = ionic.Gestures.PointerEvent.getEvents();
+ }
+ // on Android, iOS, blackberry, windows mobile we dont want any mouseevents
+ else if(ionic.Gestures.NO_MOUSEEVENTS) {
+ types = [
+ 'touchstart',
+ 'touchmove',
+ 'touchend touchcancel'];
+ }
+ // for non pointer events browsers and mixed browsers,
+ // like chrome on windows8 touch laptop
+ else {
+ types = [
+ 'touchstart mousedown',
+ 'touchmove mousemove',
+ 'touchend touchcancel mouseup'];
+ }
+
+ ionic.Gestures.EVENT_TYPES[ionic.Gestures.EVENT_START] = types[0];
+ ionic.Gestures.EVENT_TYPES[ionic.Gestures.EVENT_MOVE] = types[1];
+ ionic.Gestures.EVENT_TYPES[ionic.Gestures.EVENT_END] = types[2];
+ },
+
+
+ /**
+ * create touchlist depending on the event
+ * @param {Object} ev
+ * @param {String} eventType used by the fakemultitouch plugin
+ */
+ getTouchList: function getTouchList(ev/*, eventType*/) {
+ // get the fake pointerEvent touchlist
+ if(ionic.Gestures.HAS_POINTEREVENTS) {
+ return ionic.Gestures.PointerEvent.getTouchList();
+ }
+ // get the touchlist
+ else if(ev.touches) {
+ return ev.touches;
+ }
+ // make fake touchlist from mouse position
+ else {
+ ev.identifier = 1;
+ return [ev];
+ }
+ },
+
+
+ /**
+ * collect event data for ionic.Gestures js
+ * @param {HTMLElement} element
+ * @param {String} eventType like ionic.Gestures.EVENT_MOVE
+ * @param {Object} eventData
+ */
+ collectEventData: function collectEventData(element, eventType, touches, ev) {
+
+ // find out pointerType
+ var pointerType = ionic.Gestures.POINTER_TOUCH;
+ if(ev.type.match(/mouse/) || ionic.Gestures.PointerEvent.matchType(ionic.Gestures.POINTER_MOUSE, ev)) {
+ pointerType = ionic.Gestures.POINTER_MOUSE;
+ }
+
+ return {
+ center : ionic.Gestures.utils.getCenter(touches),
+ timeStamp : new Date().getTime(),
+ target : ev.target,
+ touches : touches,
+ eventType : eventType,
+ pointerType : pointerType,
+ srcEvent : ev,
+
+ /**
+ * prevent the browser default actions
+ * mostly used to disable scrolling of the browser
+ */
+ preventDefault: function() {
+ if(this.srcEvent.preventManipulation) {
+ this.srcEvent.preventManipulation();
+ }
+
+ if(this.srcEvent.preventDefault) {
+ //this.srcEvent.preventDefault();
+ }
+ },
+
+ /**
+ * stop bubbling the event up to its parents
+ */
+ stopPropagation: function() {
+ this.srcEvent.stopPropagation();
+ },
+
+ /**
+ * immediately stop gesture detection
+ * might be useful after a swipe was detected
+ * @return {*}
+ */
+ stopDetect: function() {
+ return ionic.Gestures.detection.stopDetect();
+ }
+ };
+ }
+ };
+
+ ionic.Gestures.PointerEvent = {
+ /**
+ * holds all pointers
+ * type {Object}
+ */
+ pointers: {},
+
+ /**
+ * get a list of pointers
+ * @returns {Array} touchlist
+ */
+ getTouchList: function() {
+ var self = this;
+ var touchlist = [];
+
+ // we can use forEach since pointerEvents only is in IE10
+ Object.keys(self.pointers).sort().forEach(function(id) {
+ touchlist.push(self.pointers[id]);
+ });
+ return touchlist;
+ },
+
+ /**
+ * update the position of a pointer
+ * @param {String} type ionic.Gestures.EVENT_END
+ * @param {Object} pointerEvent
+ */
+ updatePointer: function(type, pointerEvent) {
+ if(type == ionic.Gestures.EVENT_END) {
+ this.pointers = {};
+ }
+ else {
+ pointerEvent.identifier = pointerEvent.pointerId;
+ this.pointers[pointerEvent.pointerId] = pointerEvent;
+ }
+
+ return Object.keys(this.pointers).length;
+ },
+
+ /**
+ * check if ev matches pointertype
+ * @param {String} pointerType ionic.Gestures.POINTER_MOUSE
+ * @param {PointerEvent} ev
+ */
+ matchType: function(pointerType, ev) {
+ if(!ev.pointerType) {
+ return false;
+ }
+
+ var types = {};
+ types[ionic.Gestures.POINTER_MOUSE] = (ev.pointerType == ev.MSPOINTER_TYPE_MOUSE || ev.pointerType == ionic.Gestures.POINTER_MOUSE);
+ types[ionic.Gestures.POINTER_TOUCH] = (ev.pointerType == ev.MSPOINTER_TYPE_TOUCH || ev.pointerType == ionic.Gestures.POINTER_TOUCH);
+ types[ionic.Gestures.POINTER_PEN] = (ev.pointerType == ev.MSPOINTER_TYPE_PEN || ev.pointerType == ionic.Gestures.POINTER_PEN);
+ return types[pointerType];
+ },
+
+
+ /**
+ * get events
+ */
+ getEvents: function() {
+ return [
+ 'pointerdown MSPointerDown',
+ 'pointermove MSPointerMove',
+ 'pointerup pointercancel MSPointerUp MSPointerCancel'
+ ];
+ },
+
+ /**
+ * reset the list
+ */
+ reset: function() {
+ this.pointers = {};
+ }
+ };
+
+
+ ionic.Gestures.utils = {
+ /**
+ * extend method,
+ * also used for cloning when dest is an empty object
+ * @param {Object} dest
+ * @param {Object} src
+ * @param {Boolean} merge do a merge
+ * @returns {Object} dest
+ */
+ extend: function extend(dest, src, merge) {
+ for (var key in src) {
+ if(dest[key] !== undefined && merge) {
+ continue;
+ }
+ dest[key] = src[key];
+ }
+ return dest;
+ },
+
+
+ /**
+ * find if a node is in the given parent
+ * used for event delegation tricks
+ * @param {HTMLElement} node
+ * @param {HTMLElement} parent
+ * @returns {boolean} has_parent
+ */
+ hasParent: function(node, parent) {
+ while(node){
+ if(node == parent) {
+ return true;
+ }
+ node = node.parentNode;
+ }
+ return false;
+ },
+
+
+ /**
+ * get the center of all the touches
+ * @param {Array} touches
+ * @returns {Object} center
+ */
+ getCenter: function getCenter(touches) {
+ var valuesX = [], valuesY = [];
+
+ for(var t= 0,len=touches.length; t= y) {
+ return touch1.pageX - touch2.pageX > 0 ? ionic.Gestures.DIRECTION_LEFT : ionic.Gestures.DIRECTION_RIGHT;
+ }
+ else {
+ return touch1.pageY - touch2.pageY > 0 ? ionic.Gestures.DIRECTION_UP : ionic.Gestures.DIRECTION_DOWN;
+ }
+ },
+
+
+ /**
+ * calculate the distance between two touches
+ * @param {Touch} touch1
+ * @param {Touch} touch2
+ * @returns {Number} distance
+ */
+ getDistance: function getDistance(touch1, touch2) {
+ var x = touch2.pageX - touch1.pageX,
+ y = touch2.pageY - touch1.pageY;
+ return Math.sqrt((x*x) + (y*y));
+ },
+
+
+ /**
+ * calculate the scale factor between two touchLists (fingers)
+ * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
+ * @param {Array} start
+ * @param {Array} end
+ * @returns {Number} scale
+ */
+ getScale: function getScale(start, end) {
+ // need two fingers...
+ if(start.length >= 2 && end.length >= 2) {
+ return this.getDistance(end[0], end[1]) /
+ this.getDistance(start[0], start[1]);
+ }
+ return 1;
+ },
+
+
+ /**
+ * calculate the rotation degrees between two touchLists (fingers)
+ * @param {Array} start
+ * @param {Array} end
+ * @returns {Number} rotation
+ */
+ getRotation: function getRotation(start, end) {
+ // need two fingers
+ if(start.length >= 2 && end.length >= 2) {
+ return this.getAngle(end[1], end[0]) -
+ this.getAngle(start[1], start[0]);
+ }
+ return 0;
+ },
+
+
+ /**
+ * boolean if the direction is vertical
+ * @param {String} direction
+ * @returns {Boolean} is_vertical
+ */
+ isVertical: function isVertical(direction) {
+ return (direction == ionic.Gestures.DIRECTION_UP || direction == ionic.Gestures.DIRECTION_DOWN);
+ },
+
+
+ /**
+ * stop browser default behavior with css class
+ * @param {HtmlElement} element
+ * @param {Object} css_class
+ */
+ stopDefaultBrowserBehavior: function stopDefaultBrowserBehavior(element, css_class) {
+ // changed from making many style changes to just adding a preset classname
+ // less DOM manipulations, less code, and easier to control in the CSS side of things
+ // hammer.js doesn't come with CSS, but ionic does, which is why we prefer this method
+ if(element && element.classList) {
+ element.classList.add(css_class);
+ element.onselectstart = function() {
+ return false;
+ };
+ }
+ }
+ };
+
+
+ ionic.Gestures.detection = {
+ // contains all registred ionic.Gestures.gestures in the correct order
+ gestures: [],
+
+ // data of the current ionic.Gestures.gesture detection session
+ current: null,
+
+ // the previous ionic.Gestures.gesture session data
+ // is a full clone of the previous gesture.current object
+ previous: null,
+
+ // when this becomes true, no gestures are fired
+ stopped: false,
+
+
+ /**
+ * start ionic.Gestures.gesture detection
+ * @param {ionic.Gestures.Instance} inst
+ * @param {Object} eventData
+ */
+ startDetect: function startDetect(inst, eventData) {
+ // already busy with a ionic.Gestures.gesture detection on an element
+ if(this.current) {
+ return;
+ }
+
+ this.stopped = false;
+
+ this.current = {
+ inst : inst, // reference to ionic.GesturesInstance we're working for
+ startEvent : ionic.Gestures.utils.extend({}, eventData), // start eventData for distances, timing etc
+ lastEvent : false, // last eventData
+ name : '' // current gesture we're in/detected, can be 'tap', 'hold' etc
+ };
+
+ this.detect(eventData);
+ },
+
+
+ /**
+ * ionic.Gestures.gesture detection
+ * @param {Object} eventData
+ */
+ detect: function detect(eventData) {
+ if(!this.current || this.stopped) {
+ return;
+ }
+
+ // extend event data with calculations about scale, distance etc
+ eventData = this.extendEventData(eventData);
+
+ // instance options
+ var inst_options = this.current.inst.options;
+
+ // call ionic.Gestures.gesture handlers
+ for(var g=0,len=this.gestures.length; g b.index) {
+ return 1;
+ }
+ return 0;
+ });
+
+ return this.gestures;
+ }
+ };
+
+
+ ionic.Gestures.gestures = ionic.Gestures.gestures || {};
+
+ /**
+ * Custom gestures
+ * ==============================
+ *
+ * Gesture object
+ * --------------------
+ * The object structure of a gesture:
+ *
+ * { name: 'mygesture',
+ * index: 1337,
+ * defaults: {
+ * mygesture_option: true
+ * }
+ * handler: function(type, ev, inst) {
+ * // trigger gesture event
+ * inst.trigger(this.name, ev);
+ * }
+ * }
+
+ * @param {String} name
+ * this should be the name of the gesture, lowercase
+ * it is also being used to disable/enable the gesture per instance config.
+ *
+ * @param {Number} [index=1000]
+ * the index of the gesture, where it is going to be in the stack of gestures detection
+ * like when you build an gesture that depends on the drag gesture, it is a good
+ * idea to place it after the index of the drag gesture.
+ *
+ * @param {Object} [defaults={}]
+ * the default settings of the gesture. these are added to the instance settings,
+ * and can be overruled per instance. you can also add the name of the gesture,
+ * but this is also added by default (and set to true).
+ *
+ * @param {Function} handler
+ * this handles the gesture detection of your custom gesture and receives the
+ * following arguments:
+ *
+ * @param {Object} eventData
+ * event data containing the following properties:
+ * timeStamp {Number} time the event occurred
+ * target {HTMLElement} target element
+ * touches {Array} touches (fingers, pointers, mouse) on the screen
+ * pointerType {String} kind of pointer that was used. matches ionic.Gestures.POINTER_MOUSE|TOUCH
+ * center {Object} center position of the touches. contains pageX and pageY
+ * deltaTime {Number} the total time of the touches in the screen
+ * deltaX {Number} the delta on x axis we haved moved
+ * deltaY {Number} the delta on y axis we haved moved
+ * velocityX {Number} the velocity on the x
+ * velocityY {Number} the velocity on y
+ * angle {Number} the angle we are moving
+ * direction {String} the direction we are moving. matches ionic.Gestures.DIRECTION_UP|DOWN|LEFT|RIGHT
+ * distance {Number} the distance we haved moved
+ * scale {Number} scaling of the touches, needs 2 touches
+ * rotation {Number} rotation of the touches, needs 2 touches *
+ * eventType {String} matches ionic.Gestures.EVENT_START|MOVE|END
+ * srcEvent {Object} the source event, like TouchStart or MouseDown *
+ * startEvent {Object} contains the same properties as above,
+ * but from the first touch. this is used to calculate
+ * distances, deltaTime, scaling etc
+ *
+ * @param {ionic.Gestures.Instance} inst
+ * the instance we are doing the detection for. you can get the options from
+ * the inst.options object and trigger the gesture event by calling inst.trigger
+ *
+ *
+ * Handle gestures
+ * --------------------
+ * inside the handler you can get/set ionic.Gestures.detectionic.current. This is the current
+ * detection sessionic. It has the following properties
+ * @param {String} name
+ * contains the name of the gesture we have detected. it has not a real function,
+ * only to check in other gestures if something is detected.
+ * like in the drag gesture we set it to 'drag' and in the swipe gesture we can
+ * check if the current gesture is 'drag' by accessing ionic.Gestures.detectionic.current.name
+ *
+ * readonly
+ * @param {ionic.Gestures.Instance} inst
+ * the instance we do the detection for
+ *
+ * readonly
+ * @param {Object} startEvent
+ * contains the properties of the first gesture detection in this sessionic.
+ * Used for calculations about timing, distance, etc.
+ *
+ * readonly
+ * @param {Object} lastEvent
+ * contains all the properties of the last gesture detect in this sessionic.
+ *
+ * after the gesture detection session has been completed (user has released the screen)
+ * the ionic.Gestures.detectionic.current object is copied into ionic.Gestures.detectionic.previous,
+ * this is usefull for gestures like doubletap, where you need to know if the
+ * previous gesture was a tap
+ *
+ * options that have been set by the instance can be received by calling inst.options
+ *
+ * You can trigger a gesture event by calling inst.trigger("mygesture", event).
+ * The first param is the name of your gesture, the second the event argument
+ *
+ *
+ * Register gestures
+ * --------------------
+ * When an gesture is added to the ionic.Gestures.gestures object, it is auto registered
+ * at the setup of the first ionic.Gestures instance. You can also call ionic.Gestures.detectionic.register
+ * manually and pass your gesture object as a param
+ *
+ */
+
+ /**
+ * Hold
+ * Touch stays at the same place for x time
+ * events hold
+ */
+ ionic.Gestures.gestures.Hold = {
+ name: 'hold',
+ index: 10,
+ defaults: {
+ hold_timeout : 500,
+ hold_threshold : 1
+ },
+ timer: null,
+ handler: function holdGesture(ev, inst) {
+ switch(ev.eventType) {
+ case ionic.Gestures.EVENT_START:
+ // clear any running timers
+ clearTimeout(this.timer);
+
+ // set the gesture so we can check in the timeout if it still is
+ ionic.Gestures.detection.current.name = this.name;
+
+ // set timer and if after the timeout it still is hold,
+ // we trigger the hold event
+ this.timer = setTimeout(function() {
+ if(ionic.Gestures.detection.current.name == 'hold') {
+ ionic.tap.cancelClick();
+ inst.trigger('hold', ev);
+ }
+ }, inst.options.hold_timeout);
+ break;
+
+ // when you move or end we clear the timer
+ case ionic.Gestures.EVENT_MOVE:
+ if(ev.distance > inst.options.hold_threshold) {
+ clearTimeout(this.timer);
+ }
+ break;
+
+ case ionic.Gestures.EVENT_END:
+ clearTimeout(this.timer);
+ break;
+ }
+ }
+ };
+
+
+ /**
+ * Tap/DoubleTap
+ * Quick touch at a place or double at the same place
+ * events tap, doubletap
+ */
+ ionic.Gestures.gestures.Tap = {
+ name: 'tap',
+ index: 100,
+ defaults: {
+ tap_max_touchtime : 250,
+ tap_max_distance : 10,
+ tap_always : true,
+ doubletap_distance : 20,
+ doubletap_interval : 300
+ },
+ handler: function tapGesture(ev, inst) {
+ if(ev.eventType == ionic.Gestures.EVENT_END && ev.srcEvent.type != 'touchcancel') {
+ // previous gesture, for the double tap since these are two different gesture detections
+ var prev = ionic.Gestures.detection.previous,
+ did_doubletap = false;
+
+ // when the touchtime is higher then the max touch time
+ // or when the moving distance is too much
+ if(ev.deltaTime > inst.options.tap_max_touchtime ||
+ ev.distance > inst.options.tap_max_distance) {
+ return;
+ }
+
+ // check if double tap
+ if(prev && prev.name == 'tap' &&
+ (ev.timeStamp - prev.lastEvent.timeStamp) < inst.options.doubletap_interval &&
+ ev.distance < inst.options.doubletap_distance) {
+ inst.trigger('doubletap', ev);
+ did_doubletap = true;
+ }
+
+ // do a single tap
+ if(!did_doubletap || inst.options.tap_always) {
+ ionic.Gestures.detection.current.name = 'tap';
+ inst.trigger('tap', ev);
+ }
+ }
+ }
+ };
+
+
+ /**
+ * Swipe
+ * triggers swipe events when the end velocity is above the threshold
+ * events swipe, swipeleft, swiperight, swipeup, swipedown
+ */
+ ionic.Gestures.gestures.Swipe = {
+ name: 'swipe',
+ index: 40,
+ defaults: {
+ // set 0 for unlimited, but this can conflict with transform
+ swipe_max_touches : 1,
+ swipe_velocity : 0.7
+ },
+ handler: function swipeGesture(ev, inst) {
+ if(ev.eventType == ionic.Gestures.EVENT_END) {
+ // max touches
+ if(inst.options.swipe_max_touches > 0 &&
+ ev.touches.length > inst.options.swipe_max_touches) {
+ return;
+ }
+
+ // when the distance we moved is too small we skip this gesture
+ // or we can be already in dragging
+ if(ev.velocityX > inst.options.swipe_velocity ||
+ ev.velocityY > inst.options.swipe_velocity) {
+ // trigger swipe events
+ inst.trigger(this.name, ev);
+ inst.trigger(this.name + ev.direction, ev);
+ }
+ }
+ }
+ };
+
+
+ /**
+ * Drag
+ * Move with x fingers (default 1) around on the page. Blocking the scrolling when
+ * moving left and right is a good practice. When all the drag events are blocking
+ * you disable scrolling on that area.
+ * events drag, drapleft, dragright, dragup, dragdown
+ */
+ ionic.Gestures.gestures.Drag = {
+ name: 'drag',
+ index: 50,
+ defaults: {
+ drag_min_distance : 10,
+ // Set correct_for_drag_min_distance to true to make the starting point of the drag
+ // be calculated from where the drag was triggered, not from where the touch started.
+ // Useful to avoid a jerk-starting drag, which can make fine-adjustments
+ // through dragging difficult, and be visually unappealing.
+ correct_for_drag_min_distance : true,
+ // set 0 for unlimited, but this can conflict with transform
+ drag_max_touches : 1,
+ // prevent default browser behavior when dragging occurs
+ // be careful with it, it makes the element a blocking element
+ // when you are using the drag gesture, it is a good practice to set this true
+ drag_block_horizontal : true,
+ drag_block_vertical : true,
+ // drag_lock_to_axis keeps the drag gesture on the axis that it started on,
+ // It disallows vertical directions if the initial direction was horizontal, and vice versa.
+ drag_lock_to_axis : false,
+ // drag lock only kicks in when distance > drag_lock_min_distance
+ // This way, locking occurs only when the distance has become large enough to reliably determine the direction
+ drag_lock_min_distance : 25
+ },
+ triggered: false,
+ handler: function dragGesture(ev, inst) {
+ // current gesture isnt drag, but dragged is true
+ // this means an other gesture is busy. now call dragend
+ if(ionic.Gestures.detection.current.name != this.name && this.triggered) {
+ inst.trigger(this.name +'end', ev);
+ this.triggered = false;
+ return;
+ }
+
+ // max touches
+ if(inst.options.drag_max_touches > 0 &&
+ ev.touches.length > inst.options.drag_max_touches) {
+ return;
+ }
+
+ switch(ev.eventType) {
+ case ionic.Gestures.EVENT_START:
+ this.triggered = false;
+ break;
+
+ case ionic.Gestures.EVENT_MOVE:
+ // when the distance we moved is too small we skip this gesture
+ // or we can be already in dragging
+ if(ev.distance < inst.options.drag_min_distance &&
+ ionic.Gestures.detection.current.name != this.name) {
+ return;
+ }
+
+ // we are dragging!
+ if(ionic.Gestures.detection.current.name != this.name) {
+ ionic.Gestures.detection.current.name = this.name;
+ if (inst.options.correct_for_drag_min_distance) {
+ // When a drag is triggered, set the event center to drag_min_distance pixels from the original event center.
+ // Without this correction, the dragged distance would jumpstart at drag_min_distance pixels instead of at 0.
+ // It might be useful to save the original start point somewhere
+ var factor = Math.abs(inst.options.drag_min_distance/ev.distance);
+ ionic.Gestures.detection.current.startEvent.center.pageX += ev.deltaX * factor;
+ ionic.Gestures.detection.current.startEvent.center.pageY += ev.deltaY * factor;
+
+ // recalculate event data using new start point
+ ev = ionic.Gestures.detection.extendEventData(ev);
+ }
+ }
+
+ // lock drag to axis?
+ if(ionic.Gestures.detection.current.lastEvent.drag_locked_to_axis || (inst.options.drag_lock_to_axis && inst.options.drag_lock_min_distance<=ev.distance)) {
+ ev.drag_locked_to_axis = true;
+ }
+ var last_direction = ionic.Gestures.detection.current.lastEvent.direction;
+ if(ev.drag_locked_to_axis && last_direction !== ev.direction) {
+ // keep direction on the axis that the drag gesture started on
+ if(ionic.Gestures.utils.isVertical(last_direction)) {
+ ev.direction = (ev.deltaY < 0) ? ionic.Gestures.DIRECTION_UP : ionic.Gestures.DIRECTION_DOWN;
+ }
+ else {
+ ev.direction = (ev.deltaX < 0) ? ionic.Gestures.DIRECTION_LEFT : ionic.Gestures.DIRECTION_RIGHT;
+ }
+ }
+
+ // first time, trigger dragstart event
+ if(!this.triggered) {
+ inst.trigger(this.name +'start', ev);
+ this.triggered = true;
+ }
+
+ // trigger normal event
+ inst.trigger(this.name, ev);
+
+ // direction event, like dragdown
+ inst.trigger(this.name + ev.direction, ev);
+
+ // block the browser events
+ if( (inst.options.drag_block_vertical && ionic.Gestures.utils.isVertical(ev.direction)) ||
+ (inst.options.drag_block_horizontal && !ionic.Gestures.utils.isVertical(ev.direction))) {
+ ev.preventDefault();
+ }
+ break;
+
+ case ionic.Gestures.EVENT_END:
+ // trigger dragend
+ if(this.triggered) {
+ inst.trigger(this.name +'end', ev);
+ }
+
+ this.triggered = false;
+ break;
+ }
+ }
+ };
+
+
+ /**
+ * Transform
+ * User want to scale or rotate with 2 fingers
+ * events transform, pinch, pinchin, pinchout, rotate
+ */
+ ionic.Gestures.gestures.Transform = {
+ name: 'transform',
+ index: 45,
+ defaults: {
+ // factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1
+ transform_min_scale : 0.01,
+ // rotation in degrees
+ transform_min_rotation : 1,
+ // prevent default browser behavior when two touches are on the screen
+ // but it makes the element a blocking element
+ // when you are using the transform gesture, it is a good practice to set this true
+ transform_always_block : false
+ },
+ triggered: false,
+ handler: function transformGesture(ev, inst) {
+ // current gesture isnt drag, but dragged is true
+ // this means an other gesture is busy. now call dragend
+ if(ionic.Gestures.detection.current.name != this.name && this.triggered) {
+ inst.trigger(this.name +'end', ev);
+ this.triggered = false;
+ return;
+ }
+
+ // atleast multitouch
+ if(ev.touches.length < 2) {
+ return;
+ }
+
+ // prevent default when two fingers are on the screen
+ if(inst.options.transform_always_block) {
+ ev.preventDefault();
+ }
+
+ switch(ev.eventType) {
+ case ionic.Gestures.EVENT_START:
+ this.triggered = false;
+ break;
+
+ case ionic.Gestures.EVENT_MOVE:
+ var scale_threshold = Math.abs(1-ev.scale);
+ var rotation_threshold = Math.abs(ev.rotation);
+
+ // when the distance we moved is too small we skip this gesture
+ // or we can be already in dragging
+ if(scale_threshold < inst.options.transform_min_scale &&
+ rotation_threshold < inst.options.transform_min_rotation) {
+ return;
+ }
+
+ // we are transforming!
+ ionic.Gestures.detection.current.name = this.name;
+
+ // first time, trigger dragstart event
+ if(!this.triggered) {
+ inst.trigger(this.name +'start', ev);
+ this.triggered = true;
+ }
+
+ inst.trigger(this.name, ev); // basic transform event
+
+ // trigger rotate event
+ if(rotation_threshold > inst.options.transform_min_rotation) {
+ inst.trigger('rotate', ev);
+ }
+
+ // trigger pinch event
+ if(scale_threshold > inst.options.transform_min_scale) {
+ inst.trigger('pinch', ev);
+ inst.trigger('pinch'+ ((ev.scale < 1) ? 'in' : 'out'), ev);
+ }
+ break;
+
+ case ionic.Gestures.EVENT_END:
+ // trigger dragend
+ if(this.triggered) {
+ inst.trigger(this.name +'end', ev);
+ }
+
+ this.triggered = false;
+ break;
+ }
+ }
+ };
+
+
+ /**
+ * Touch
+ * Called as first, tells the user has touched the screen
+ * events touch
+ */
+ ionic.Gestures.gestures.Touch = {
+ name: 'touch',
+ index: -Infinity,
+ defaults: {
+ // call preventDefault at touchstart, and makes the element blocking by
+ // disabling the scrolling of the page, but it improves gestures like
+ // transforming and dragging.
+ // be careful with using this, it can be very annoying for users to be stuck
+ // on the page
+ prevent_default: false,
+
+ // disable mouse events, so only touch (or pen!) input triggers events
+ prevent_mouseevents: false
+ },
+ handler: function touchGesture(ev, inst) {
+ if(inst.options.prevent_mouseevents && ev.pointerType == ionic.Gestures.POINTER_MOUSE) {
+ ev.stopDetect();
+ return;
+ }
+
+ if(inst.options.prevent_default) {
+ ev.preventDefault();
+ }
+
+ if(ev.eventType == ionic.Gestures.EVENT_START) {
+ inst.trigger(this.name, ev);
+ }
+ }
+ };
+
+
+ /**
+ * Release
+ * Called as last, tells the user has released the screen
+ * events release
+ */
+ ionic.Gestures.gestures.Release = {
+ name: 'release',
+ index: Infinity,
+ handler: function releaseGesture(ev, inst) {
+ if(ev.eventType == ionic.Gestures.EVENT_END) {
+ inst.trigger(this.name, ev);
+ }
+ }
+ };
+})(window.ionic);
+
+(function(window, document, ionic) {
+
+ var IOS = 'ios';
+ var ANDROID = 'android';
+ var WINDOWS_PHONE = 'windowsphone';
+
+ /**
+ * @ngdoc utility
+ * @name ionic.Platform
+ * @module ionic
+ */
+ ionic.Platform = {
+
+ // Put navigator on platform so it can be mocked and set
+ // the browser does not allow window.navigator to be set
+ navigator: window.navigator,
+
+ /**
+ * @ngdoc property
+ * @name ionic.Platform#isReady
+ * @returns {boolean} Whether the device is ready.
+ */
+ isReady: false,
+ /**
+ * @ngdoc property
+ * @name ionic.Platform#isFullScreen
+ * @returns {boolean} Whether the device is fullscreen.
+ */
+ isFullScreen: false,
+ /**
+ * @ngdoc property
+ * @name ionic.Platform#platforms
+ * @returns {Array(string)} An array of all platforms found.
+ */
+ platforms: null,
+ /**
+ * @ngdoc property
+ * @name ionic.Platform#grade
+ * @returns {string} What grade the current platform is.
+ */
+ grade: null,
+ ua: navigator.userAgent,
+
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#ready
+ * @description
+ * Trigger a callback once the device is ready, or immediately
+ * if the device is already ready. This method can be run from
+ * anywhere and does not need to be wrapped by any additonal methods.
+ * When the app is within a WebView (Cordova), it'll fire
+ * the callback once the device is ready. If the app is within
+ * a web browser, it'll fire the callback after `window.load`.
+ * Please remember that Cordova features (Camera, FileSystem, etc) still
+ * will not work in a web browser.
+ * @param {function} callback The function to call.
+ */
+ ready: function(cb) {
+ // run through tasks to complete now that the device is ready
+ if(this.isReady) {
+ cb();
+ } else {
+ // the platform isn't ready yet, add it to this array
+ // which will be called once the platform is ready
+ readyCallbacks.push(cb);
+ }
+ },
+
+ /**
+ * @private
+ */
+ detect: function() {
+ ionic.Platform._checkPlatforms();
+
+ ionic.requestAnimationFrame(function(){
+ // only add to the body class if we got platform info
+ for(var i = 0; i < ionic.Platform.platforms.length; i++) {
+ document.body.classList.add('platform-' + ionic.Platform.platforms[i]);
+ }
+ });
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#setGrade
+ * @description Set the grade of the device: 'a', 'b', or 'c'. 'a' is the best
+ * (most css features enabled), 'c' is the worst. By default, sets the grade
+ * depending on the current device.
+ * @param {string} grade The new grade to set.
+ */
+ setGrade: function(grade) {
+ var oldGrade = this.grade;
+ this.grade = grade;
+ ionic.requestAnimationFrame(function() {
+ if (oldGrade) {
+ document.body.classList.remove('grade-' + oldGrade);
+ }
+ document.body.classList.add('grade-' + grade);
+ });
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#device
+ * @description Return the current device (given by cordova).
+ * @returns {object} The device object.
+ */
+ device: function() {
+ return window.device || {};
+ },
+
+ _checkPlatforms: function(platforms) {
+ this.platforms = [];
+ var grade = 'a';
+
+ if(this.isWebView()) {
+ this.platforms.push('webview');
+ this.platforms.push('cordova');
+ } else {
+ this.platforms.push('browser');
+ }
+ if(this.isIPad()) this.platforms.push('ipad');
+
+ var platform = this.platform();
+ if(platform) {
+ this.platforms.push(platform);
+
+ var version = this.version();
+ if(version) {
+ var v = version.toString();
+ if(v.indexOf('.') > 0) {
+ v = v.replace('.', '_');
+ } else {
+ v += '_0';
+ }
+ this.platforms.push(platform + v.split('_')[0]);
+ this.platforms.push(platform + v);
+
+ if(this.isAndroid() && version < 4.4) {
+ grade = (version < 4 ? 'c' : 'b');
+ } else if(this.isWindowsPhone()) {
+ grade = 'b';
+ }
+ }
+ }
+
+ this.setGrade(grade);
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#isWebView
+ * @returns {boolean} Check if we are running within a WebView (such as Cordova).
+ */
+ isWebView: function() {
+ return !(!window.cordova && !window.PhoneGap && !window.phonegap);
+ },
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#isIPad
+ * @returns {boolean} Whether we are running on iPad.
+ */
+ isIPad: function() {
+ if( /iPad/i.test(ionic.Platform.navigator.platform) ) {
+ return true;
+ }
+ return /iPad/i.test(this.ua);
+ },
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#isIOS
+ * @returns {boolean} Whether we are running on iOS.
+ */
+ isIOS: function() {
+ return this.is(IOS);
+ },
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#isAndroid
+ * @returns {boolean} Whether we are running on Android.
+ */
+ isAndroid: function() {
+ return this.is(ANDROID);
+ },
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#isWindowsPhone
+ * @returns {boolean} Whether we are running on Windows Phone.
+ */
+ isWindowsPhone: function() {
+ return this.is(WINDOWS_PHONE);
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#platform
+ * @returns {string} The name of the current platform.
+ */
+ platform: function() {
+ // singleton to get the platform name
+ if(platformName === null) this.setPlatform(this.device().platform);
+ return platformName;
+ },
+
+ /**
+ * @private
+ */
+ setPlatform: function(n) {
+ if(typeof n != 'undefined' && n !== null && n.length) {
+ platformName = n.toLowerCase();
+ } else if(this.ua.indexOf('Android') > 0) {
+ platformName = ANDROID;
+ } else if(this.ua.indexOf('iPhone') > -1 || this.ua.indexOf('iPad') > -1 || this.ua.indexOf('iPod') > -1) {
+ platformName = IOS;
+ } else if(this.ua.indexOf('Windows Phone') > -1) {
+ platformName = WINDOWS_PHONE;
+ } else {
+ platformName = ionic.Platform.navigator.platform && navigator.platform.toLowerCase().split(' ')[0] || '';
+ }
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#version
+ * @returns {string} The version of the current device platform.
+ */
+ version: function() {
+ // singleton to get the platform version
+ if(platformVersion === null) this.setVersion(this.device().version);
+ return platformVersion;
+ },
+
+ /**
+ * @private
+ */
+ setVersion: function(v) {
+ if(typeof v != 'undefined' && v !== null) {
+ v = v.split('.');
+ v = parseFloat(v[0] + '.' + (v.length > 1 ? v[1] : 0));
+ if(!isNaN(v)) {
+ platformVersion = v;
+ return;
+ }
+ }
+
+ platformVersion = 0;
+
+ // fallback to user-agent checking
+ var pName = this.platform();
+ var versionMatch = {
+ 'android': /Android (\d+).(\d+)?/,
+ 'ios': /OS (\d+)_(\d+)?/,
+ 'windowsphone': /Windows Phone (\d+).(\d+)?/
+ };
+ if(versionMatch[pName]) {
+ v = this.ua.match( versionMatch[pName] );
+ if(v && v.length > 2) {
+ platformVersion = parseFloat( v[1] + '.' + v[2] );
+ }
+ }
+ },
+
+ // Check if the platform is the one detected by cordova
+ is: function(type) {
+ type = type.toLowerCase();
+ // check if it has an array of platforms
+ if(this.platforms) {
+ for(var x = 0; x < this.platforms.length; x++) {
+ if(this.platforms[x] === type) return true;
+ }
+ }
+ // exact match
+ var pName = this.platform();
+ if(pName) {
+ return pName === type.toLowerCase();
+ }
+
+ // A quick hack for to check userAgent
+ return this.ua.toLowerCase().indexOf(type) >= 0;
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#exitApp
+ * @description Exit the app.
+ */
+ exitApp: function() {
+ this.ready(function(){
+ navigator.app && navigator.app.exitApp && navigator.app.exitApp();
+ });
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#showStatusBar
+ * @description Shows or hides the device status bar (in Cordova).
+ * @param {boolean} shouldShow Whether or not to show the status bar.
+ */
+ showStatusBar: function(val) {
+ // Only useful when run within cordova
+ this._showStatusBar = val;
+ this.ready(function(){
+ // run this only when or if the platform (cordova) is ready
+ ionic.requestAnimationFrame(function(){
+ if(ionic.Platform._showStatusBar) {
+ // they do not want it to be full screen
+ window.StatusBar && window.StatusBar.show();
+ document.body.classList.remove('status-bar-hide');
+ } else {
+ // it should be full screen
+ window.StatusBar && window.StatusBar.hide();
+ document.body.classList.add('status-bar-hide');
+ }
+ });
+ });
+ },
+
+ /**
+ * @ngdoc method
+ * @name ionic.Platform#fullScreen
+ * @description
+ * Sets whether the app is fullscreen or not (in Cordova).
+ * @param {boolean=} showFullScreen Whether or not to set the app to fullscreen. Defaults to true.
+ * @param {boolean=} showStatusBar Whether or not to show the device's status bar. Defaults to false.
+ */
+ fullScreen: function(showFullScreen, showStatusBar) {
+ // showFullScreen: default is true if no param provided
+ this.isFullScreen = (showFullScreen !== false);
+
+ // add/remove the fullscreen classname to the body
+ ionic.DomUtil.ready(function(){
+ // run this only when or if the DOM is ready
+ ionic.requestAnimationFrame(function(){
+ // fixing pane height before we adjust this
+ panes = document.getElementsByClassName('pane');
+ for(var i = 0;i
+ *
+ *
+ * ```
+ *
+ * ### Additional Notes:
+ *
+ * - Ionic tap works with Ionic's JavaScript scrolling
+ * - Elements can come and go from the DOM and Ionic tap doesn't keep adding and removing
+ * listeners
+ * - No "tap delay" after the first "tap" (you can tap as fast as you want, they all click)
+ * - Minimal events listeners, only being added to document
+ * - Correct focus in/out on each input type (select, textearea, range) on each platform/device
+ * - Shows and hides virtual keyboard correctly for each platform/device
+ * - Works with labels surrounding inputs
+ * - Does not fire off a click if the user moves the pointer too far
+ * - Adds and removes an 'activated' css class
+ * - Multiple [unit tests](https://github.com/driftyco/ionic/blob/master/test/unit/utils/tap.unit.js) for each scenario
+ *
+ */
+/*
+
+ IONIC TAP
+ ---------------
+ - Both touch and mouse events are added to the document.body on DOM ready
+ - If a touch event happens, it does not use mouse event listeners
+ - On touchend, if the distance between start and end was small, trigger a click
+ - In the triggered click event, add a 'isIonicTap' property
+ - The triggered click receives the same x,y coordinates as as the end event
+ - On document.body click listener (with useCapture=true), only allow clicks with 'isIonicTap'
+ - Triggering clicks with mouse events work the same as touch, except with mousedown/mouseup
+ - Tapping inputs is disabled during scrolling
+*/
+
+var tapDoc; // the element which the listeners are on (document.body)
+var tapActiveEle; // the element which is active (probably has focus)
+var tapEnabledTouchEvents;
+var tapMouseResetTimer;
+var tapPointerMoved;
+var tapPointerStart;
+var tapTouchFocusedInput;
+var tapLastTouchTarget;
+var tapTouchMoveListener = 'touchmove';
+
+// how much the coordinates can be off between start/end, but still a click
+var TAP_RELEASE_TOLERANCE = 6; // default tolerance
+var TAP_RELEASE_BUTTON_TOLERANCE = 50; // button elements should have a larger tolerance
+
+var tapEventListeners = {
+ 'click': tapClickGateKeeper,
+
+ 'mousedown': tapMouseDown,
+ 'mouseup': tapMouseUp,
+ 'mousemove': tapMouseMove,
+
+ 'touchstart': tapTouchStart,
+ 'touchend': tapTouchEnd,
+ 'touchcancel': tapTouchCancel,
+ 'touchmove': tapTouchMove,
+
+ 'pointerdown': tapTouchStart,
+ 'pointerup': tapTouchEnd,
+ 'pointercancel': tapTouchCancel,
+ 'pointermove': tapTouchMove,
+
+ 'MSPointerDown': tapTouchStart,
+ 'MSPointerUp': tapTouchEnd,
+ 'MSPointerCancel': tapTouchCancel,
+ 'MSPointerMove': tapTouchMove,
+
+ 'focusin': tapFocusIn,
+ 'focusout': tapFocusOut
+};
+
+ionic.tap = {
+
+ register: function(ele) {
+ tapDoc = ele;
+
+ tapEventListener('click', true, true);
+ tapEventListener('mouseup');
+ tapEventListener('mousedown');
+
+ if( window.navigator.pointerEnabled ) {
+ tapEventListener('pointerdown');
+ tapEventListener('pointerup');
+ tapEventListener('pointcancel');
+ tapTouchMoveListener = 'pointermove';
+
+ } else if (window.navigator.msPointerEnabled) {
+ tapEventListener('MSPointerDown');
+ tapEventListener('MSPointerUp');
+ tapEventListener('MSPointerCancel');
+ tapTouchMoveListener = 'MSPointerMove';
+
+ } else {
+ tapEventListener('touchstart');
+ tapEventListener('touchend');
+ tapEventListener('touchcancel');
+ }
+
+ tapEventListener('focusin');
+ tapEventListener('focusout');
+
+ return function() {
+ for(var type in tapEventListeners) {
+ tapEventListener(type, false);
+ }
+ tapDoc = null;
+ tapActiveEle = null;
+ tapEnabledTouchEvents = false;
+ tapPointerMoved = false;
+ tapPointerStart = null;
+ };
+ },
+
+ ignoreScrollStart: function(e) {
+ return (e.defaultPrevented) || // defaultPrevented has been assigned by another component handling the event
+ (/^(file|range)$/i).test(e.target.type) ||
+ (e.target.dataset ? e.target.dataset.preventScroll : e.target.getAttribute('data-prevent-scroll')) == 'true' || // manually set within an elements attributes
+ (!!(/^(object|embed)$/i).test(e.target.tagName)) || // flash/movie/object touches should not try to scroll
+ ionic.tap.isElementTapDisabled(e.target); // check if this element, or an ancestor, has `data-tap-disabled` attribute
+ },
+
+ isTextInput: function(ele) {
+ return !!ele &&
+ (ele.tagName == 'TEXTAREA' ||
+ ele.contentEditable === 'true' ||
+ (ele.tagName == 'INPUT' && !(/^(radio|checkbox|range|file|submit|reset)$/i).test(ele.type)) );
+ },
+
+ isDateInput: function(ele) {
+ return !!ele &&
+ (ele.tagName == 'INPUT' && (/^(date|time|datetime-local|month|week)$/i).test(ele.type));
+ },
+
+ isLabelWithTextInput: function(ele) {
+ var container = tapContainingElement(ele, false);
+
+ return !!container &&
+ ionic.tap.isTextInput( tapTargetElement( container ) );
+ },
+
+ containsOrIsTextInput: function(ele) {
+ return ionic.tap.isTextInput(ele) || ionic.tap.isLabelWithTextInput(ele);
+ },
+
+ cloneFocusedInput: function(container, scrollIntance) {
+ if(ionic.tap.hasCheckedClone) return;
+ ionic.tap.hasCheckedClone = true;
+
+ ionic.requestAnimationFrame(function(){
+ var focusInput = container.querySelector(':focus');
+ if( ionic.tap.isTextInput(focusInput) ) {
+ var clonedInput = focusInput.parentElement.querySelector('.cloned-text-input');
+ if(!clonedInput) {
+ clonedInput = document.createElement(focusInput.tagName);
+ clonedInput.placeholder = focusInput.placeholder;
+ clonedInput.type = focusInput.type;
+ clonedInput.value = focusInput.value;
+ clonedInput.style = focusInput.style;
+ clonedInput.className = focusInput.className;
+ clonedInput.classList.add('cloned-text-input');
+ clonedInput.readOnly = true;
+ if (focusInput.isContentEditable) {
+ clonedInput.contentEditable = focusInput.contentEditable;
+ clonedInput.innerHTML = focusInput.innerHTML;
+ }
+ focusInput.parentElement.insertBefore(clonedInput, focusInput);
+ focusInput.style.top = focusInput.offsetTop;
+ focusInput.classList.add('previous-input-focus');
+ }
+ }
+ });
+ },
+
+ hasCheckedClone: false,
+
+ removeClonedInputs: function(container, scrollIntance) {
+ ionic.tap.hasCheckedClone = false;
+
+ ionic.requestAnimationFrame(function(){
+ var clonedInputs = container.querySelectorAll('.cloned-text-input');
+ var previousInputFocus = container.querySelectorAll('.previous-input-focus');
+ var x;
+
+ for(x=0; x releaseTolerance ||
+ Math.abs(tapPointerStart.y - endCoordinates.y) > releaseTolerance;
+}
+
+function tapContainingElement(ele, allowSelf) {
+ var climbEle = ele;
+ for(var x=0; x<6; x++) {
+ if(!climbEle) break;
+ if(climbEle.tagName === 'LABEL') return climbEle;
+ climbEle = climbEle.parentElement;
+ }
+ if(allowSelf !== false) return ele;
+}
+
+function tapTargetElement(ele) {
+ if(ele && ele.tagName === 'LABEL') {
+ if(ele.control) return ele.control;
+
+ // older devices do not support the "control" property
+ if(ele.querySelector) {
+ var control = ele.querySelector('input,textarea,select');
+ if(control) return control;
+ }
+ }
+ return ele;
+}
+
+ionic.DomUtil.ready(function(){
+ var ng = typeof angular !== 'undefined' ? angular : null;
+ //do nothing for e2e tests
+ if (!ng || (ng && !ng.scenario)) {
+ ionic.tap.register(document);
+ }
+});
+
+(function(document, ionic) {
+ 'use strict';
+
+ var queueElements = {}; // elements that should get an active state in XX milliseconds
+ var activeElements = {}; // elements that are currently active
+ var keyId = 0; // a counter for unique keys for the above ojects
+ var ACTIVATED_CLASS = 'activated';
+
+ ionic.activator = {
+
+ start: function(e) {
+ var self = this;
+
+ // when an element is touched/clicked, it climbs up a few
+ // parents to see if it is an .item or .button element
+ ionic.requestAnimationFrame(function(){
+ if ( ionic.tap.requiresNativeClick(e.target) ) return;
+ var ele = e.target;
+ var eleToActivate;
+
+ for(var x=0; x<6; x++) {
+ if(!ele || ele.nodeType !== 1) break;
+ if(eleToActivate && ele.classList.contains('item')) {
+ eleToActivate = ele;
+ break;
+ }
+ if( ele.tagName == 'A' || ele.tagName == 'BUTTON' || ele.hasAttribute('ng-click') ) {
+ eleToActivate = ele;
+ break;
+ }
+ if( ele.classList.contains('button') ) {
+ eleToActivate = ele;
+ break;
+ }
+ // no sense climbing past these
+ if(ele.classList.contains('pane') || ele.tagName == 'BODY' || ele.tagName == 'ION-CONTENT'){
+ break;
+ }
+ ele = ele.parentElement;
+ }
+
+ if(eleToActivate) {
+ // queue that this element should be set to active
+ queueElements[keyId] = eleToActivate;
+
+ // in XX milliseconds, set the queued elements to active
+ if(e.type === 'touchstart') {
+ self._activateTimeout = setTimeout(activateElements, 80);
+ } else {
+ ionic.requestAnimationFrame(activateElements);
+ }
+
+ keyId = (keyId > 19 ? 0 : keyId + 1);
+ }
+
+ });
+ },
+
+ end: function() {
+ // clear out any active/queued elements after XX milliseconds
+ clearTimeout(this._activateTimeout);
+ setTimeout(clear, 200);
+ }
+
+ };
+
+ function clear() {
+ // clear out any elements that are queued to be set to active
+ queueElements = {};
+
+ // in the next frame, remove the active class from all active elements
+ ionic.requestAnimationFrame(deactivateElements);
+ }
+
+ function activateElements() {
+ // activate all elements in the queue
+ for(var key in queueElements) {
+ if(queueElements[key]) {
+ queueElements[key].classList.add(ACTIVATED_CLASS);
+ activeElements[key] = queueElements[key];
+ }
+ }
+ queueElements = {};
+ }
+
+ function deactivateElements() {
+ for(var key in activeElements) {
+ if(activeElements[key]) {
+ activeElements[key].classList.remove(ACTIVATED_CLASS);
+ delete activeElements[key];
+ }
+ }
+ }
+
+})(document, ionic);
+
+(function(ionic) {
+
+ /* for nextUid() function below */
+ var uid = ['0','0','0'];
+
+ /**
+ * Various utilities used throughout Ionic
+ *
+ * Some of these are adopted from underscore.js and backbone.js, both also MIT licensed.
+ */
+ ionic.Utils = {
+
+ arrayMove: function (arr, old_index, new_index) {
+ if (new_index >= arr.length) {
+ var k = new_index - arr.length;
+ while ((k--) + 1) {
+ arr.push(undefined);
+ }
+ }
+ arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
+ return arr;
+ },
+
+ /**
+ * Return a function that will be called with the given context
+ */
+ proxy: function(func, context) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ return function() {
+ return func.apply(context, args.concat(Array.prototype.slice.call(arguments)));
+ };
+ },
+
+ /**
+ * Only call a function once in the given interval.
+ *
+ * @param func {Function} the function to call
+ * @param wait {int} how long to wait before/after to allow function calls
+ * @param immediate {boolean} whether to call immediately or after the wait interval
+ */
+ debounce: function(func, wait, immediate) {
+ var timeout, args, context, timestamp, result;
+ return function() {
+ context = this;
+ args = arguments;
+ timestamp = new Date();
+ var later = function() {
+ var last = (new Date()) - timestamp;
+ if (last < wait) {
+ timeout = setTimeout(later, wait - last);
+ } else {
+ timeout = null;
+ if (!immediate) result = func.apply(context, args);
+ }
+ };
+ var callNow = immediate && !timeout;
+ if (!timeout) {
+ timeout = setTimeout(later, wait);
+ }
+ if (callNow) result = func.apply(context, args);
+ return result;
+ };
+ },
+
+ /**
+ * Throttle the given fun, only allowing it to be
+ * called at most every `wait` ms.
+ */
+ throttle: function(func, wait, options) {
+ var context, args, result;
+ var timeout = null;
+ var previous = 0;
+ options || (options = {});
+ var later = function() {
+ previous = options.leading === false ? 0 : Date.now();
+ timeout = null;
+ result = func.apply(context, args);
+ };
+ return function() {
+ var now = Date.now();
+ if (!previous && options.leading === false) previous = now;
+ var remaining = wait - (now - previous);
+ context = this;
+ args = arguments;
+ if (remaining <= 0) {
+ clearTimeout(timeout);
+ timeout = null;
+ previous = now;
+ result = func.apply(context, args);
+ } else if (!timeout && options.trailing !== false) {
+ timeout = setTimeout(later, remaining);
+ }
+ return result;
+ };
+ },
+ // Borrowed from Backbone.js's extend
+ // Helper function to correctly set up the prototype chain, for subclasses.
+ // Similar to `goog.inherits`, but uses a hash of prototype properties and
+ // class properties to be extended.
+ inherit: function(protoProps, staticProps) {
+ var parent = this;
+ var child;
+
+ // The constructor function for the new subclass is either defined by you
+ // (the "constructor" property in your `extend` definition), or defaulted
+ // by us to simply call the parent's constructor.
+ if (protoProps && protoProps.hasOwnProperty('constructor')) {
+ child = protoProps.constructor;
+ } else {
+ child = function(){ return parent.apply(this, arguments); };
+ }
+
+ // Add static properties to the constructor function, if supplied.
+ ionic.extend(child, parent, staticProps);
+
+ // Set the prototype chain to inherit from `parent`, without calling
+ // `parent`'s constructor function.
+ var Surrogate = function(){ this.constructor = child; };
+ Surrogate.prototype = parent.prototype;
+ child.prototype = new Surrogate();
+
+ // Add prototype properties (instance properties) to the subclass,
+ // if supplied.
+ if (protoProps) ionic.extend(child.prototype, protoProps);
+
+ // Set a convenience property in case the parent's prototype is needed
+ // later.
+ child.__super__ = parent.prototype;
+
+ return child;
+ },
+
+ // Extend adapted from Underscore.js
+ extend: function(obj) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ for(var i = 0; i < args.length; i++) {
+ var source = args[i];
+ if (source) {
+ for (var prop in source) {
+ obj[prop] = source[prop];
+ }
+ }
+ }
+ return obj;
+ },
+
+ /**
+ * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
+ * characters such as '012ABC'. The reason why we are not using simply a number counter is that
+ * the number string gets longer over time, and it can also overflow, where as the nextId
+ * will grow much slower, it is a string, and it will never overflow.
+ *
+ * @returns an unique alpha-numeric string
+ */
+ nextUid: function() {
+ var index = uid.length;
+ var digit;
+
+ while(index) {
+ index--;
+ digit = uid[index].charCodeAt(0);
+ if (digit == 57 /*'9'*/) {
+ uid[index] = 'A';
+ return uid.join('');
+ }
+ if (digit == 90 /*'Z'*/) {
+ uid[index] = '0';
+ } else {
+ uid[index] = String.fromCharCode(digit + 1);
+ return uid.join('');
+ }
+ }
+ uid.unshift('0');
+ return uid.join('');
+ }
+ };
+
+ // Bind a few of the most useful functions to the ionic scope
+ ionic.inherit = ionic.Utils.inherit;
+ ionic.extend = ionic.Utils.extend;
+ ionic.throttle = ionic.Utils.throttle;
+ ionic.proxy = ionic.Utils.proxy;
+ ionic.debounce = ionic.Utils.debounce;
+
+})(window.ionic);
+
+/**
+ * @ngdoc page
+ * @name keyboard
+ * @module ionic
+ * @description
+ * On both Android and iOS, Ionic will attempt to prevent the keyboard from
+ * obscuring inputs and focusable elements when it appears by scrolling them
+ * into view. In order for this to work, any focusable elements must be within
+ * a [Scroll View](http://ionicframework.com/docs/api/directive/ionScroll/)
+ * or a directive such as [Content](http://ionicframework.com/docs/api/directive/ionContent/)
+ * that has a Scroll View.
+ *
+ * It will also attempt to prevent the native overflow scrolling on focus,
+ * which can cause layout issues such as pushing headers up and out of view.
+ *
+ * The keyboard fixes work best in conjunction with the
+ * [Ionic Keyboard Plugin](https://github.com/driftyco/ionic-plugins-keyboard),
+ * although it will perform reasonably well without. However, if you are using
+ * Cordova there is no reason not to use the plugin.
+ *
+ * ### Hide when keyboard shows
+ *
+ * To hide an element when the keyboard is open, add the class `hide-on-keyboard-open`.
+ *
+ * ```html
+ *
+ * ```
+ * ----------
+ *
+ * ### Plugin Usage
+ * Information on using the plugin can be found at
+ * [https://github.com/driftyco/ionic-plugins-keyboard](https://github.com/driftyco/ionic-plugins-keyboard).
+ *
+ * ----------
+ *
+ * ### Android Notes
+ * - If your app is running in fullscreen, i.e. you have
+ * ` ` in your `config.xml` file
+ * you will need to set `ionic.Platform.isFullScreen = true` manually.
+ *
+ * - You can configure the behavior of the web view when the keyboard shows by setting
+ * [android:windowSoftInputMode](http://developer.android.com/reference/android/R.attr.html#windowSoftInputMode)
+ * to either `adjustPan`, `adjustResize` or `adjustNothing` in your app's
+ * activity in `AndroidManifest.xml`. `adjustResize` is the recommended setting
+ * for Ionic, but if for some reason you do use `adjustPan` you will need to
+ * set `ionic.Platform.isFullScreen = true`.
+ *
+ * ```xml
+ *
+ *
+ * ```
+ *
+ * ### iOS Notes
+ * - If the content of your app (including the header) is being pushed up and
+ * out of view on input focus, try setting `cordova.plugins.Keyboard.disableScroll(true)`.
+ * This does **not** disable scrolling in the Ionic scroll view, rather it
+ * disables the native overflow scrolling that happens automatically as a
+ * result of focusing on inputs below the keyboard.
+ *
+ */
+
+var keyboardViewportHeight = getViewportHeight();
+var keyboardIsOpen;
+var keyboardActiveElement;
+var keyboardFocusOutTimer;
+var keyboardFocusInTimer;
+var keyboardPollHeightTimer;
+var keyboardLastShow = 0;
+
+var KEYBOARD_OPEN_CSS = 'keyboard-open';
+var SCROLL_CONTAINER_CSS = 'scroll';
+
+ionic.keyboard = {
+ isOpen: false,
+ height: null,
+ landscape: false,
+
+ hide: function() {
+ clearTimeout(keyboardFocusInTimer);
+ clearTimeout(keyboardFocusOutTimer);
+ clearTimeout(keyboardPollHeightTimer);
+
+ ionic.keyboard.isOpen = false;
+
+ ionic.trigger('resetScrollView', {
+ target: keyboardActiveElement
+ }, true);
+
+ ionic.requestAnimationFrame(function(){
+ document.body.classList.remove(KEYBOARD_OPEN_CSS);
+ });
+
+ // the keyboard is gone now, remove the touchmove that disables native scroll
+ if (window.navigator.msPointerEnabled) {
+ document.removeEventListener("MSPointerMove", keyboardPreventDefault);
+ } else {
+ document.removeEventListener('touchmove', keyboardPreventDefault);
+ }
+ document.removeEventListener('keydown', keyboardOnKeyDown);
+
+ if( keyboardHasPlugin() ) {
+ cordova.plugins.Keyboard.close();
+ }
+ },
+
+ show: function() {
+ if( keyboardHasPlugin() ) {
+ cordova.plugins.Keyboard.show();
+ }
+ }
+};
+
+function keyboardInit() {
+ if( keyboardHasPlugin() ) {
+ window.addEventListener('native.keyboardshow', keyboardNativeShow);
+ window.addEventListener('native.keyboardhide', keyboardFocusOut);
+
+ //deprecated
+ window.addEventListener('native.showkeyboard', keyboardNativeShow);
+ window.addEventListener('native.hidekeyboard', keyboardFocusOut);
+
+ } else {
+ document.body.addEventListener('focusout', keyboardFocusOut);
+ }
+
+ document.body.addEventListener('ionic.focusin', keyboardBrowserFocusIn);
+ document.body.addEventListener('focusin', keyboardBrowserFocusIn);
+
+ document.body.addEventListener('orientationchange', keyboardOrientationChange);
+
+ if (window.navigator.msPointerEnabled) {
+ document.removeEventListener("MSPointerDown", keyboardInit);
+ } else {
+ document.removeEventListener('touchstart', keyboardInit);
+ }
+}
+
+function keyboardNativeShow(e) {
+ clearTimeout(keyboardFocusOutTimer);
+ ionic.keyboard.height = e.keyboardHeight;
+}
+
+function keyboardBrowserFocusIn(e) {
+ if( !e.target || !ionic.tap.isTextInput(e.target) || ionic.tap.isDateInput(e.target) || !keyboardIsWithinScroll(e.target) ) return;
+
+ document.addEventListener('keydown', keyboardOnKeyDown, false);
+
+ document.body.scrollTop = 0;
+ document.body.querySelector('.scroll-content').scrollTop = 0;
+
+ keyboardActiveElement = e.target;
+
+ keyboardSetShow(e);
+}
+
+function keyboardSetShow(e) {
+ clearTimeout(keyboardFocusInTimer);
+ clearTimeout(keyboardFocusOutTimer);
+
+ keyboardFocusInTimer = setTimeout(function(){
+ if ( keyboardLastShow + 350 > Date.now() ) return;
+ void 0;
+ keyboardLastShow = Date.now();
+ var keyboardHeight;
+ var elementBounds = keyboardActiveElement.getBoundingClientRect();
+ var count = 0;
+
+ keyboardPollHeightTimer = setInterval(function(){
+
+ keyboardHeight = keyboardGetHeight();
+ if (count > 10){
+ clearInterval(keyboardPollHeightTimer);
+ //waited long enough, just guess
+ keyboardHeight = 275;
+ }
+ if (keyboardHeight){
+ clearInterval(keyboardPollHeightTimer);
+ keyboardShow(e.target, elementBounds.top, elementBounds.bottom, keyboardViewportHeight, keyboardHeight);
+ }
+ count++;
+
+ }, 100);
+ }, 32);
+}
+
+function keyboardShow(element, elementTop, elementBottom, viewportHeight, keyboardHeight) {
+ var details = {
+ target: element,
+ elementTop: Math.round(elementTop),
+ elementBottom: Math.round(elementBottom),
+ keyboardHeight: keyboardHeight,
+ viewportHeight: viewportHeight
+ };
+
+ details.hasPlugin = keyboardHasPlugin();
+
+ details.contentHeight = viewportHeight - keyboardHeight;
+
+ void 0;
+
+ // figure out if the element is under the keyboard
+ details.isElementUnderKeyboard = (details.elementBottom > details.contentHeight);
+
+ ionic.keyboard.isOpen = true;
+
+ // send event so the scroll view adjusts
+ keyboardActiveElement = element;
+ ionic.trigger('scrollChildIntoView', details, true);
+
+ ionic.requestAnimationFrame(function(){
+ document.body.classList.add(KEYBOARD_OPEN_CSS);
+ });
+
+ // any showing part of the document that isn't within the scroll the user
+ // could touchmove and cause some ugly changes to the app, so disable
+ // any touchmove events while the keyboard is open using e.preventDefault()
+ if (window.navigator.msPointerEnabled) {
+ document.addEventListener("MSPointerMove", keyboardPreventDefault, false);
+ } else {
+ document.addEventListener('touchmove', keyboardPreventDefault, false);
+ }
+
+ return details;
+}
+
+function keyboardFocusOut(e) {
+ clearTimeout(keyboardFocusOutTimer);
+
+ keyboardFocusOutTimer = setTimeout(ionic.keyboard.hide, 350);
+}
+
+function keyboardUpdateViewportHeight() {
+ if( getViewportHeight() > keyboardViewportHeight ) {
+ keyboardViewportHeight = getViewportHeight();
+ }
+}
+
+function keyboardOnKeyDown(e) {
+ if( ionic.scroll.isScrolling ) {
+ keyboardPreventDefault(e);
+ }
+}
+
+function keyboardPreventDefault(e) {
+ if( e.target.tagName !== 'TEXTAREA' ) {
+ e.preventDefault();
+ }
+}
+
+function keyboardOrientationChange() {
+ var updatedViewportHeight = getViewportHeight();
+
+ //too slow, have to wait for updated height
+ if (updatedViewportHeight === keyboardViewportHeight){
+ var count = 0;
+ var pollViewportHeight = setInterval(function(){
+ //give up
+ if (count > 10){
+ clearInterval(pollViewportHeight);
+ }
+
+ updatedViewportHeight = getViewportHeight();
+
+ if (updatedViewportHeight !== keyboardViewportHeight){
+ if (updatedViewportHeight < keyboardViewportHeight){
+ ionic.keyboard.landscape = true;
+ } else {
+ ionic.keyboard.landscape = false;
+ }
+ keyboardViewportHeight = updatedViewportHeight;
+ clearInterval(pollViewportHeight);
+ }
+ count++;
+
+ }, 50);
+ } else {
+ keyboardViewportHeight = updatedViewportHeight;
+ }
+}
+
+function keyboardGetHeight() {
+ // check if we are already have a keyboard height from the plugin
+ if ( ionic.keyboard.height ) {
+ return ionic.keyboard.height;
+ }
+
+ if ( ionic.Platform.isAndroid() ){
+ //should be using the plugin, no way to know how big the keyboard is, so guess
+ if ( ionic.Platform.isFullScreen ){
+ return 275;
+ }
+ //otherwise, wait for the screen to resize
+ if ( getViewportHeight() < keyboardViewportHeight ){
+ return keyboardViewportHeight - getViewportHeight();
+ } else {
+ return 0;
+ }
+ }
+
+ // fallback for when its the webview without the plugin
+ // or for just the standard web browser
+ if( ionic.Platform.isIOS() ) {
+ if ( ionic.keyboard.landscape ){
+ return 206;
+ }
+
+ if (!ionic.Platform.isWebView()){
+ return 216;
+ }
+
+ return 260;
+ }
+
+ // safe guess
+ return 275;
+}
+
+function getViewportHeight() {
+ return window.innerHeight || screen.height;
+}
+
+function keyboardIsWithinScroll(ele) {
+ while(ele) {
+ if(ele.classList.contains(SCROLL_CONTAINER_CSS)) {
+ return true;
+ }
+ ele = ele.parentElement;
+ }
+ return false;
+}
+
+function keyboardHasPlugin() {
+ return !!(window.cordova && cordova.plugins && cordova.plugins.Keyboard);
+}
+
+ionic.Platform.ready(function() {
+ keyboardUpdateViewportHeight();
+
+ // Android sometimes reports bad innerHeight on window.load
+ // try it again in a lil bit to play it safe
+ setTimeout(keyboardUpdateViewportHeight, 999);
+
+ // only initialize the adjustments for the virtual keyboard
+ // if a touchstart event happens
+ if (window.navigator.msPointerEnabled) {
+ document.addEventListener("MSPointerDown", keyboardInit, false);
+ } else {
+ document.addEventListener('touchstart', keyboardInit, false);
+ }
+});
+
+
+
+var viewportTag;
+var viewportProperties = {};
+
+ionic.viewport = {
+ orientation: function() {
+ // 0 = Portrait
+ // 90 = Landscape
+ // not using window.orientation because each device has a different implementation
+ return (window.innerWidth > window.innerHeight ? 90 : 0);
+ }
+};
+
+function viewportLoadTag() {
+ var x;
+
+ for(x=0; x 1 ? keyValue[1] : '_');
+ }
+ }
+ viewportUpdate();
+ }
+}
+
+function viewportUpdate() {
+ // unit tests in viewport.unit.js
+
+ var initWidth = viewportProperties.width;
+ var initHeight = viewportProperties.height;
+ var p = ionic.Platform;
+ var version = p.version();
+ var DEVICE_WIDTH = 'device-width';
+ var DEVICE_HEIGHT = 'device-height';
+ var orientation = ionic.viewport.orientation();
+
+ // Most times we're removing the height and adding the width
+ // So this is the default to start with, then modify per platform/version/oreintation
+ delete viewportProperties.height;
+ viewportProperties.width = DEVICE_WIDTH;
+
+ if( p.isIPad() ) {
+ // iPad
+
+ if( version > 7 ) {
+ // iPad >= 7.1
+ // https://issues.apache.org/jira/browse/CB-4323
+ delete viewportProperties.width;
+
+ } else {
+ // iPad <= 7.0
+
+ if( p.isWebView() ) {
+ // iPad <= 7.0 WebView
+
+ if( orientation == 90 ) {
+ // iPad <= 7.0 WebView Landscape
+ viewportProperties.height = '0';
+
+ } else if(version == 7) {
+ // iPad <= 7.0 WebView Portait
+ viewportProperties.height = DEVICE_HEIGHT;
+ }
+ } else {
+ // iPad <= 6.1 Browser
+ if(version < 7) {
+ viewportProperties.height = '0';
+ }
+ }
+ }
+
+ } else if( p.isIOS() ) {
+ // iPhone
+
+ if( p.isWebView() ) {
+ // iPhone WebView
+
+ if(version > 7) {
+ // iPhone >= 7.1 WebView
+ delete viewportProperties.width;
+
+ } else if(version < 7) {
+ // iPhone <= 6.1 WebView
+ // if height was set it needs to get removed with this hack for <= 6.1
+ if( initHeight ) viewportProperties.height = '0';
+
+ } else if(version == 7) {
+ //iPhone == 7.0 WebView
+ viewportProperties.height = DEVICE_HEIGHT;
+ }
+
+ } else {
+ // iPhone Browser
+
+ if (version < 7) {
+ // iPhone <= 6.1 Browser
+ // if height was set it needs to get removed with this hack for <= 6.1
+ if( initHeight ) viewportProperties.height = '0';
+ }
+ }
+
+ }
+
+ // only update the viewport tag if there was a change
+ if(initWidth !== viewportProperties.width || initHeight !== viewportProperties.height) {
+ viewportTagUpdate();
+ }
+}
+
+function viewportTagUpdate() {
+ var key, props = [];
+ for(key in viewportProperties) {
+ if( viewportProperties[key] ) {
+ props.push(key + (viewportProperties[key] == '_' ? '' : '=' + viewportProperties[key]) );
+ }
+ }
+
+ viewportTag.content = props.join(', ');
+}
+
+ionic.Platform.ready(function() {
+ viewportLoadTag();
+
+ window.addEventListener("orientationchange", function(){
+ setTimeout(viewportUpdate, 1000);
+ }, false);
+});
+
+(function(ionic) {
+'use strict';
+ ionic.views.View = function() {
+ this.initialize.apply(this, arguments);
+ };
+
+ ionic.views.View.inherit = ionic.inherit;
+
+ ionic.extend(ionic.views.View.prototype, {
+ initialize: function() {}
+ });
+
+})(window.ionic);
+
+/*
+ * Scroller
+ * http://github.com/zynga/scroller
+ *
+ * Copyright 2011, Zynga Inc.
+ * Licensed under the MIT License.
+ * https://raw.github.com/zynga/scroller/master/MIT-LICENSE.txt
+ *
+ * Based on the work of: Unify Project (unify-project.org)
+ * http://unify-project.org
+ * Copyright 2011, Deutsche Telekom AG
+ * License: MIT + Apache (V2)
+ */
+
+/* jshint eqnull: true */
+
+/**
+ * Generic animation class with support for dropped frames both optional easing and duration.
+ *
+ * Optional duration is useful when the lifetime is defined by another condition than time
+ * e.g. speed of an animating object, etc.
+ *
+ * Dropped frame logic allows to keep using the same updater logic independent from the actual
+ * rendering. This eases a lot of cases where it might be pretty complex to break down a state
+ * based on the pure time difference.
+ */
+var zyngaCore = { effect: {} };
+(function(global) {
+ var time = Date.now || function() {
+ return +new Date();
+ };
+ var desiredFrames = 60;
+ var millisecondsPerSecond = 1000;
+ var running = {};
+ var counter = 1;
+
+ zyngaCore.effect.Animate = {
+
+ /**
+ * A requestAnimationFrame wrapper / polyfill.
+ *
+ * @param callback {Function} The callback to be invoked before the next repaint.
+ * @param root {HTMLElement} The root element for the repaint
+ */
+ requestAnimationFrame: (function() {
+
+ // Check for request animation Frame support
+ var requestFrame = global.requestAnimationFrame || global.webkitRequestAnimationFrame || global.mozRequestAnimationFrame || global.oRequestAnimationFrame;
+ var isNative = !!requestFrame;
+
+ if (requestFrame && !/requestAnimationFrame\(\)\s*\{\s*\[native code\]\s*\}/i.test(requestFrame.toString())) {
+ isNative = false;
+ }
+
+ if (isNative) {
+ return function(callback, root) {
+ requestFrame(callback, root);
+ };
+ }
+
+ var TARGET_FPS = 60;
+ var requests = {};
+ var requestCount = 0;
+ var rafHandle = 1;
+ var intervalHandle = null;
+ var lastActive = +new Date();
+
+ return function(callback, root) {
+ var callbackHandle = rafHandle++;
+
+ // Store callback
+ requests[callbackHandle] = callback;
+ requestCount++;
+
+ // Create timeout at first request
+ if (intervalHandle === null) {
+
+ intervalHandle = setInterval(function() {
+
+ var time = +new Date();
+ var currentRequests = requests;
+
+ // Reset data structure before executing callbacks
+ requests = {};
+ requestCount = 0;
+
+ for(var key in currentRequests) {
+ if (currentRequests.hasOwnProperty(key)) {
+ currentRequests[key](time);
+ lastActive = time;
+ }
+ }
+
+ // Disable the timeout when nothing happens for a certain
+ // period of time
+ if (time - lastActive > 2500) {
+ clearInterval(intervalHandle);
+ intervalHandle = null;
+ }
+
+ }, 1000 / TARGET_FPS);
+ }
+
+ return callbackHandle;
+ };
+
+ })(),
+
+
+ /**
+ * Stops the given animation.
+ *
+ * @param id {Integer} Unique animation ID
+ * @return {Boolean} Whether the animation was stopped (aka, was running before)
+ */
+ stop: function(id) {
+ var cleared = running[id] != null;
+ if (cleared) {
+ running[id] = null;
+ }
+
+ return cleared;
+ },
+
+
+ /**
+ * Whether the given animation is still running.
+ *
+ * @param id {Integer} Unique animation ID
+ * @return {Boolean} Whether the animation is still running
+ */
+ isRunning: function(id) {
+ return running[id] != null;
+ },
+
+
+ /**
+ * Start the animation.
+ *
+ * @param stepCallback {Function} Pointer to function which is executed on every step.
+ * Signature of the method should be `function(percent, now, virtual) { return continueWithAnimation; }`
+ * @param verifyCallback {Function} Executed before every animation step.
+ * Signature of the method should be `function() { return continueWithAnimation; }`
+ * @param completedCallback {Function}
+ * Signature of the method should be `function(droppedFrames, finishedAnimation) {}`
+ * @param duration {Integer} Milliseconds to run the animation
+ * @param easingMethod {Function} Pointer to easing function
+ * Signature of the method should be `function(percent) { return modifiedValue; }`
+ * @param root {Element} Render root, when available. Used for internal
+ * usage of requestAnimationFrame.
+ * @return {Integer} Identifier of animation. Can be used to stop it any time.
+ */
+ start: function(stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) {
+
+ var start = time();
+ var lastFrame = start;
+ var percent = 0;
+ var dropCounter = 0;
+ var id = counter++;
+
+ if (!root) {
+ root = document.body;
+ }
+
+ // Compacting running db automatically every few new animations
+ if (id % 20 === 0) {
+ var newRunning = {};
+ for (var usedId in running) {
+ newRunning[usedId] = true;
+ }
+ running = newRunning;
+ }
+
+ // This is the internal step method which is called every few milliseconds
+ var step = function(virtual) {
+
+ // Normalize virtual value
+ var render = virtual !== true;
+
+ // Get current time
+ var now = time();
+
+ // Verification is executed before next animation step
+ if (!running[id] || (verifyCallback && !verifyCallback(id))) {
+
+ running[id] = null;
+ completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, false);
+ return;
+
+ }
+
+ // For the current rendering to apply let's update omitted steps in memory.
+ // This is important to bring internal state variables up-to-date with progress in time.
+ if (render) {
+
+ var droppedFrames = Math.round((now - lastFrame) / (millisecondsPerSecond / desiredFrames)) - 1;
+ for (var j = 0; j < Math.min(droppedFrames, 4); j++) {
+ step(true);
+ dropCounter++;
+ }
+
+ }
+
+ // Compute percent value
+ if (duration) {
+ percent = (now - start) / duration;
+ if (percent > 1) {
+ percent = 1;
+ }
+ }
+
+ // Execute step callback, then...
+ var value = easingMethod ? easingMethod(percent) : percent;
+ if ((stepCallback(value, now, render) === false || percent === 1) && render) {
+ running[id] = null;
+ completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, percent === 1 || duration == null);
+ } else if (render) {
+ lastFrame = now;
+ zyngaCore.effect.Animate.requestAnimationFrame(step, root);
+ }
+ };
+
+ // Mark as running
+ running[id] = true;
+
+ // Init first step
+ zyngaCore.effect.Animate.requestAnimationFrame(step, root);
+
+ // Return unique animation ID
+ return id;
+ }
+ };
+})(this);
+
+/*
+ * Scroller
+ * http://github.com/zynga/scroller
+ *
+ * Copyright 2011, Zynga Inc.
+ * Licensed under the MIT License.
+ * https://raw.github.com/zynga/scroller/master/MIT-LICENSE.txt
+ *
+ * Based on the work of: Unify Project (unify-project.org)
+ * http://unify-project.org
+ * Copyright 2011, Deutsche Telekom AG
+ * License: MIT + Apache (V2)
+ */
+
+var Scroller;
+
+(function(ionic) {
+ var NOOP = function(){};
+
+ // Easing Equations (c) 2003 Robert Penner, all rights reserved.
+ // Open source under the BSD License.
+
+ /**
+ * @param pos {Number} position between 0 (start of effect) and 1 (end of effect)
+ **/
+ var easeOutCubic = function(pos) {
+ return (Math.pow((pos - 1), 3) + 1);
+ };
+
+ /**
+ * @param pos {Number} position between 0 (start of effect) and 1 (end of effect)
+ **/
+ var easeInOutCubic = function(pos) {
+ if ((pos /= 0.5) < 1) {
+ return 0.5 * Math.pow(pos, 3);
+ }
+
+ return 0.5 * (Math.pow((pos - 2), 3) + 2);
+ };
+
+
+/**
+ * ionic.views.Scroll
+ * A powerful scroll view with support for bouncing, pull to refresh, and paging.
+ * @param {Object} options options for the scroll view
+ * @class A scroll view system
+ * @memberof ionic.views
+ */
+ionic.views.Scroll = ionic.views.View.inherit({
+ initialize: function(options) {
+ var self = this;
+
+ this.__container = options.el;
+ this.__content = options.el.firstElementChild;
+
+ //Remove any scrollTop attached to these elements; they are virtual scroll now
+ //This also stops on-load-scroll-to-window.location.hash that the browser does
+ setTimeout(function() {
+ if (self.__container && self.__content) {
+ self.__container.scrollTop = 0;
+ self.__content.scrollTop = 0;
+ }
+ });
+
+ this.options = {
+
+ /** Disable scrolling on x-axis by default */
+ scrollingX: false,
+ scrollbarX: true,
+
+ /** Enable scrolling on y-axis */
+ scrollingY: true,
+ scrollbarY: true,
+
+ startX: 0,
+ startY: 0,
+
+ /** The amount to dampen mousewheel events */
+ wheelDampen: 6,
+
+ /** The minimum size the scrollbars scale to while scrolling */
+ minScrollbarSizeX: 5,
+ minScrollbarSizeY: 5,
+
+ /** Scrollbar fading after scrolling */
+ scrollbarsFade: true,
+ scrollbarFadeDelay: 300,
+ /** The initial fade delay when the pane is resized or initialized */
+ scrollbarResizeFadeDelay: 1000,
+
+ /** Enable animations for deceleration, snap back, zooming and scrolling */
+ animating: true,
+
+ /** duration for animations triggered by scrollTo/zoomTo */
+ animationDuration: 250,
+
+ /** Enable bouncing (content can be slowly moved outside and jumps back after releasing) */
+ bouncing: true,
+
+ /** Enable locking to the main axis if user moves only slightly on one of them at start */
+ locking: true,
+
+ /** Enable pagination mode (switching between full page content panes) */
+ paging: false,
+
+ /** Enable snapping of content to a configured pixel grid */
+ snapping: false,
+
+ /** Enable zooming of content via API, fingers and mouse wheel */
+ zooming: false,
+
+ /** Minimum zoom level */
+ minZoom: 0.5,
+
+ /** Maximum zoom level */
+ maxZoom: 3,
+
+ /** Multiply or decrease scrolling speed **/
+ speedMultiplier: 1,
+
+ deceleration: 0.97,
+
+ /** Callback that is fired on the later of touch end or deceleration end,
+ provided that another scrolling action has not begun. Used to know
+ when to fade out a scrollbar. */
+ scrollingComplete: NOOP,
+
+ /** This configures the amount of change applied to deceleration when reaching boundaries **/
+ penetrationDeceleration : 0.03,
+
+ /** This configures the amount of change applied to acceleration when reaching boundaries **/
+ penetrationAcceleration : 0.08,
+
+ // The ms interval for triggering scroll events
+ scrollEventInterval: 10,
+
+ getContentWidth: function() {
+ return Math.max(self.__content.scrollWidth, self.__content.offsetWidth);
+ },
+ getContentHeight: function() {
+ return Math.max(self.__content.scrollHeight, self.__content.offsetHeight + (self.__content.offsetTop * 2));
+ }
+ };
+
+ for (var key in options) {
+ this.options[key] = options[key];
+ }
+
+ this.hintResize = ionic.debounce(function() {
+ self.resize();
+ }, 1000, true);
+
+ this.onScroll = function() {
+
+ if(!ionic.scroll.isScrolling) {
+ setTimeout(self.setScrollStart, 50);
+ } else {
+ clearTimeout(self.scrollTimer);
+ self.scrollTimer = setTimeout(self.setScrollStop, 80);
+ }
+
+ };
+
+ this.setScrollStart = function() {
+ ionic.scroll.isScrolling = Math.abs(ionic.scroll.lastTop - self.__scrollTop) > 1;
+ clearTimeout(self.scrollTimer);
+ self.scrollTimer = setTimeout(self.setScrollStop, 80);
+ };
+
+ this.setScrollStop = function() {
+ ionic.scroll.isScrolling = false;
+ ionic.scroll.lastTop = self.__scrollTop;
+ };
+
+ this.triggerScrollEvent = ionic.throttle(function() {
+ self.onScroll();
+ ionic.trigger('scroll', {
+ scrollTop: self.__scrollTop,
+ scrollLeft: self.__scrollLeft,
+ target: self.__container
+ });
+ }, this.options.scrollEventInterval);
+
+ this.triggerScrollEndEvent = function() {
+ ionic.trigger('scrollend', {
+ scrollTop: self.__scrollTop,
+ scrollLeft: self.__scrollLeft,
+ target: self.__container
+ });
+ };
+
+ this.__scrollLeft = this.options.startX;
+ this.__scrollTop = this.options.startY;
+
+ // Get the render update function, initialize event handlers,
+ // and calculate the size of the scroll container
+ this.__callback = this.getRenderFn();
+ this.__initEventHandlers();
+ this.__createScrollbars();
+
+ },
+
+ run: function() {
+ this.resize();
+
+ // Fade them out
+ this.__fadeScrollbars('out', this.options.scrollbarResizeFadeDelay);
+ },
+
+
+
+ /*
+ ---------------------------------------------------------------------------
+ INTERNAL FIELDS :: STATUS
+ ---------------------------------------------------------------------------
+ */
+
+ /** Whether only a single finger is used in touch handling */
+ __isSingleTouch: false,
+
+ /** Whether a touch event sequence is in progress */
+ __isTracking: false,
+
+ /** Whether a deceleration animation went to completion. */
+ __didDecelerationComplete: false,
+
+ /**
+ * Whether a gesture zoom/rotate event is in progress. Activates when
+ * a gesturestart event happens. This has higher priority than dragging.
+ */
+ __isGesturing: false,
+
+ /**
+ * Whether the user has moved by such a distance that we have enabled
+ * dragging mode. Hint: It's only enabled after some pixels of movement to
+ * not interrupt with clicks etc.
+ */
+ __isDragging: false,
+
+ /**
+ * Not touching and dragging anymore, and smoothly animating the
+ * touch sequence using deceleration.
+ */
+ __isDecelerating: false,
+
+ /**
+ * Smoothly animating the currently configured change
+ */
+ __isAnimating: false,
+
+
+
+ /*
+ ---------------------------------------------------------------------------
+ INTERNAL FIELDS :: DIMENSIONS
+ ---------------------------------------------------------------------------
+ */
+
+ /** Available outer left position (from document perspective) */
+ __clientLeft: 0,
+
+ /** Available outer top position (from document perspective) */
+ __clientTop: 0,
+
+ /** Available outer width */
+ __clientWidth: 0,
+
+ /** Available outer height */
+ __clientHeight: 0,
+
+ /** Outer width of content */
+ __contentWidth: 0,
+
+ /** Outer height of content */
+ __contentHeight: 0,
+
+ /** Snapping width for content */
+ __snapWidth: 100,
+
+ /** Snapping height for content */
+ __snapHeight: 100,
+
+ /** Height to assign to refresh area */
+ __refreshHeight: null,
+
+ /** Whether the refresh process is enabled when the event is released now */
+ __refreshActive: false,
+
+ /** Callback to execute on activation. This is for signalling the user about a refresh is about to happen when he release */
+ __refreshActivate: null,
+
+ /** Callback to execute on deactivation. This is for signalling the user about the refresh being cancelled */
+ __refreshDeactivate: null,
+
+ /** Callback to execute to start the actual refresh. Call {@link #refreshFinish} when done */
+ __refreshStart: null,
+
+ /** Zoom level */
+ __zoomLevel: 1,
+
+ /** Scroll position on x-axis */
+ __scrollLeft: 0,
+
+ /** Scroll position on y-axis */
+ __scrollTop: 0,
+
+ /** Maximum allowed scroll position on x-axis */
+ __maxScrollLeft: 0,
+
+ /** Maximum allowed scroll position on y-axis */
+ __maxScrollTop: 0,
+
+ /* Scheduled left position (final position when animating) */
+ __scheduledLeft: 0,
+
+ /* Scheduled top position (final position when animating) */
+ __scheduledTop: 0,
+
+ /* Scheduled zoom level (final scale when animating) */
+ __scheduledZoom: 0,
+
+
+
+ /*
+ ---------------------------------------------------------------------------
+ INTERNAL FIELDS :: LAST POSITIONS
+ ---------------------------------------------------------------------------
+ */
+
+ /** Left position of finger at start */
+ __lastTouchLeft: null,
+
+ /** Top position of finger at start */
+ __lastTouchTop: null,
+
+ /** Timestamp of last move of finger. Used to limit tracking range for deceleration speed. */
+ __lastTouchMove: null,
+
+ /** List of positions, uses three indexes for each state: left, top, timestamp */
+ __positions: null,
+
+
+
+ /*
+ ---------------------------------------------------------------------------
+ INTERNAL FIELDS :: DECELERATION SUPPORT
+ ---------------------------------------------------------------------------
+ */
+
+ /** Minimum left scroll position during deceleration */
+ __minDecelerationScrollLeft: null,
+
+ /** Minimum top scroll position during deceleration */
+ __minDecelerationScrollTop: null,
+
+ /** Maximum left scroll position during deceleration */
+ __maxDecelerationScrollLeft: null,
+
+ /** Maximum top scroll position during deceleration */
+ __maxDecelerationScrollTop: null,
+
+ /** Current factor to modify horizontal scroll position with on every step */
+ __decelerationVelocityX: null,
+
+ /** Current factor to modify vertical scroll position with on every step */
+ __decelerationVelocityY: null,
+
+
+ /** the browser-specific property to use for transforms */
+ __transformProperty: null,
+ __perspectiveProperty: null,
+
+ /** scrollbar indicators */
+ __indicatorX: null,
+ __indicatorY: null,
+
+ /** Timeout for scrollbar fading */
+ __scrollbarFadeTimeout: null,
+
+ /** whether we've tried to wait for size already */
+ __didWaitForSize: null,
+ __sizerTimeout: null,
+
+ __initEventHandlers: function() {
+ var self = this;
+
+ // Event Handler
+ var container = this.__container;
+
+ self.scrollChildIntoView = function(e) {
+
+ //distance from bottom of scrollview to top of viewport
+ var scrollBottomOffsetToTop;
+
+ if( !self.isScrolledIntoView ) {
+ // shrink scrollview so we can actually scroll if the input is hidden
+ // if it isn't shrink so we can scroll to inputs under the keyboard
+ if (ionic.Platform.isIOS() || ionic.Platform.isFullScreen){
+ // if there are things below the scroll view account for them and
+ // subtract them from the keyboard height when resizing
+ scrollBottomOffsetToTop = container.getBoundingClientRect().bottom;
+ var scrollBottomOffsetToBottom = e.detail.viewportHeight - scrollBottomOffsetToTop;
+ var keyboardOffset = Math.max(0, e.detail.keyboardHeight - scrollBottomOffsetToBottom);
+ container.style.height = (container.clientHeight - keyboardOffset) + "px";
+ container.style.overflow = "visible";
+ //update scroll view
+ self.resize();
+ }
+ self.isScrolledIntoView = true;
+ }
+
+ //If the element is positioned under the keyboard...
+ if( e.detail.isElementUnderKeyboard ) {
+ var delay;
+ // Wait on android for web view to resize
+ if ( ionic.Platform.isAndroid() && !ionic.Platform.isFullScreen ) {
+ // android y u resize so slow
+ if ( ionic.Platform.version() < 4.4) {
+ delay = 500;
+ } else {
+ // probably overkill for chrome
+ delay = 350;
+ }
+ } else {
+ delay = 80;
+ }
+
+ //Put element in middle of visible screen
+ //Wait for android to update view height and resize() to reset scroll position
+ ionic.scroll.isScrolling = true;
+ setTimeout(function(){
+ //middle of the scrollview, where we want to scroll to
+ var scrollMidpointOffset = container.clientHeight * 0.5;
+
+ scrollBottomOffsetToTop = container.getBoundingClientRect().bottom;
+ //distance from top of focused element to the bottom of the scroll view
+ var elementTopOffsetToScrollBottom = e.detail.elementTop - scrollBottomOffsetToTop;
+
+ var scrollTop = elementTopOffsetToScrollBottom + scrollMidpointOffset;
+
+ if (scrollTop > 0){
+ ionic.tap.cloneFocusedInput(container, self);
+ self.scrollBy(0, scrollTop, true);
+ self.onScroll();
+ }
+ }, delay);
+ }
+
+ //Only the first scrollView parent of the element that broadcasted this event
+ //(the active element that needs to be shown) should receive this event
+ e.stopPropagation();
+ };
+
+ self.resetScrollView = function(e) {
+ //return scrollview to original height once keyboard has hidden
+ if(self.isScrolledIntoView) {
+ self.isScrolledIntoView = false;
+ container.style.height = "";
+ container.style.overflow = "";
+ self.resize();
+ ionic.scroll.isScrolling = false;
+ }
+ };
+
+ //Broadcasted when keyboard is shown on some platforms.
+ //See js/utils/keyboard.js
+ container.addEventListener('scrollChildIntoView', self.scrollChildIntoView);
+ container.addEventListener('resetScrollView', self.resetScrollView);
+
+ function getEventTouches(e) {
+ return e.touches && e.touches.length ? e.touches : [{
+ pageX: e.pageX,
+ pageY: e.pageY
+ }];
+ }
+
+ self.touchStart = function(e) {
+ self.startCoordinates = ionic.tap.pointerCoord(e);
+
+ if ( ionic.tap.ignoreScrollStart(e) ) {
+ return;
+ }
+
+ self.__isDown = true;
+
+ if( ionic.tap.containsOrIsTextInput(e.target) || e.target.tagName === 'SELECT' ) {
+ // do not start if the target is a text input
+ // if there is a touchmove on this input, then we can start the scroll
+ self.__hasStarted = false;
+ return;
+ }
+
+ self.__isSelectable = true;
+ self.__enableScrollY = true;
+ self.__hasStarted = true;
+ self.doTouchStart(getEventTouches(e), e.timeStamp);
+ e.preventDefault();
+ };
+
+ self.touchMove = function(e) {
+ if(!self.__isDown ||
+ e.defaultPrevented ||
+ (e.target.tagName === 'TEXTAREA' && e.target.parentElement.querySelector(':focus')) ) {
+ return;
+ }
+
+ if( !self.__hasStarted && ( ionic.tap.containsOrIsTextInput(e.target) || e.target.tagName === 'SELECT' ) ) {
+ // the target is a text input and scroll has started
+ // since the text input doesn't start on touchStart, do it here
+ self.__hasStarted = true;
+ self.doTouchStart(getEventTouches(e), e.timeStamp);
+ e.preventDefault();
+ return;
+ }
+
+ if(self.startCoordinates) {
+ // we have start coordinates, so get this touch move's current coordinates
+ var currentCoordinates = ionic.tap.pointerCoord(e);
+
+ if( self.__isSelectable &&
+ ionic.tap.isTextInput(e.target) &&
+ Math.abs(self.startCoordinates.x - currentCoordinates.x) > 20 ) {
+ // user slid the text input's caret on its x axis, disable any future y scrolling
+ self.__enableScrollY = false;
+ self.__isSelectable = true;
+ }
+
+ if( self.__enableScrollY && Math.abs(self.startCoordinates.y - currentCoordinates.y) > 10 ) {
+ // user scrolled the entire view on the y axis
+ // disabled being able to select text on an input
+ // hide the input which has focus, and show a cloned one that doesn't have focus
+ self.__isSelectable = false;
+ ionic.tap.cloneFocusedInput(container, self);
+ }
+ }
+
+ self.doTouchMove(getEventTouches(e), e.timeStamp, e.scale);
+ self.__isDown = true;
+ };
+
+ self.touchEnd = function(e) {
+ if(!self.__isDown) return;
+
+ self.doTouchEnd(e.timeStamp);
+ self.__isDown = false;
+ self.__hasStarted = false;
+ self.__isSelectable = true;
+ self.__enableScrollY = true;
+
+ if( !self.__isDragging && !self.__isDecelerating && !self.__isAnimating ) {
+ ionic.tap.removeClonedInputs(container, self);
+ }
+ };
+
+ if ('ontouchstart' in window) {
+ // Touch Events
+ container.addEventListener("touchstart", self.touchStart, false);
+ document.addEventListener("touchmove", self.touchMove, false);
+ document.addEventListener("touchend", self.touchEnd, false);
+ document.addEventListener("touchcancel", self.touchEnd, false);
+
+ } else if (window.navigator.pointerEnabled) {
+ // Pointer Events
+ container.addEventListener("pointerdown", self.touchStart, false);
+ document.addEventListener("pointermove", self.touchMove, false);
+ document.addEventListener("pointerup", self.touchEnd, false);
+ document.addEventListener("pointercancel", self.touchEnd, false);
+
+ } else if (window.navigator.msPointerEnabled) {
+ // IE10, WP8 (Pointer Events)
+ container.addEventListener("MSPointerDown", self.touchStart, false);
+ document.addEventListener("MSPointerMove", self.touchMove, false);
+ document.addEventListener("MSPointerUp", self.touchEnd, false);
+ document.addEventListener("MSPointerCancel", self.touchEnd, false);
+
+ } else {
+ // Mouse Events
+ var mousedown = false;
+
+ self.mouseDown = function(e) {
+ if ( ionic.tap.ignoreScrollStart(e) || e.target.tagName === 'SELECT' ) {
+ return;
+ }
+ self.doTouchStart(getEventTouches(e), e.timeStamp);
+
+ if( !ionic.tap.isTextInput(e.target) ) {
+ e.preventDefault();
+ }
+ mousedown = true;
+ };
+
+ self.mouseMove = function(e) {
+ if (!mousedown || e.defaultPrevented) {
+ return;
+ }
+
+ self.doTouchMove(getEventTouches(e), e.timeStamp);
+
+ mousedown = true;
+ };
+
+ self.mouseUp = function(e) {
+ if (!mousedown) {
+ return;
+ }
+
+ self.doTouchEnd(e.timeStamp);
+
+ mousedown = false;
+ };
+
+ self.mouseWheel = ionic.animationFrameThrottle(function(e) {
+ var scrollParent = ionic.DomUtil.getParentOrSelfWithClass(e.target, 'ionic-scroll');
+ if (scrollParent === self.__container) {
+
+ self.hintResize();
+ self.scrollBy(
+ e.wheelDeltaX/self.options.wheelDampen,
+ -e.wheelDeltaY/self.options.wheelDampen
+ );
+
+ self.__fadeScrollbars('in');
+ clearTimeout(self.__wheelHideBarTimeout);
+ self.__wheelHideBarTimeout = setTimeout(function() {
+ self.__fadeScrollbars('out');
+ }, 100);
+ }
+ });
+
+ container.addEventListener("mousedown", self.mouseDown, false);
+ document.addEventListener("mousemove", self.mouseMove, false);
+ document.addEventListener("mouseup", self.mouseUp, false);
+ document.addEventListener('mousewheel', self.mouseWheel, false);
+ }
+ },
+
+ __cleanup: function() {
+ var container = this.__container;
+ var self = this;
+
+ container.removeEventListener('touchstart', self.touchStart);
+ document.removeEventListener('touchmove', self.touchMove);
+ document.removeEventListener('touchend', self.touchEnd);
+ document.removeEventListener('touchcancel', self.touchCancel);
+
+ container.removeEventListener("pointerdown", self.touchStart);
+ document.removeEventListener("pointermove", self.touchMove);
+ document.removeEventListener("pointerup", self.touchEnd);
+ document.removeEventListener("pointercancel", self.touchEnd);
+
+ container.removeEventListener("MSPointerDown", self.touchStart);
+ document.removeEventListener("MSPointerMove", self.touchMove);
+ document.removeEventListener("MSPointerUp", self.touchEnd);
+ document.removeEventListener("MSPointerCancel", self.touchEnd);
+
+ container.removeEventListener("mousedown", self.mouseDown);
+ document.removeEventListener("mousemove", self.mouseMove);
+ document.removeEventListener("mouseup", self.mouseUp);
+ document.removeEventListener('mousewheel', self.mouseWheel);
+
+ container.removeEventListener('scrollChildIntoView', self.scrollChildIntoView);
+ container.removeEventListener('resetScrollView', self.resetScrollView);
+
+ ionic.tap.removeClonedInputs(container, self);
+
+ delete this.__container;
+ delete this.__content;
+ delete this.__indicatorX;
+ delete this.__indicatorY;
+ delete this.options.el;
+
+ this.__callback = this.scrollChildIntoView = this.resetScrollView = angular.noop;
+
+ this.mouseMove = this.mouseDown = this.mouseUp = this.mouseWheel =
+ this.touchStart = this.touchMove = this.touchEnd = this.touchCancel = angular.noop;
+
+ this.resize = this.scrollTo = this.zoomTo =
+ this.__scrollingComplete = angular.noop;
+ container = null;
+ },
+
+ /** Create a scroll bar div with the given direction **/
+ __createScrollbar: function(direction) {
+ var bar = document.createElement('div'),
+ indicator = document.createElement('div');
+
+ indicator.className = 'scroll-bar-indicator scroll-bar-fade-out';
+
+ if(direction == 'h') {
+ bar.className = 'scroll-bar scroll-bar-h';
+ } else {
+ bar.className = 'scroll-bar scroll-bar-v';
+ }
+
+ bar.appendChild(indicator);
+ return bar;
+ },
+
+ __createScrollbars: function() {
+ var indicatorX, indicatorY;
+
+ if(this.options.scrollingX) {
+ indicatorX = {
+ el: this.__createScrollbar('h'),
+ sizeRatio: 1
+ };
+ indicatorX.indicator = indicatorX.el.children[0];
+
+ if(this.options.scrollbarX) {
+ this.__container.appendChild(indicatorX.el);
+ }
+ this.__indicatorX = indicatorX;
+ }
+
+ if(this.options.scrollingY) {
+ indicatorY = {
+ el: this.__createScrollbar('v'),
+ sizeRatio: 1
+ };
+ indicatorY.indicator = indicatorY.el.children[0];
+
+ if(this.options.scrollbarY) {
+ this.__container.appendChild(indicatorY.el);
+ }
+ this.__indicatorY = indicatorY;
+ }
+ },
+
+ __resizeScrollbars: function() {
+ var self = this;
+
+ // Update horiz bar
+ if(self.__indicatorX) {
+ var width = Math.max(Math.round(self.__clientWidth * self.__clientWidth / (self.__contentWidth)), 20);
+ if(width > self.__contentWidth) {
+ width = 0;
+ }
+ self.__indicatorX.size = width;
+ self.__indicatorX.minScale = this.options.minScrollbarSizeX / width;
+ self.__indicatorX.indicator.style.width = width + 'px';
+ self.__indicatorX.maxPos = self.__clientWidth - width;
+ self.__indicatorX.sizeRatio = self.__maxScrollLeft ? self.__indicatorX.maxPos / self.__maxScrollLeft : 1;
+ }
+
+ // Update vert bar
+ if(self.__indicatorY) {
+ var height = Math.max(Math.round(self.__clientHeight * self.__clientHeight / (self.__contentHeight)), 20);
+ if(height > self.__contentHeight) {
+ height = 0;
+ }
+ self.__indicatorY.size = height;
+ self.__indicatorY.minScale = this.options.minScrollbarSizeY / height;
+ self.__indicatorY.maxPos = self.__clientHeight - height;
+ self.__indicatorY.indicator.style.height = height + 'px';
+ self.__indicatorY.sizeRatio = self.__maxScrollTop ? self.__indicatorY.maxPos / self.__maxScrollTop : 1;
+ }
+ },
+
+ /**
+ * Move and scale the scrollbars as the page scrolls.
+ */
+ __repositionScrollbars: function() {
+ var self = this, width, heightScale,
+ widthDiff, heightDiff,
+ x, y,
+ xstop = 0, ystop = 0;
+
+ if(self.__indicatorX) {
+ // Handle the X scrollbar
+
+ // Don't go all the way to the right if we have a vertical scrollbar as well
+ if(self.__indicatorY) xstop = 10;
+
+ x = Math.round(self.__indicatorX.sizeRatio * self.__scrollLeft) || 0,
+
+ // The the difference between the last content X position, and our overscrolled one
+ widthDiff = self.__scrollLeft - (self.__maxScrollLeft - xstop);
+
+ if(self.__scrollLeft < 0) {
+
+ widthScale = Math.max(self.__indicatorX.minScale,
+ (self.__indicatorX.size - Math.abs(self.__scrollLeft)) / self.__indicatorX.size);
+
+ // Stay at left
+ x = 0;
+
+ // Make sure scale is transformed from the left/center origin point
+ self.__indicatorX.indicator.style[self.__transformOriginProperty] = 'left center';
+ } else if(widthDiff > 0) {
+
+ widthScale = Math.max(self.__indicatorX.minScale,
+ (self.__indicatorX.size - widthDiff) / self.__indicatorX.size);
+
+ // Stay at the furthest x for the scrollable viewport
+ x = self.__indicatorX.maxPos - xstop;
+
+ // Make sure scale is transformed from the right/center origin point
+ self.__indicatorX.indicator.style[self.__transformOriginProperty] = 'right center';
+
+ } else {
+
+ // Normal motion
+ x = Math.min(self.__maxScrollLeft, Math.max(0, x));
+ widthScale = 1;
+
+ }
+
+ self.__indicatorX.indicator.style[self.__transformProperty] = 'translate3d(' + x + 'px, 0, 0) scaleX(' + widthScale + ')';
+ }
+
+ if(self.__indicatorY) {
+
+ y = Math.round(self.__indicatorY.sizeRatio * self.__scrollTop) || 0;
+
+ // Don't go all the way to the right if we have a vertical scrollbar as well
+ if(self.__indicatorX) ystop = 10;
+
+ heightDiff = self.__scrollTop - (self.__maxScrollTop - ystop);
+
+ if(self.__scrollTop < 0) {
+
+ heightScale = Math.max(self.__indicatorY.minScale, (self.__indicatorY.size - Math.abs(self.__scrollTop)) / self.__indicatorY.size);
+
+ // Stay at top
+ y = 0;
+
+ // Make sure scale is transformed from the center/top origin point
+ self.__indicatorY.indicator.style[self.__transformOriginProperty] = 'center top';
+
+ } else if(heightDiff > 0) {
+
+ heightScale = Math.max(self.__indicatorY.minScale, (self.__indicatorY.size - heightDiff) / self.__indicatorY.size);
+
+ // Stay at bottom of scrollable viewport
+ y = self.__indicatorY.maxPos - ystop;
+
+ // Make sure scale is transformed from the center/bottom origin point
+ self.__indicatorY.indicator.style[self.__transformOriginProperty] = 'center bottom';
+
+ } else {
+
+ // Normal motion
+ y = Math.min(self.__maxScrollTop, Math.max(0, y));
+ heightScale = 1;
+
+ }
+
+ self.__indicatorY.indicator.style[self.__transformProperty] = 'translate3d(0,' + y + 'px, 0) scaleY(' + heightScale + ')';
+ }
+ },
+
+ __fadeScrollbars: function(direction, delay) {
+ var self = this;
+
+ if(!this.options.scrollbarsFade) {
+ return;
+ }
+
+ var className = 'scroll-bar-fade-out';
+
+ if(self.options.scrollbarsFade === true) {
+ clearTimeout(self.__scrollbarFadeTimeout);
+
+ if(direction == 'in') {
+ if(self.__indicatorX) { self.__indicatorX.indicator.classList.remove(className); }
+ if(self.__indicatorY) { self.__indicatorY.indicator.classList.remove(className); }
+ } else {
+ self.__scrollbarFadeTimeout = setTimeout(function() {
+ if(self.__indicatorX) { self.__indicatorX.indicator.classList.add(className); }
+ if(self.__indicatorY) { self.__indicatorY.indicator.classList.add(className); }
+ }, delay || self.options.scrollbarFadeDelay);
+ }
+ }
+ },
+
+ __scrollingComplete: function() {
+ var self = this;
+ self.options.scrollingComplete();
+ ionic.tap.removeClonedInputs(self.__container, self);
+
+ self.__fadeScrollbars('out');
+ },
+
+ resize: function() {
+ if(!this.__container || !this.options) return;
+
+ // Update Scroller dimensions for changed content
+ // Add padding to bottom of content
+ this.setDimensions(
+ this.__container.clientWidth,
+ this.__container.clientHeight,
+ this.options.getContentWidth(),
+ this.options.getContentHeight()
+ );
+ },
+ /*
+ ---------------------------------------------------------------------------
+ PUBLIC API
+ ---------------------------------------------------------------------------
+ */
+
+ getRenderFn: function() {
+ var self = this;
+
+ var content = this.__content;
+
+ var docStyle = document.documentElement.style;
+
+ var engine;
+ if ('MozAppearance' in docStyle) {
+ engine = 'gecko';
+ } else if ('WebkitAppearance' in docStyle) {
+ engine = 'webkit';
+ } else if (typeof navigator.cpuClass === 'string') {
+ engine = 'trident';
+ }
+
+ var vendorPrefix = {
+ trident: 'ms',
+ gecko: 'Moz',
+ webkit: 'Webkit',
+ presto: 'O'
+ }[engine];
+
+ var helperElem = document.createElement("div");
+ var undef;
+
+ var perspectiveProperty = vendorPrefix + "Perspective";
+ var transformProperty = vendorPrefix + "Transform";
+ var transformOriginProperty = vendorPrefix + 'TransformOrigin';
+
+ self.__perspectiveProperty = transformProperty;
+ self.__transformProperty = transformProperty;
+ self.__transformOriginProperty = transformOriginProperty;
+
+ if (helperElem.style[perspectiveProperty] !== undef) {
+
+ return function(left, top, zoom, wasResize) {
+ content.style[transformProperty] = 'translate3d(' + (-left) + 'px,' + (-top) + 'px,0) scale(' + zoom + ')';
+ self.__repositionScrollbars();
+ if(!wasResize) {
+ self.triggerScrollEvent();
+ }
+ };
+
+ } else if (helperElem.style[transformProperty] !== undef) {
+
+ return function(left, top, zoom, wasResize) {
+ content.style[transformProperty] = 'translate(' + (-left) + 'px,' + (-top) + 'px) scale(' + zoom + ')';
+ self.__repositionScrollbars();
+ if(!wasResize) {
+ self.triggerScrollEvent();
+ }
+ };
+
+ } else {
+
+ return function(left, top, zoom, wasResize) {
+ content.style.marginLeft = left ? (-left/zoom) + 'px' : '';
+ content.style.marginTop = top ? (-top/zoom) + 'px' : '';
+ content.style.zoom = zoom || '';
+ self.__repositionScrollbars();
+ if(!wasResize) {
+ self.triggerScrollEvent();
+ }
+ };
+
+ }
+ },
+
+
+ /**
+ * Configures the dimensions of the client (outer) and content (inner) elements.
+ * Requires the available space for the outer element and the outer size of the inner element.
+ * All values which are falsy (null or zero etc.) are ignored and the old value is kept.
+ *
+ * @param clientWidth {Integer} Inner width of outer element
+ * @param clientHeight {Integer} Inner height of outer element
+ * @param contentWidth {Integer} Outer width of inner element
+ * @param contentHeight {Integer} Outer height of inner element
+ */
+ setDimensions: function(clientWidth, clientHeight, contentWidth, contentHeight) {
+ var self = this;
+
+ // Only update values which are defined
+ if (clientWidth === +clientWidth) {
+ self.__clientWidth = clientWidth;
+ }
+
+ if (clientHeight === +clientHeight) {
+ self.__clientHeight = clientHeight;
+ }
+
+ if (contentWidth === +contentWidth) {
+ self.__contentWidth = contentWidth;
+ }
+
+ if (contentHeight === +contentHeight) {
+ self.__contentHeight = contentHeight;
+ }
+
+ // Refresh maximums
+ self.__computeScrollMax();
+ self.__resizeScrollbars();
+
+ // Refresh scroll position
+ self.scrollTo(self.__scrollLeft, self.__scrollTop, true, null, true);
+
+ },
+
+
+ /**
+ * Sets the client coordinates in relation to the document.
+ *
+ * @param left {Integer} Left position of outer element
+ * @param top {Integer} Top position of outer element
+ */
+ setPosition: function(left, top) {
+
+ var self = this;
+
+ self.__clientLeft = left || 0;
+ self.__clientTop = top || 0;
+
+ },
+
+
+ /**
+ * Configures the snapping (when snapping is active)
+ *
+ * @param width {Integer} Snapping width
+ * @param height {Integer} Snapping height
+ */
+ setSnapSize: function(width, height) {
+
+ var self = this;
+
+ self.__snapWidth = width;
+ self.__snapHeight = height;
+
+ },
+
+
+ /**
+ * Activates pull-to-refresh. A special zone on the top of the list to start a list refresh whenever
+ * the user event is released during visibility of this zone. This was introduced by some apps on iOS like
+ * the official Twitter client.
+ *
+ * @param height {Integer} Height of pull-to-refresh zone on top of rendered list
+ * @param activateCallback {Function} Callback to execute on activation. This is for signalling the user about a refresh is about to happen when he release.
+ * @param deactivateCallback {Function} Callback to execute on deactivation. This is for signalling the user about the refresh being cancelled.
+ * @param startCallback {Function} Callback to execute to start the real async refresh action. Call {@link #finishPullToRefresh} after finish of refresh.
+ * @param showCallback {Function} Callback to execute when the refresher should be shown. This is for showing the refresher during a negative scrollTop.
+ * @param hideCallback {Function} Callback to execute when the refresher should be hidden. This is for hiding the refresher when it's behind the nav bar.
+ * @param tailCallback {Function} Callback to execute just before the refresher returns to it's original state. This is for zooming out the refresher.
+ */
+ activatePullToRefresh: function(height, activateCallback, deactivateCallback, startCallback, showCallback, hideCallback, tailCallback) {
+
+ var self = this;
+
+ self.__refreshHeight = height;
+ self.__refreshActivate = function(){ionic.requestAnimationFrame(activateCallback);};
+ self.__refreshDeactivate = function(){ionic.requestAnimationFrame(deactivateCallback);};
+ self.__refreshStart = function(){ionic.requestAnimationFrame(startCallback);};
+ self.__refreshShow = function(){ionic.requestAnimationFrame(showCallback);};
+ self.__refreshHide = function(){ionic.requestAnimationFrame(hideCallback);};
+ self.__refreshTail = function(){ionic.requestAnimationFrame(tailCallback);};
+ self.__refreshTailTime = 100;
+ self.__minSpinTime = 600;
+ },
+
+
+ /**
+ * Starts pull-to-refresh manually.
+ */
+ triggerPullToRefresh: function() {
+ // Use publish instead of scrollTo to allow scrolling to out of boundary position
+ // We don't need to normalize scrollLeft, zoomLevel, etc. here because we only y-scrolling when pull-to-refresh is enabled
+ this.__publish(this.__scrollLeft, -this.__refreshHeight, this.__zoomLevel, true);
+
+ var d = new Date();
+ self.refreshStartTime = d.getTime();
+
+ if (this.__refreshStart) {
+ this.__refreshStart();
+ }
+ },
+
+
+ /**
+ * Signalizes that pull-to-refresh is finished.
+ */
+ finishPullToRefresh: function() {
+
+ var self = this;
+ // delay to make sure the spinner has a chance to spin for a split second before it's dismissed
+ var d = new Date();
+ var delay = 0;
+ if(self.refreshStartTime + self.__minSpinTime > d.getTime()){
+ delay = self.refreshStartTime + self.__minSpinTime - d.getTime();
+ }
+ setTimeout(function(){
+ if(self.__refreshTail){
+ self.__refreshTail();
+ }
+ setTimeout(function(){
+ self.__refreshActive = false;
+ if (self.__refreshDeactivate) {
+ self.__refreshDeactivate();
+ }
+
+ self.scrollTo(self.__scrollLeft, self.__scrollTop, true);
+ },self.__refreshTailTime);
+ },delay);
+ },
+
+
+ /**
+ * Returns the scroll position and zooming values
+ *
+ * @return {Map} `left` and `top` scroll position and `zoom` level
+ */
+ getValues: function() {
+
+ var self = this;
+
+ return {
+ left: self.__scrollLeft,
+ top: self.__scrollTop,
+ zoom: self.__zoomLevel
+ };
+
+ },
+
+
+ /**
+ * Returns the maximum scroll values
+ *
+ * @return {Map} `left` and `top` maximum scroll values
+ */
+ getScrollMax: function() {
+
+ var self = this;
+
+ return {
+ left: self.__maxScrollLeft,
+ top: self.__maxScrollTop
+ };
+
+ },
+
+
+ /**
+ * Zooms to the given level. Supports optional animation. Zooms
+ * the center when no coordinates are given.
+ *
+ * @param level {Number} Level to zoom to
+ * @param animate {Boolean} Whether to use animation
+ * @param originLeft {Number} Zoom in at given left coordinate
+ * @param originTop {Number} Zoom in at given top coordinate
+ */
+ zoomTo: function(level, animate, originLeft, originTop) {
+
+ var self = this;
+
+ if (!self.options.zooming) {
+ throw new Error("Zooming is not enabled!");
+ }
+
+ // Stop deceleration
+ if (self.__isDecelerating) {
+ zyngaCore.effect.Animate.stop(self.__isDecelerating);
+ self.__isDecelerating = false;
+ }
+
+ var oldLevel = self.__zoomLevel;
+
+ // Normalize input origin to center of viewport if not defined
+ if (originLeft == null) {
+ originLeft = self.__clientWidth / 2;
+ }
+
+ if (originTop == null) {
+ originTop = self.__clientHeight / 2;
+ }
+
+ // Limit level according to configuration
+ level = Math.max(Math.min(level, self.options.maxZoom), self.options.minZoom);
+
+ // Recompute maximum values while temporary tweaking maximum scroll ranges
+ self.__computeScrollMax(level);
+
+ // Recompute left and top coordinates based on new zoom level
+ var left = ((originLeft + self.__scrollLeft) * level / oldLevel) - originLeft;
+ var top = ((originTop + self.__scrollTop) * level / oldLevel) - originTop;
+
+ // Limit x-axis
+ if (left > self.__maxScrollLeft) {
+ left = self.__maxScrollLeft;
+ } else if (left < 0) {
+ left = 0;
+ }
+
+ // Limit y-axis
+ if (top > self.__maxScrollTop) {
+ top = self.__maxScrollTop;
+ } else if (top < 0) {
+ top = 0;
+ }
+
+ // Push values out
+ self.__publish(left, top, level, animate);
+
+ },
+
+
+ /**
+ * Zooms the content by the given factor.
+ *
+ * @param factor {Number} Zoom by given factor
+ * @param animate {Boolean} Whether to use animation
+ * @param originLeft {Number} Zoom in at given left coordinate
+ * @param originTop {Number} Zoom in at given top coordinate
+ */
+ zoomBy: function(factor, animate, originLeft, originTop) {
+
+ var self = this;
+
+ self.zoomTo(self.__zoomLevel * factor, animate, originLeft, originTop);
+
+ },
+
+
+ /**
+ * Scrolls to the given position. Respect limitations and snapping automatically.
+ *
+ * @param left {Number} Horizontal scroll position, keeps current if value is null
+ * @param top {Number} Vertical scroll position, keeps current if value is null
+ * @param animate {Boolean} Whether the scrolling should happen using an animation
+ * @param zoom {Number} Zoom level to go to
+ */
+ scrollTo: function(left, top, animate, zoom, wasResize) {
+ var self = this;
+
+ // Stop deceleration
+ if (self.__isDecelerating) {
+ zyngaCore.effect.Animate.stop(self.__isDecelerating);
+ self.__isDecelerating = false;
+ }
+
+ // Correct coordinates based on new zoom level
+ if (zoom != null && zoom !== self.__zoomLevel) {
+
+ if (!self.options.zooming) {
+ throw new Error("Zooming is not enabled!");
+ }
+
+ left *= zoom;
+ top *= zoom;
+
+ // Recompute maximum values while temporary tweaking maximum scroll ranges
+ self.__computeScrollMax(zoom);
+
+ } else {
+
+ // Keep zoom when not defined
+ zoom = self.__zoomLevel;
+
+ }
+
+ if (!self.options.scrollingX) {
+
+ left = self.__scrollLeft;
+
+ } else {
+
+ if (self.options.paging) {
+ left = Math.round(left / self.__clientWidth) * self.__clientWidth;
+ } else if (self.options.snapping) {
+ left = Math.round(left / self.__snapWidth) * self.__snapWidth;
+ }
+
+ }
+
+ if (!self.options.scrollingY) {
+
+ top = self.__scrollTop;
+
+ } else {
+
+ if (self.options.paging) {
+ top = Math.round(top / self.__clientHeight) * self.__clientHeight;
+ } else if (self.options.snapping) {
+ top = Math.round(top / self.__snapHeight) * self.__snapHeight;
+ }
+
+ }
+
+ // Limit for allowed ranges
+ left = Math.max(Math.min(self.__maxScrollLeft, left), 0);
+ top = Math.max(Math.min(self.__maxScrollTop, top), 0);
+
+ // Don't animate when no change detected, still call publish to make sure
+ // that rendered position is really in-sync with internal data
+ if (left === self.__scrollLeft && top === self.__scrollTop) {
+ animate = false;
+ }
+
+ // Publish new values
+ self.__publish(left, top, zoom, animate, wasResize);
+
+ },
+
+
+ /**
+ * Scroll by the given offset
+ *
+ * @param left {Number} Scroll x-axis by given offset
+ * @param top {Number} Scroll y-axis by given offset
+ * @param animate {Boolean} Whether to animate the given change
+ */
+ scrollBy: function(left, top, animate) {
+
+ var self = this;
+
+ var startLeft = self.__isAnimating ? self.__scheduledLeft : self.__scrollLeft;
+ var startTop = self.__isAnimating ? self.__scheduledTop : self.__scrollTop;
+
+ self.scrollTo(startLeft + (left || 0), startTop + (top || 0), animate);
+
+ },
+
+
+
+ /*
+ ---------------------------------------------------------------------------
+ EVENT CALLBACKS
+ ---------------------------------------------------------------------------
+ */
+
+ /**
+ * Mouse wheel handler for zooming support
+ */
+ doMouseZoom: function(wheelDelta, timeStamp, pageX, pageY) {
+
+ var self = this;
+ var change = wheelDelta > 0 ? 0.97 : 1.03;
+
+ return self.zoomTo(self.__zoomLevel * change, false, pageX - self.__clientLeft, pageY - self.__clientTop);
+
+ },
+
+ /**
+ * Touch start handler for scrolling support
+ */
+ doTouchStart: function(touches, timeStamp) {
+ this.hintResize();
+
+ if (timeStamp instanceof Date) {
+ timeStamp = timeStamp.valueOf();
+ }
+ if (typeof timeStamp !== "number") {
+ timeStamp = Date.now();
+ }
+
+ var self = this;
+
+ // Reset interruptedAnimation flag
+ self.__interruptedAnimation = true;
+
+ // Stop deceleration
+ if (self.__isDecelerating) {
+ zyngaCore.effect.Animate.stop(self.__isDecelerating);
+ self.__isDecelerating = false;
+ self.__interruptedAnimation = true;
+ }
+
+ // Stop animation
+ if (self.__isAnimating) {
+ zyngaCore.effect.Animate.stop(self.__isAnimating);
+ self.__isAnimating = false;
+ self.__interruptedAnimation = true;
+ }
+
+ // Use center point when dealing with two fingers
+ var currentTouchLeft, currentTouchTop;
+ var isSingleTouch = touches.length === 1;
+ if (isSingleTouch) {
+ currentTouchLeft = touches[0].pageX;
+ currentTouchTop = touches[0].pageY;
+ } else {
+ currentTouchLeft = Math.abs(touches[0].pageX + touches[1].pageX) / 2;
+ currentTouchTop = Math.abs(touches[0].pageY + touches[1].pageY) / 2;
+ }
+
+ // Store initial positions
+ self.__initialTouchLeft = currentTouchLeft;
+ self.__initialTouchTop = currentTouchTop;
+
+ // Store initial touchList for scale calculation
+ self.__initialTouches = touches;
+
+ // Store current zoom level
+ self.__zoomLevelStart = self.__zoomLevel;
+
+ // Store initial touch positions
+ self.__lastTouchLeft = currentTouchLeft;
+ self.__lastTouchTop = currentTouchTop;
+
+ // Store initial move time stamp
+ self.__lastTouchMove = timeStamp;
+
+ // Reset initial scale
+ self.__lastScale = 1;
+
+ // Reset locking flags
+ self.__enableScrollX = !isSingleTouch && self.options.scrollingX;
+ self.__enableScrollY = !isSingleTouch && self.options.scrollingY;
+
+ // Reset tracking flag
+ self.__isTracking = true;
+
+ // Reset deceleration complete flag
+ self.__didDecelerationComplete = false;
+
+ // Dragging starts directly with two fingers, otherwise lazy with an offset
+ self.__isDragging = !isSingleTouch;
+
+ // Some features are disabled in multi touch scenarios
+ self.__isSingleTouch = isSingleTouch;
+
+ // Clearing data structure
+ self.__positions = [];
+
+ },
+
+
+ /**
+ * Touch move handler for scrolling support
+ */
+ doTouchMove: function(touches, timeStamp, scale) {
+ if (timeStamp instanceof Date) {
+ timeStamp = timeStamp.valueOf();
+ }
+ if (typeof timeStamp !== "number") {
+ timeStamp = Date.now();
+ }
+
+ var self = this;
+
+ // Ignore event when tracking is not enabled (event might be outside of element)
+ if (!self.__isTracking) {
+ return;
+ }
+
+ var currentTouchLeft, currentTouchTop;
+
+ // Compute move based around of center of fingers
+ if (touches.length === 2) {
+ currentTouchLeft = Math.abs(touches[0].pageX + touches[1].pageX) / 2;
+ currentTouchTop = Math.abs(touches[0].pageY + touches[1].pageY) / 2;
+
+ // Calculate scale when not present and only when touches are used
+ if (!scale && self.options.zooming) {
+ scale = self.__getScale(self.__initialTouches, touches);
+ }
+ } else {
+ currentTouchLeft = touches[0].pageX;
+ currentTouchTop = touches[0].pageY;
+ }
+
+ var positions = self.__positions;
+
+ // Are we already is dragging mode?
+ if (self.__isDragging) {
+
+ // Compute move distance
+ var moveX = currentTouchLeft - self.__lastTouchLeft;
+ var moveY = currentTouchTop - self.__lastTouchTop;
+
+ // Read previous scroll position and zooming
+ var scrollLeft = self.__scrollLeft;
+ var scrollTop = self.__scrollTop;
+ var level = self.__zoomLevel;
+
+ // Work with scaling
+ if (scale != null && self.options.zooming) {
+
+ var oldLevel = level;
+
+ // Recompute level based on previous scale and new scale
+ level = level / self.__lastScale * scale;
+
+ // Limit level according to configuration
+ level = Math.max(Math.min(level, self.options.maxZoom), self.options.minZoom);
+
+ // Only do further compution when change happened
+ if (oldLevel !== level) {
+
+ // Compute relative event position to container
+ var currentTouchLeftRel = currentTouchLeft - self.__clientLeft;
+ var currentTouchTopRel = currentTouchTop - self.__clientTop;
+
+ // Recompute left and top coordinates based on new zoom level
+ scrollLeft = ((currentTouchLeftRel + scrollLeft) * level / oldLevel) - currentTouchLeftRel;
+ scrollTop = ((currentTouchTopRel + scrollTop) * level / oldLevel) - currentTouchTopRel;
+
+ // Recompute max scroll values
+ self.__computeScrollMax(level);
+
+ }
+ }
+
+ if (self.__enableScrollX) {
+
+ scrollLeft -= moveX * this.options.speedMultiplier;
+ var maxScrollLeft = self.__maxScrollLeft;
+
+ if (scrollLeft > maxScrollLeft || scrollLeft < 0) {
+
+ // Slow down on the edges
+ if (self.options.bouncing) {
+
+ scrollLeft += (moveX / 2 * this.options.speedMultiplier);
+
+ } else if (scrollLeft > maxScrollLeft) {
+
+ scrollLeft = maxScrollLeft;
+
+ } else {
+
+ scrollLeft = 0;
+
+ }
+ }
+ }
+
+ // Compute new vertical scroll position
+ if (self.__enableScrollY) {
+
+ scrollTop -= moveY * this.options.speedMultiplier;
+ var maxScrollTop = self.__maxScrollTop;
+
+ if (scrollTop > maxScrollTop || scrollTop < 0) {
+
+ // Slow down on the edges
+ if (self.options.bouncing || (self.__refreshHeight && scrollTop < 0)) {
+
+ scrollTop += (moveY / 2 * this.options.speedMultiplier);
+
+ // Support pull-to-refresh (only when only y is scrollable)
+ if (!self.__enableScrollX && self.__refreshHeight != null) {
+
+ // hide the refresher when it's behind the header bar in case of header transparency
+ if(scrollTop < 0){
+ self.__refreshHidden = false;
+ self.__refreshShow();
+ }else{
+ self.__refreshHide();
+ self.__refreshHidden = true;
+ }
+
+ if (!self.__refreshActive && scrollTop <= -self.__refreshHeight) {
+
+ self.__refreshActive = true;
+ if (self.__refreshActivate) {
+ self.__refreshActivate();
+ }
+
+ } else if (self.__refreshActive && scrollTop > -self.__refreshHeight) {
+
+ self.__refreshActive = false;
+ if (self.__refreshDeactivate) {
+ self.__refreshDeactivate();
+ }
+
+ }
+ }
+
+ } else if (scrollTop > maxScrollTop) {
+
+ scrollTop = maxScrollTop;
+
+ } else {
+
+ scrollTop = 0;
+
+ }
+ }else if(self.__refreshHeight && !self.__refreshHidden){
+ // if a positive scroll value and the refresher is still not hidden, hide it
+ self.__refreshHide();
+ self.__refreshHidden = true;
+ }
+ }
+
+ // Keep list from growing infinitely (holding min 10, max 20 measure points)
+ if (positions.length > 60) {
+ positions.splice(0, 30);
+ }
+
+ // Track scroll movement for decleration
+ positions.push(scrollLeft, scrollTop, timeStamp);
+
+ // Sync scroll position
+ self.__publish(scrollLeft, scrollTop, level);
+
+ // Otherwise figure out whether we are switching into dragging mode now.
+ } else {
+
+ var minimumTrackingForScroll = self.options.locking ? 3 : 0;
+ var minimumTrackingForDrag = 5;
+
+ var distanceX = Math.abs(currentTouchLeft - self.__initialTouchLeft);
+ var distanceY = Math.abs(currentTouchTop - self.__initialTouchTop);
+
+ self.__enableScrollX = self.options.scrollingX && distanceX >= minimumTrackingForScroll;
+ self.__enableScrollY = self.options.scrollingY && distanceY >= minimumTrackingForScroll;
+
+ positions.push(self.__scrollLeft, self.__scrollTop, timeStamp);
+
+ self.__isDragging = (self.__enableScrollX || self.__enableScrollY) && (distanceX >= minimumTrackingForDrag || distanceY >= minimumTrackingForDrag);
+ if (self.__isDragging) {
+ self.__interruptedAnimation = false;
+ self.__fadeScrollbars('in');
+ }
+
+ }
+
+ // Update last touch positions and time stamp for next event
+ self.__lastTouchLeft = currentTouchLeft;
+ self.__lastTouchTop = currentTouchTop;
+ self.__lastTouchMove = timeStamp;
+ self.__lastScale = scale;
+
+ },
+
+
+ /**
+ * Touch end handler for scrolling support
+ */
+ doTouchEnd: function(timeStamp) {
+ if (timeStamp instanceof Date) {
+ timeStamp = timeStamp.valueOf();
+ }
+ if (typeof timeStamp !== "number") {
+ timeStamp = Date.now();
+ }
+
+ var self = this;
+
+ // Ignore event when tracking is not enabled (no touchstart event on element)
+ // This is required as this listener ('touchmove') sits on the document and not on the element itself.
+ if (!self.__isTracking) {
+ return;
+ }
+
+ // Not touching anymore (when two finger hit the screen there are two touch end events)
+ self.__isTracking = false;
+
+ // Be sure to reset the dragging flag now. Here we also detect whether
+ // the finger has moved fast enough to switch into a deceleration animation.
+ if (self.__isDragging) {
+
+ // Reset dragging flag
+ self.__isDragging = false;
+
+ // Start deceleration
+ // Verify that the last move detected was in some relevant time frame
+ if (self.__isSingleTouch && self.options.animating && (timeStamp - self.__lastTouchMove) <= 100) {
+
+ // Then figure out what the scroll position was about 100ms ago
+ var positions = self.__positions;
+ var endPos = positions.length - 1;
+ var startPos = endPos;
+
+ // Move pointer to position measured 100ms ago
+ for (var i = endPos; i > 0 && positions[i] > (self.__lastTouchMove - 100); i -= 3) {
+ startPos = i;
+ }
+
+ // If start and stop position is identical in a 100ms timeframe,
+ // we cannot compute any useful deceleration.
+ if (startPos !== endPos) {
+
+ // Compute relative movement between these two points
+ var timeOffset = positions[endPos] - positions[startPos];
+ var movedLeft = self.__scrollLeft - positions[startPos - 2];
+ var movedTop = self.__scrollTop - positions[startPos - 1];
+
+ // Based on 50ms compute the movement to apply for each render step
+ self.__decelerationVelocityX = movedLeft / timeOffset * (1000 / 60);
+ self.__decelerationVelocityY = movedTop / timeOffset * (1000 / 60);
+
+ // How much velocity is required to start the deceleration
+ var minVelocityToStartDeceleration = self.options.paging || self.options.snapping ? 4 : 1;
+
+ // Verify that we have enough velocity to start deceleration
+ if (Math.abs(self.__decelerationVelocityX) > minVelocityToStartDeceleration || Math.abs(self.__decelerationVelocityY) > minVelocityToStartDeceleration) {
+
+ // Deactivate pull-to-refresh when decelerating
+ if (!self.__refreshActive) {
+ self.__startDeceleration(timeStamp);
+ }
+ }
+ } else {
+ self.__scrollingComplete();
+ }
+ } else if ((timeStamp - self.__lastTouchMove) > 100) {
+ self.__scrollingComplete();
+ }
+ }
+
+ // If this was a slower move it is per default non decelerated, but this
+ // still means that we want snap back to the bounds which is done here.
+ // This is placed outside the condition above to improve edge case stability
+ // e.g. touchend fired without enabled dragging. This should normally do not
+ // have modified the scroll positions or even showed the scrollbars though.
+ if (!self.__isDecelerating) {
+
+ if (self.__refreshActive && self.__refreshStart) {
+
+ // Use publish instead of scrollTo to allow scrolling to out of boundary position
+ // We don't need to normalize scrollLeft, zoomLevel, etc. here because we only y-scrolling when pull-to-refresh is enabled
+ self.__publish(self.__scrollLeft, -self.__refreshHeight, self.__zoomLevel, true);
+
+ var d = new Date();
+ self.refreshStartTime = d.getTime();
+
+ if (self.__refreshStart) {
+ self.__refreshStart();
+ }
+ // for iOS-ey style scrolling
+ if(!ionic.Platform.isAndroid())self.__startDeceleration();
+ } else {
+
+ if (self.__interruptedAnimation || self.__isDragging) {
+ self.__scrollingComplete();
+ }
+ self.scrollTo(self.__scrollLeft, self.__scrollTop, true, self.__zoomLevel);
+
+ // Directly signalize deactivation (nothing todo on refresh?)
+ if (self.__refreshActive) {
+
+ self.__refreshActive = false;
+ if (self.__refreshDeactivate) {
+ self.__refreshDeactivate();
+ }
+
+ }
+ }
+ }
+
+ // Fully cleanup list
+ self.__positions.length = 0;
+
+ },
+
+
+
+ /*
+ ---------------------------------------------------------------------------
+ PRIVATE API
+ ---------------------------------------------------------------------------
+ */
+
+ /**
+ * Applies the scroll position to the content element
+ *
+ * @param left {Number} Left scroll position
+ * @param top {Number} Top scroll position
+ * @param animate {Boolean} Whether animation should be used to move to the new coordinates
+ */
+ __publish: function(left, top, zoom, animate, wasResize) {
+
+ var self = this;
+
+ // Remember whether we had an animation, then we try to continue based on the current "drive" of the animation
+ var wasAnimating = self.__isAnimating;
+ if (wasAnimating) {
+ zyngaCore.effect.Animate.stop(wasAnimating);
+ self.__isAnimating = false;
+ }
+
+ if (animate && self.options.animating) {
+
+ // Keep scheduled positions for scrollBy/zoomBy functionality
+ self.__scheduledLeft = left;
+ self.__scheduledTop = top;
+ self.__scheduledZoom = zoom;
+
+ var oldLeft = self.__scrollLeft;
+ var oldTop = self.__scrollTop;
+ var oldZoom = self.__zoomLevel;
+
+ var diffLeft = left - oldLeft;
+ var diffTop = top - oldTop;
+ var diffZoom = zoom - oldZoom;
+
+ var step = function(percent, now, render) {
+
+ if (render) {
+
+ self.__scrollLeft = oldLeft + (diffLeft * percent);
+ self.__scrollTop = oldTop + (diffTop * percent);
+ self.__zoomLevel = oldZoom + (diffZoom * percent);
+
+ // Push values out
+ if (self.__callback) {
+ self.__callback(self.__scrollLeft, self.__scrollTop, self.__zoomLevel, wasResize);
+ }
+
+ }
+ };
+
+ var verify = function(id) {
+ return self.__isAnimating === id;
+ };
+
+ var completed = function(renderedFramesPerSecond, animationId, wasFinished) {
+ if (animationId === self.__isAnimating) {
+ self.__isAnimating = false;
+ }
+ if (self.__didDecelerationComplete || wasFinished) {
+ self.__scrollingComplete();
+ }
+
+ if (self.options.zooming) {
+ self.__computeScrollMax();
+ }
+ };
+
+ // When continuing based on previous animation we choose an ease-out animation instead of ease-in-out
+ self.__isAnimating = zyngaCore.effect.Animate.start(step, verify, completed, self.options.animationDuration, wasAnimating ? easeOutCubic : easeInOutCubic);
+
+ } else {
+
+ self.__scheduledLeft = self.__scrollLeft = left;
+ self.__scheduledTop = self.__scrollTop = top;
+ self.__scheduledZoom = self.__zoomLevel = zoom;
+
+ // Push values out
+ if (self.__callback) {
+ self.__callback(left, top, zoom, wasResize);
+ }
+
+ // Fix max scroll ranges
+ if (self.options.zooming) {
+ self.__computeScrollMax();
+ }
+ }
+ },
+
+
+ /**
+ * Recomputes scroll minimum values based on client dimensions and content dimensions.
+ */
+ __computeScrollMax: function(zoomLevel) {
+
+ var self = this;
+
+ if (zoomLevel == null) {
+ zoomLevel = self.__zoomLevel;
+ }
+
+ self.__maxScrollLeft = Math.max((self.__contentWidth * zoomLevel) - self.__clientWidth, 0);
+ self.__maxScrollTop = Math.max((self.__contentHeight * zoomLevel) - self.__clientHeight, 0);
+
+ if(!self.__didWaitForSize && !self.__maxScrollLeft && !self.__maxScrollTop) {
+ self.__didWaitForSize = true;
+ self.__waitForSize();
+ }
+ },
+
+
+ /**
+ * If the scroll view isn't sized correctly on start, wait until we have at least some size
+ */
+ __waitForSize: function() {
+
+ var self = this;
+
+ clearTimeout(self.__sizerTimeout);
+
+ var sizer = function() {
+ self.resize();
+
+ if((self.options.scrollingX && !self.__maxScrollLeft) || (self.options.scrollingY && !self.__maxScrollTop)) {
+ //self.__sizerTimeout = setTimeout(sizer, 1000);
+ }
+ };
+
+ sizer();
+ self.__sizerTimeout = setTimeout(sizer, 1000);
+ },
+
+ /*
+ ---------------------------------------------------------------------------
+ ANIMATION (DECELERATION) SUPPORT
+ ---------------------------------------------------------------------------
+ */
+
+ /**
+ * Called when a touch sequence end and the speed of the finger was high enough
+ * to switch into deceleration mode.
+ */
+ __startDeceleration: function(timeStamp) {
+
+ var self = this;
+
+ if (self.options.paging) {
+
+ var scrollLeft = Math.max(Math.min(self.__scrollLeft, self.__maxScrollLeft), 0);
+ var scrollTop = Math.max(Math.min(self.__scrollTop, self.__maxScrollTop), 0);
+ var clientWidth = self.__clientWidth;
+ var clientHeight = self.__clientHeight;
+
+ // We limit deceleration not to the min/max values of the allowed range, but to the size of the visible client area.
+ // Each page should have exactly the size of the client area.
+ self.__minDecelerationScrollLeft = Math.floor(scrollLeft / clientWidth) * clientWidth;
+ self.__minDecelerationScrollTop = Math.floor(scrollTop / clientHeight) * clientHeight;
+ self.__maxDecelerationScrollLeft = Math.ceil(scrollLeft / clientWidth) * clientWidth;
+ self.__maxDecelerationScrollTop = Math.ceil(scrollTop / clientHeight) * clientHeight;
+
+ } else {
+
+ self.__minDecelerationScrollLeft = 0;
+ self.__minDecelerationScrollTop = 0;
+ self.__maxDecelerationScrollLeft = self.__maxScrollLeft;
+ self.__maxDecelerationScrollTop = self.__maxScrollTop;
+ if(self.__refreshActive) self.__minDecelerationScrollTop = self.__refreshHeight *-1;
+ }
+
+ // Wrap class method
+ var step = function(percent, now, render) {
+ self.__stepThroughDeceleration(render);
+ };
+
+ // How much velocity is required to keep the deceleration running
+ self.__minVelocityToKeepDecelerating = self.options.snapping ? 4 : 0.1;
+
+ // Detect whether it's still worth to continue animating steps
+ // If we are already slow enough to not being user perceivable anymore, we stop the whole process here.
+ var verify = function() {
+ var shouldContinue = Math.abs(self.__decelerationVelocityX) >= self.__minVelocityToKeepDecelerating ||
+ Math.abs(self.__decelerationVelocityY) >= self.__minVelocityToKeepDecelerating;
+ if (!shouldContinue) {
+ self.__didDecelerationComplete = true;
+
+ //Make sure the scroll values are within the boundaries after a bounce,
+ //not below 0 or above maximum
+ if (self.options.bouncing && !self.__refreshActive) {
+ self.scrollTo(
+ Math.min( Math.max(self.__scrollLeft, 0), self.__maxScrollLeft ),
+ Math.min( Math.max(self.__scrollTop, 0), self.__maxScrollTop ),
+ self.__refreshActive
+ );
+ }
+ }
+ return shouldContinue;
+ };
+
+ var completed = function(renderedFramesPerSecond, animationId, wasFinished) {
+ self.__isDecelerating = false;
+ if (self.__didDecelerationComplete) {
+ self.__scrollingComplete();
+ }
+
+ // Animate to grid when snapping is active, otherwise just fix out-of-boundary positions
+ if(self.options.paging) {
+ self.scrollTo(self.__scrollLeft, self.__scrollTop, self.options.snapping);
+ }
+ };
+
+ // Start animation and switch on flag
+ self.__isDecelerating = zyngaCore.effect.Animate.start(step, verify, completed);
+
+ },
+
+
+ /**
+ * Called on every step of the animation
+ *
+ * @param inMemory {Boolean} Whether to not render the current step, but keep it in memory only. Used internally only!
+ */
+ __stepThroughDeceleration: function(render) {
+
+ var self = this;
+
+
+ //
+ // COMPUTE NEXT SCROLL POSITION
+ //
+
+ // Add deceleration to scroll position
+ var scrollLeft = self.__scrollLeft + self.__decelerationVelocityX;// * self.options.deceleration);
+ var scrollTop = self.__scrollTop + self.__decelerationVelocityY;// * self.options.deceleration);
+
+
+ //
+ // HARD LIMIT SCROLL POSITION FOR NON BOUNCING MODE
+ //
+
+ if (!self.options.bouncing) {
+
+ var scrollLeftFixed = Math.max(Math.min(self.__maxDecelerationScrollLeft, scrollLeft), self.__minDecelerationScrollLeft);
+ if (scrollLeftFixed !== scrollLeft) {
+ scrollLeft = scrollLeftFixed;
+ self.__decelerationVelocityX = 0;
+ }
+
+ var scrollTopFixed = Math.max(Math.min(self.__maxDecelerationScrollTop, scrollTop), self.__minDecelerationScrollTop);
+ if (scrollTopFixed !== scrollTop) {
+ scrollTop = scrollTopFixed;
+ self.__decelerationVelocityY = 0;
+ }
+
+ }
+
+
+ //
+ // UPDATE SCROLL POSITION
+ //
+
+ if (render) {
+
+ self.__publish(scrollLeft, scrollTop, self.__zoomLevel);
+
+ } else {
+
+ self.__scrollLeft = scrollLeft;
+ self.__scrollTop = scrollTop;
+
+ }
+
+
+ //
+ // SLOW DOWN
+ //
+
+ // Slow down velocity on every iteration
+ if (!self.options.paging) {
+
+ // This is the factor applied to every iteration of the animation
+ // to slow down the process. This should emulate natural behavior where
+ // objects slow down when the initiator of the movement is removed
+ var frictionFactor = self.options.deceleration;
+
+ self.__decelerationVelocityX *= frictionFactor;
+ self.__decelerationVelocityY *= frictionFactor;
+
+ }
+
+
+ //
+ // BOUNCING SUPPORT
+ //
+
+ if (self.options.bouncing) {
+
+ var scrollOutsideX = 0;
+ var scrollOutsideY = 0;
+
+ // This configures the amount of change applied to deceleration/acceleration when reaching boundaries
+ var penetrationDeceleration = self.options.penetrationDeceleration;
+ var penetrationAcceleration = self.options.penetrationAcceleration;
+
+ // Check limits
+ if (scrollLeft < self.__minDecelerationScrollLeft) {
+ scrollOutsideX = self.__minDecelerationScrollLeft - scrollLeft;
+ } else if (scrollLeft > self.__maxDecelerationScrollLeft) {
+ scrollOutsideX = self.__maxDecelerationScrollLeft - scrollLeft;
+ }
+
+ if (scrollTop < self.__minDecelerationScrollTop) {
+ scrollOutsideY = self.__minDecelerationScrollTop - scrollTop;
+ } else if (scrollTop > self.__maxDecelerationScrollTop) {
+ scrollOutsideY = self.__maxDecelerationScrollTop - scrollTop;
+ }
+
+ // Slow down until slow enough, then flip back to snap position
+ if (scrollOutsideX !== 0) {
+ var isHeadingOutwardsX = scrollOutsideX * self.__decelerationVelocityX <= self.__minDecelerationScrollLeft;
+ if (isHeadingOutwardsX) {
+ self.__decelerationVelocityX += scrollOutsideX * penetrationDeceleration;
+ }
+ var isStoppedX = Math.abs(self.__decelerationVelocityX) <= self.__minVelocityToKeepDecelerating;
+ //If we're not heading outwards, or if the above statement got us below minDeceleration, go back towards bounds
+ if (!isHeadingOutwardsX || isStoppedX) {
+ self.__decelerationVelocityX = scrollOutsideX * penetrationAcceleration;
+ }
+ }
+
+ if (scrollOutsideY !== 0) {
+ var isHeadingOutwardsY = scrollOutsideY * self.__decelerationVelocityY <= self.__minDecelerationScrollTop;
+ if (isHeadingOutwardsY) {
+ self.__decelerationVelocityY += scrollOutsideY * penetrationDeceleration;
+ }
+ var isStoppedY = Math.abs(self.__decelerationVelocityY) <= self.__minVelocityToKeepDecelerating;
+ //If we're not heading outwards, or if the above statement got us below minDeceleration, go back towards bounds
+ if (!isHeadingOutwardsY || isStoppedY) {
+ self.__decelerationVelocityY = scrollOutsideY * penetrationAcceleration;
+ }
+ }
+ }
+ },
+
+
+ /**
+ * calculate the distance between two touches
+ * @param {Touch} touch1
+ * @param {Touch} touch2
+ * @returns {Number} distance
+ */
+ __getDistance: function getDistance(touch1, touch2) {
+ var x = touch2.pageX - touch1.pageX,
+ y = touch2.pageY - touch1.pageY;
+ return Math.sqrt((x*x) + (y*y));
+ },
+
+
+ /**
+ * calculate the scale factor between two touchLists (fingers)
+ * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
+ * @param {Array} start
+ * @param {Array} end
+ * @returns {Number} scale
+ */
+ __getScale: function getScale(start, end) {
+
+ var self = this;
+
+ // need two fingers...
+ if(start.length >= 2 && end.length >= 2) {
+ return self.__getDistance(end[0], end[1]) /
+ self.__getDistance(start[0], start[1]);
+ }
+ return 1;
+ }
+});
+
+ionic.scroll = {
+ isScrolling: false,
+ lastTop: 0
+};
+
+})(ionic);
+
+(function(ionic) {
+'use strict';
+
+ ionic.views.HeaderBar = ionic.views.View.inherit({
+ initialize: function(opts) {
+ this.el = opts.el;
+
+ ionic.extend(this, {
+ alignTitle: 'center'
+ }, opts);
+
+ this.align();
+ },
+
+ align: function(align) {
+
+ align || (align = this.alignTitle);
+
+ // Find the titleEl element
+ var titleEl = this.el.querySelector('.title');
+ if(!titleEl) {
+ return;
+ }
+
+ var self = this;
+ //We have to rAF here so all of the elements have time to initialize
+ ionic.requestAnimationFrame(function() {
+ var i, c, childSize;
+ var childNodes = self.el.childNodes;
+ var leftWidth = 0;
+ var rightWidth = 0;
+ var isCountingRightWidth = false;
+
+ // Compute how wide the left children are
+ // Skip all titles (there may still be two titles, one leaving the dom)
+ // Once we encounter a titleEl, realize we are now counting the right-buttons, not left
+ for(i = 0; i < childNodes.length; i++) {
+ c = childNodes[i];
+ if (c.tagName && c.tagName.toLowerCase() == 'h1') {
+ isCountingRightWidth = true;
+ continue;
+ }
+
+ childSize = null;
+ if(c.nodeType == 3) {
+ var bounds = ionic.DomUtil.getTextBounds(c);
+ if(bounds) {
+ childSize = bounds.width;
+ }
+ } else if(c.nodeType == 1) {
+ childSize = c.offsetWidth;
+ }
+ if(childSize) {
+ if (isCountingRightWidth) {
+ rightWidth += childSize;
+ } else {
+ leftWidth += childSize;
+ }
+ }
+ }
+
+ var margin = Math.max(leftWidth, rightWidth) + 10;
+
+ //Reset left and right before setting again
+ titleEl.style.left = titleEl.style.right = '';
+
+ // Size and align the header titleEl based on the sizes of the left and
+ // right children, and the desired alignment mode
+ if(align == 'center') {
+ if(margin > 10) {
+ titleEl.style.left = margin + 'px';
+ titleEl.style.right = margin + 'px';
+ }
+ if(titleEl.offsetWidth < titleEl.scrollWidth) {
+ if(rightWidth > 0) {
+ titleEl.style.right = (rightWidth + 5) + 'px';
+ }
+ }
+ } else if(align == 'left') {
+ titleEl.classList.add('title-left');
+ if(leftWidth > 0) {
+ titleEl.style.left = (leftWidth + 15) + 'px';
+ }
+ } else if(align == 'right') {
+ titleEl.classList.add('title-right');
+ if(rightWidth > 0) {
+ titleEl.style.right = (rightWidth + 15) + 'px';
+ }
+ }
+ });
+ }
+ });
+
+})(ionic);
+
+(function(ionic) {
+'use strict';
+
+ var ITEM_CLASS = 'item';
+ var ITEM_CONTENT_CLASS = 'item-content';
+ var ITEM_SLIDING_CLASS = 'item-sliding';
+ var ITEM_OPTIONS_CLASS = 'item-options';
+ var ITEM_PLACEHOLDER_CLASS = 'item-placeholder';
+ var ITEM_REORDERING_CLASS = 'item-reordering';
+ var ITEM_REORDER_BTN_CLASS = 'item-reorder';
+
+ var DragOp = function() {};
+ DragOp.prototype = {
+ start: function(e) {
+ },
+ drag: function(e) {
+ },
+ end: function(e) {
+ },
+ isSameItem: function(item) {
+ return false;
+ }
+ };
+
+ var SlideDrag = function(opts) {
+ this.dragThresholdX = opts.dragThresholdX || 10;
+ this.el = opts.el;
+ this.canSwipe = opts.canSwipe;
+ };
+
+ SlideDrag.prototype = new DragOp();
+
+ SlideDrag.prototype.start = function(e) {
+ var content, buttons, offsetX, buttonsWidth;
+
+ if (!this.canSwipe()) {
+ return;
+ }
+
+ if(e.target.classList.contains(ITEM_CONTENT_CLASS)) {
+ content = e.target;
+ } else if(e.target.classList.contains(ITEM_CLASS)) {
+ content = e.target.querySelector('.' + ITEM_CONTENT_CLASS);
+ } else {
+ content = ionic.DomUtil.getParentWithClass(e.target, ITEM_CONTENT_CLASS);
+ }
+
+ // If we don't have a content area as one of our children (or ourselves), skip
+ if(!content) {
+ return;
+ }
+
+ // Make sure we aren't animating as we slide
+ content.classList.remove(ITEM_SLIDING_CLASS);
+
+ // Grab the starting X point for the item (for example, so we can tell whether it is open or closed to start)
+ offsetX = parseFloat(content.style[ionic.CSS.TRANSFORM].replace('translate3d(', '').split(',')[0]) || 0;
+
+ // Grab the buttons
+ buttons = content.parentNode.querySelector('.' + ITEM_OPTIONS_CLASS);
+ if(!buttons) {
+ return;
+ }
+ buttons.classList.remove('invisible');
+
+ buttonsWidth = buttons.offsetWidth;
+
+ this._currentDrag = {
+ buttons: buttons,
+ buttonsWidth: buttonsWidth,
+ content: content,
+ startOffsetX: offsetX
+ };
+ };
+
+ /**
+ * Check if this is the same item that was previously dragged.
+ */
+ SlideDrag.prototype.isSameItem = function(op) {
+ if(op._lastDrag && this._currentDrag) {
+ return this._currentDrag.content == op._lastDrag.content;
+ }
+ return false;
+ };
+
+ SlideDrag.prototype.clean = function(e) {
+ var lastDrag = this._lastDrag;
+
+ if(!lastDrag) return;
+
+ lastDrag.content.style[ionic.CSS.TRANSITION] = '';
+ lastDrag.content.style[ionic.CSS.TRANSFORM] = '';
+ ionic.requestAnimationFrame(function() {
+ setTimeout(function() {
+ lastDrag.buttons && lastDrag.buttons.classList.add('invisible');
+ }, 250);
+ });
+ };
+
+ SlideDrag.prototype.drag = ionic.animationFrameThrottle(function(e) {
+ var buttonsWidth;
+
+ // We really aren't dragging
+ if(!this._currentDrag) {
+ return;
+ }
+
+ // Check if we should start dragging. Check if we've dragged past the threshold,
+ // or we are starting from the open state.
+ if(!this._isDragging &&
+ ((Math.abs(e.gesture.deltaX) > this.dragThresholdX) ||
+ (Math.abs(this._currentDrag.startOffsetX) > 0)))
+ {
+ this._isDragging = true;
+ }
+
+ if(this._isDragging) {
+ buttonsWidth = this._currentDrag.buttonsWidth;
+
+ // Grab the new X point, capping it at zero
+ var newX = Math.min(0, this._currentDrag.startOffsetX + e.gesture.deltaX);
+
+ // If the new X position is past the buttons, we need to slow down the drag (rubber band style)
+ if(newX < -buttonsWidth) {
+ // Calculate the new X position, capped at the top of the buttons
+ newX = Math.min(-buttonsWidth, -buttonsWidth + (((e.gesture.deltaX + buttonsWidth) * 0.4)));
+ }
+
+ this._currentDrag.content.style[ionic.CSS.TRANSFORM] = 'translate3d(' + newX + 'px, 0, 0)';
+ this._currentDrag.content.style[ionic.CSS.TRANSITION] = 'none';
+ }
+ });
+
+ SlideDrag.prototype.end = function(e, doneCallback) {
+ var _this = this;
+
+ // There is no drag, just end immediately
+ if(!this._currentDrag) {
+ doneCallback && doneCallback();
+ return;
+ }
+
+ // If we are currently dragging, we want to snap back into place
+ // The final resting point X will be the width of the exposed buttons
+ var restingPoint = -this._currentDrag.buttonsWidth;
+
+ // Check if the drag didn't clear the buttons mid-point
+ // and we aren't moving fast enough to swipe open
+ if(e.gesture.deltaX > -(this._currentDrag.buttonsWidth/2)) {
+
+ // If we are going left but too slow, or going right, go back to resting
+ if(e.gesture.direction == "left" && Math.abs(e.gesture.velocityX) < 0.3) {
+ restingPoint = 0;
+ } else if(e.gesture.direction == "right") {
+ restingPoint = 0;
+ }
+
+ }
+
+ ionic.requestAnimationFrame(function() {
+ if(restingPoint === 0) {
+ _this._currentDrag.content.style[ionic.CSS.TRANSFORM] = '';
+ var buttons = _this._currentDrag.buttons;
+ setTimeout(function() {
+ buttons && buttons.classList.add('invisible');
+ }, 250);
+ } else {
+ _this._currentDrag.content.style[ionic.CSS.TRANSFORM] = 'translate3d(' + restingPoint + 'px, 0, 0)';
+ }
+ _this._currentDrag.content.style[ionic.CSS.TRANSITION] = '';
+
+
+ // Kill the current drag
+ _this._lastDrag = _this._currentDrag;
+ _this._currentDrag = null;
+
+ // We are done, notify caller
+ doneCallback && doneCallback();
+ });
+ };
+
+ var ReorderDrag = function(opts) {
+ this.dragThresholdY = opts.dragThresholdY || 0;
+ this.onReorder = opts.onReorder;
+ this.listEl = opts.listEl;
+ this.el = opts.el;
+ this.scrollEl = opts.scrollEl;
+ this.scrollView = opts.scrollView;
+ // Get the True Top of the list el http://www.quirksmode.org/js/findpos.html
+ this.listElTrueTop = 0;
+ if (this.listEl.offsetParent) {
+ var obj = this.listEl;
+ do {
+ this.listElTrueTop += obj.offsetTop;
+ obj = obj.offsetParent;
+ } while (obj);
+ }
+ };
+
+ ReorderDrag.prototype = new DragOp();
+
+ ReorderDrag.prototype._moveElement = function(e) {
+ var y = e.gesture.center.pageY +
+ this.scrollView.getValues().top -
+ (this._currentDrag.elementHeight / 2) -
+ this.listElTrueTop;
+ this.el.style[ionic.CSS.TRANSFORM] = 'translate3d(0, '+y+'px, 0)';
+ };
+
+ ReorderDrag.prototype.start = function(e) {
+ var content;
+
+ var startIndex = ionic.DomUtil.getChildIndex(this.el, this.el.nodeName.toLowerCase());
+ var elementHeight = this.el.scrollHeight;
+ var placeholder = this.el.cloneNode(true);
+
+ placeholder.classList.add(ITEM_PLACEHOLDER_CLASS);
+
+ this.el.parentNode.insertBefore(placeholder, this.el);
+ this.el.classList.add(ITEM_REORDERING_CLASS);
+
+ this._currentDrag = {
+ elementHeight: elementHeight,
+ startIndex: startIndex,
+ placeholder: placeholder,
+ scrollHeight: scroll,
+ list: placeholder.parentNode
+ };
+
+ this._moveElement(e);
+ };
+
+ ReorderDrag.prototype.drag = ionic.animationFrameThrottle(function(e) {
+ // We really aren't dragging
+ var self = this;
+ if(!this._currentDrag) {
+ return;
+ }
+
+ var scrollY = 0;
+ var pageY = e.gesture.center.pageY;
+ var offset = this.listElTrueTop;
+
+ //If we have a scrollView, check scroll boundaries for dragged element and scroll if necessary
+ if (this.scrollView) {
+
+ var container = this.scrollView.__container;
+ scrollY = this.scrollView.getValues().top;
+
+ var containerTop = container.offsetTop;
+ var pixelsPastTop = containerTop - pageY + this._currentDrag.elementHeight/2;
+ var pixelsPastBottom = pageY + this._currentDrag.elementHeight/2 - containerTop - container.offsetHeight;
+
+ if (e.gesture.deltaY < 0 && pixelsPastTop > 0 && scrollY > 0) {
+ this.scrollView.scrollBy(null, -pixelsPastTop);
+ //Trigger another drag so the scrolling keeps going
+ ionic.requestAnimationFrame(function() {
+ self.drag(e);
+ });
+ }
+ if (e.gesture.deltaY > 0 && pixelsPastBottom > 0) {
+ if (scrollY < this.scrollView.getScrollMax().top) {
+ this.scrollView.scrollBy(null, pixelsPastBottom);
+ //Trigger another drag so the scrolling keeps going
+ ionic.requestAnimationFrame(function() {
+ self.drag(e);
+ });
+ }
+ }
+ }
+
+ // Check if we should start dragging. Check if we've dragged past the threshold,
+ // or we are starting from the open state.
+ if(!this._isDragging && Math.abs(e.gesture.deltaY) > this.dragThresholdY) {
+ this._isDragging = true;
+ }
+
+ if(this._isDragging) {
+ this._moveElement(e);
+
+ this._currentDrag.currentY = scrollY + pageY - offset;
+
+ // this._reorderItems();
+ }
+ });
+
+ // When an item is dragged, we need to reorder any items for sorting purposes
+ ReorderDrag.prototype._getReorderIndex = function() {
+ var self = this;
+ var placeholder = this._currentDrag.placeholder;
+ var siblings = Array.prototype.slice.call(this._currentDrag.placeholder.parentNode.children)
+ .filter(function(el) {
+ return el.nodeName === self.el.nodeName && el !== self.el;
+ });
+
+ var dragOffsetTop = this._currentDrag.currentY;
+ var el;
+ for (var i = 0, len = siblings.length; i < len; i++) {
+ el = siblings[i];
+ if (i === len - 1) {
+ if (dragOffsetTop > el.offsetTop) {
+ return i;
+ }
+ } else if (i === 0) {
+ if (dragOffsetTop < el.offsetTop + el.offsetHeight) {
+ return i;
+ }
+ } else if (dragOffsetTop > el.offsetTop - el.offsetHeight / 2 &&
+ dragOffsetTop < el.offsetTop + el.offsetHeight) {
+ return i;
+ }
+ }
+ return this._currentDrag.startIndex;
+ };
+
+ ReorderDrag.prototype.end = function(e, doneCallback) {
+ if(!this._currentDrag) {
+ doneCallback && doneCallback();
+ return;
+ }
+
+ var placeholder = this._currentDrag.placeholder;
+ var finalIndex = this._getReorderIndex();
+
+ // Reposition the element
+ this.el.classList.remove(ITEM_REORDERING_CLASS);
+ this.el.style[ionic.CSS.TRANSFORM] = '';
+
+ placeholder.parentNode.insertBefore(this.el, placeholder);
+ placeholder.parentNode.removeChild(placeholder);
+
+ this.onReorder && this.onReorder(this.el, this._currentDrag.startIndex, finalIndex);
+
+ this._currentDrag = null;
+ doneCallback && doneCallback();
+ };
+
+
+
+ /**
+ * The ListView handles a list of items. It will process drag animations, edit mode,
+ * and other operations that are common on mobile lists or table views.
+ */
+ ionic.views.ListView = ionic.views.View.inherit({
+ initialize: function(opts) {
+ var _this = this;
+
+ opts = ionic.extend({
+ onReorder: function(el, oldIndex, newIndex) {},
+ virtualRemoveThreshold: -200,
+ virtualAddThreshold: 200,
+ canSwipe: function() {
+ return true;
+ }
+ }, opts);
+
+ ionic.extend(this, opts);
+
+ if(!this.itemHeight && this.listEl) {
+ this.itemHeight = this.listEl.children[0] && parseInt(this.listEl.children[0].style.height, 10);
+ }
+
+ //ionic.views.ListView.__super__.initialize.call(this, opts);
+
+ this.onRefresh = opts.onRefresh || function() {};
+ this.onRefreshOpening = opts.onRefreshOpening || function() {};
+ this.onRefreshHolding = opts.onRefreshHolding || function() {};
+
+ window.ionic.onGesture('release', function(e) {
+ _this._handleEndDrag(e);
+ }, this.el);
+
+ window.ionic.onGesture('drag', function(e) {
+ _this._handleDrag(e);
+ }, this.el);
+ // Start the drag states
+ this._initDrag();
+ },
+ /**
+ * Called to tell the list to stop refreshing. This is useful
+ * if you are refreshing the list and are done with refreshing.
+ */
+ stopRefreshing: function() {
+ var refresher = this.el.querySelector('.list-refresher');
+ refresher.style.height = '0px';
+ },
+
+ /**
+ * If we scrolled and have virtual mode enabled, compute the window
+ * of active elements in order to figure out the viewport to render.
+ */
+ didScroll: function(e) {
+ if(this.isVirtual) {
+ var itemHeight = this.itemHeight;
+
+ // TODO: This would be inaccurate if we are windowed
+ var totalItems = this.listEl.children.length;
+
+ // Grab the total height of the list
+ var scrollHeight = e.target.scrollHeight;
+
+ // Get the viewport height
+ var viewportHeight = this.el.parentNode.offsetHeight;
+
+ // scrollTop is the current scroll position
+ var scrollTop = e.scrollTop;
+
+ // High water is the pixel position of the first element to include (everything before
+ // that will be removed)
+ var highWater = Math.max(0, e.scrollTop + this.virtualRemoveThreshold);
+
+ // Low water is the pixel position of the last element to include (everything after
+ // that will be removed)
+ var lowWater = Math.min(scrollHeight, Math.abs(e.scrollTop) + viewportHeight + this.virtualAddThreshold);
+
+ // Compute how many items per viewport size can show
+ var itemsPerViewport = Math.floor((lowWater - highWater) / itemHeight);
+
+ // Get the first and last elements in the list based on how many can fit
+ // between the pixel range of lowWater and highWater
+ var first = parseInt(Math.abs(highWater / itemHeight), 10);
+ var last = parseInt(Math.abs(lowWater / itemHeight), 10);
+
+ // Get the items we need to remove
+ this._virtualItemsToRemove = Array.prototype.slice.call(this.listEl.children, 0, first);
+
+ // Grab the nodes we will be showing
+ var nodes = Array.prototype.slice.call(this.listEl.children, first, first + itemsPerViewport);
+
+ this.renderViewport && this.renderViewport(highWater, lowWater, first, last);
+ }
+ },
+
+ didStopScrolling: function(e) {
+ if(this.isVirtual) {
+ for(var i = 0; i < this._virtualItemsToRemove.length; i++) {
+ var el = this._virtualItemsToRemove[i];
+ //el.parentNode.removeChild(el);
+ this.didHideItem && this.didHideItem(i);
+ }
+ // Once scrolling stops, check if we need to remove old items
+
+ }
+ },
+
+ /**
+ * Clear any active drag effects on the list.
+ */
+ clearDragEffects: function() {
+ if(this._lastDragOp) {
+ this._lastDragOp.clean && this._lastDragOp.clean();
+ this._lastDragOp = null;
+ }
+ },
+
+ _initDrag: function() {
+ //ionic.views.ListView.__super__._initDrag.call(this);
+
+ // Store the last one
+ this._lastDragOp = this._dragOp;
+
+ this._dragOp = null;
+ },
+
+ // Return the list item from the given target
+ _getItem: function(target) {
+ while(target) {
+ if(target.classList && target.classList.contains(ITEM_CLASS)) {
+ return target;
+ }
+ target = target.parentNode;
+ }
+ return null;
+ },
+
+
+ _startDrag: function(e) {
+ var _this = this;
+
+ var didStart = false;
+
+ this._isDragging = false;
+
+ var lastDragOp = this._lastDragOp;
+ var item;
+
+ // Check if this is a reorder drag
+ if(ionic.DomUtil.getParentOrSelfWithClass(e.target, ITEM_REORDER_BTN_CLASS) && (e.gesture.direction == 'up' || e.gesture.direction == 'down')) {
+ item = this._getItem(e.target);
+
+ if(item) {
+ this._dragOp = new ReorderDrag({
+ listEl: this.el,
+ el: item,
+ scrollEl: this.scrollEl,
+ scrollView: this.scrollView,
+ onReorder: function(el, start, end) {
+ _this.onReorder && _this.onReorder(el, start, end);
+ }
+ });
+ this._dragOp.start(e);
+ e.preventDefault();
+ }
+ }
+
+ // Or check if this is a swipe to the side drag
+ else if(!this._didDragUpOrDown && (e.gesture.direction == 'left' || e.gesture.direction == 'right') && Math.abs(e.gesture.deltaX) > 5) {
+
+ // Make sure this is an item with buttons
+ item = this._getItem(e.target);
+ if(item && item.querySelector('.item-options')) {
+ this._dragOp = new SlideDrag({ el: this.el, canSwipe: this.canSwipe });
+ this._dragOp.start(e);
+ e.preventDefault();
+ }
+ }
+
+ // If we had a last drag operation and this is a new one on a different item, clean that last one
+ if(lastDragOp && this._dragOp && !this._dragOp.isSameItem(lastDragOp) && e.defaultPrevented) {
+ lastDragOp.clean && lastDragOp.clean();
+ }
+ },
+
+
+ _handleEndDrag: function(e) {
+ var _this = this;
+
+ this._didDragUpOrDown = false;
+
+ if(!this._dragOp) {
+ //ionic.views.ListView.__super__._handleEndDrag.call(this, e);
+ return;
+ }
+
+ this._dragOp.end(e, function() {
+ _this._initDrag();
+ });
+ },
+
+ /**
+ * Process the drag event to move the item to the left or right.
+ */
+ _handleDrag: function(e) {
+ var _this = this, content, buttons;
+
+ if(Math.abs(e.gesture.deltaY) > 5) {
+ this._didDragUpOrDown = true;
+ }
+
+ // If we get a drag event, make sure we aren't in another drag, then check if we should
+ // start one
+ if(!this.isDragging && !this._dragOp) {
+ this._startDrag(e);
+ }
+
+ // No drag still, pass it up
+ if(!this._dragOp) {
+ //ionic.views.ListView.__super__._handleDrag.call(this, e);
+ return;
+ }
+
+ e.gesture.srcEvent.preventDefault();
+ this._dragOp.drag(e);
+ }
+
+ });
+
+})(ionic);
+
+(function(ionic) {
+'use strict';
+
+ ionic.views.Modal = ionic.views.View.inherit({
+ initialize: function(opts) {
+ opts = ionic.extend({
+ focusFirstInput: false,
+ unfocusOnHide: true,
+ focusFirstDelay: 600,
+ backdropClickToClose: true,
+ hardwareBackButtonClose: true,
+ }, opts);
+
+ ionic.extend(this, opts);
+
+ this.el = opts.el;
+ },
+ show: function() {
+ var self = this;
+
+ if(self.focusFirstInput) {
+ // Let any animations run first
+ window.setTimeout(function() {
+ var input = self.el.querySelector('input, textarea');
+ input && input.focus && input.focus();
+ }, self.focusFirstDelay);
+ }
+ },
+ hide: function() {
+ // Unfocus all elements
+ if(this.unfocusOnHide) {
+ var inputs = this.el.querySelectorAll('input, textarea');
+ // Let any animations run first
+ window.setTimeout(function() {
+ for(var i = 0; i < inputs.length; i++) {
+ inputs[i].blur && inputs[i].blur();
+ }
+ });
+ }
+ }
+ });
+
+})(ionic);
+
+(function(ionic) {
+'use strict';
+
+ /**
+ * The side menu view handles one of the side menu's in a Side Menu Controller
+ * configuration.
+ * It takes a DOM reference to that side menu element.
+ */
+ ionic.views.SideMenu = ionic.views.View.inherit({
+ initialize: function(opts) {
+ this.el = opts.el;
+ this.isEnabled = (typeof opts.isEnabled === 'undefined') ? true : opts.isEnabled;
+ this.setWidth(opts.width);
+ },
+ getFullWidth: function() {
+ return this.width;
+ },
+ setWidth: function(width) {
+ this.width = width;
+ this.el.style.width = width + 'px';
+ },
+ setIsEnabled: function(isEnabled) {
+ this.isEnabled = isEnabled;
+ },
+ bringUp: function() {
+ if(this.el.style.zIndex !== '0') {
+ this.el.style.zIndex = '0';
+ }
+ },
+ pushDown: function() {
+ if(this.el.style.zIndex !== '-1') {
+ this.el.style.zIndex = '-1';
+ }
+ }
+ });
+
+ ionic.views.SideMenuContent = ionic.views.View.inherit({
+ initialize: function(opts) {
+ ionic.extend(this, {
+ animationClass: 'menu-animated',
+ onDrag: function(e) {},
+ onEndDrag: function(e) {}
+ }, opts);
+
+ ionic.onGesture('drag', ionic.proxy(this._onDrag, this), this.el);
+ ionic.onGesture('release', ionic.proxy(this._onEndDrag, this), this.el);
+ },
+ _onDrag: function(e) {
+ this.onDrag && this.onDrag(e);
+ },
+ _onEndDrag: function(e) {
+ this.onEndDrag && this.onEndDrag(e);
+ },
+ disableAnimation: function() {
+ this.el.classList.remove(this.animationClass);
+ },
+ enableAnimation: function() {
+ this.el.classList.add(this.animationClass);
+ },
+ getTranslateX: function() {
+ return parseFloat(this.el.style[ionic.CSS.TRANSFORM].replace('translate3d(', '').split(',')[0]);
+ },
+ setTranslateX: ionic.animationFrameThrottle(function(x) {
+ this.el.style[ionic.CSS.TRANSFORM] = 'translate3d(' + x + 'px, 0, 0)';
+ })
+ });
+
+})(ionic);
+
+/*
+ * Adapted from Swipe.js 2.0
+ *
+ * Brad Birdsall
+ * Copyright 2013, MIT License
+ *
+*/
+
+(function(ionic) {
+'use strict';
+
+ionic.views.Slider = ionic.views.View.inherit({
+ initialize: function (options) {
+ var slider = this;
+
+ // utilities
+ var noop = function() {}; // simple no operation function
+ var offloadFn = function(fn) { setTimeout(fn || noop, 0); }; // offload a functions execution
+
+ // check browser capabilities
+ var browser = {
+ addEventListener: !!window.addEventListener,
+ touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
+ transitions: (function(temp) {
+ var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition'];
+ for ( var i in props ) if (temp.style[ props[i] ] !== undefined) return true;
+ return false;
+ })(document.createElement('swipe'))
+ };
+
+
+ var container = options.el;
+
+ // quit if no root element
+ if (!container) return;
+ var element = container.children[0];
+ var slides, slidePos, width, length;
+ options = options || {};
+ var index = parseInt(options.startSlide, 10) || 0;
+ var speed = options.speed || 300;
+ options.continuous = options.continuous !== undefined ? options.continuous : true;
+
+ function setup() {
+
+ // cache slides
+ slides = element.children;
+ length = slides.length;
+
+ // set continuous to false if only one slide
+ if (slides.length < 2) options.continuous = false;
+
+ //special case if two slides
+ if (browser.transitions && options.continuous && slides.length < 3) {
+ element.appendChild(slides[0].cloneNode(true));
+ element.appendChild(element.children[1].cloneNode(true));
+ slides = element.children;
+ }
+
+ // create an array to store current positions of each slide
+ slidePos = new Array(slides.length);
+
+ // determine width of each slide
+ width = container.offsetWidth || container.getBoundingClientRect().width;
+
+ element.style.width = (slides.length * width) + 'px';
+
+ // stack elements
+ var pos = slides.length;
+ while(pos--) {
+
+ var slide = slides[pos];
+
+ slide.style.width = width + 'px';
+ slide.setAttribute('data-index', pos);
+
+ if (browser.transitions) {
+ slide.style.left = (pos * -width) + 'px';
+ move(pos, index > pos ? -width : (index < pos ? width : 0), 0);
+ }
+
+ }
+
+ // reposition elements before and after index
+ if (options.continuous && browser.transitions) {
+ move(circle(index-1), -width, 0);
+ move(circle(index+1), width, 0);
+ }
+
+ if (!browser.transitions) element.style.left = (index * -width) + 'px';
+
+ container.style.visibility = 'visible';
+
+ options.slidesChanged && options.slidesChanged();
+ }
+
+ function prev() {
+
+ if (options.continuous) slide(index-1);
+ else if (index) slide(index-1);
+
+ }
+
+ function next() {
+
+ if (options.continuous) slide(index+1);
+ else if (index < slides.length - 1) slide(index+1);
+
+ }
+
+ function circle(index) {
+
+ // a simple positive modulo using slides.length
+ return (slides.length + (index % slides.length)) % slides.length;
+
+ }
+
+ function slide(to, slideSpeed) {
+
+ // do nothing if already on requested slide
+ if (index == to) return;
+
+ if (browser.transitions) {
+
+ var direction = Math.abs(index-to) / (index-to); // 1: backward, -1: forward
+
+ // get the actual position of the slide
+ if (options.continuous) {
+ var natural_direction = direction;
+ direction = -slidePos[circle(to)] / width;
+
+ // if going forward but to < index, use to = slides.length + to
+ // if going backward but to > index, use to = -slides.length + to
+ if (direction !== natural_direction) to = -direction * slides.length + to;
+
+ }
+
+ var diff = Math.abs(index-to) - 1;
+
+ // move all the slides between index and to in the right direction
+ while (diff--) move( circle((to > index ? to : index) - diff - 1), width * direction, 0);
+
+ to = circle(to);
+
+ move(index, width * direction, slideSpeed || speed);
+ move(to, 0, slideSpeed || speed);
+
+ if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place
+
+ } else {
+
+ to = circle(to);
+ animate(index * -width, to * -width, slideSpeed || speed);
+ //no fallback for a circular continuous if the browser does not accept transitions
+ }
+
+ index = to;
+ offloadFn(options.callback && options.callback(index, slides[index]));
+ }
+
+ function move(index, dist, speed) {
+
+ translate(index, dist, speed);
+ slidePos[index] = dist;
+
+ }
+
+ function translate(index, dist, speed) {
+
+ var slide = slides[index];
+ var style = slide && slide.style;
+
+ if (!style) return;
+
+ style.webkitTransitionDuration =
+ style.MozTransitionDuration =
+ style.msTransitionDuration =
+ style.OTransitionDuration =
+ style.transitionDuration = speed + 'ms';
+
+ style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)';
+ style.msTransform =
+ style.MozTransform =
+ style.OTransform = 'translateX(' + dist + 'px)';
+
+ }
+
+ function animate(from, to, speed) {
+
+ // if not an animation, just reposition
+ if (!speed) {
+
+ element.style.left = to + 'px';
+ return;
+
+ }
+
+ var start = +new Date();
+
+ var timer = setInterval(function() {
+
+ var timeElap = +new Date() - start;
+
+ if (timeElap > speed) {
+
+ element.style.left = to + 'px';
+
+ if (delay) begin();
+
+ options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);
+
+ clearInterval(timer);
+ return;
+
+ }
+
+ element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px';
+
+ }, 4);
+
+ }
+
+ // setup auto slideshow
+ var delay = options.auto || 0;
+ var interval;
+
+ function begin() {
+
+ interval = setTimeout(next, delay);
+
+ }
+
+ function stop() {
+
+ delay = options.auto || 0;
+ clearTimeout(interval);
+
+ }
+
+
+ // setup initial vars
+ var start = {};
+ var delta = {};
+ var isScrolling;
+
+ // setup event capturing
+ var events = {
+
+ handleEvent: function(event) {
+ if(event.type == 'mousedown' || event.type == 'mouseup' || event.type == 'mousemove') {
+ event.touches = [{
+ pageX: event.pageX,
+ pageY: event.pageY
+ }];
+ }
+
+ switch (event.type) {
+ case 'mousedown': this.start(event); break;
+ case 'touchstart': this.start(event); break;
+ case 'touchmove': this.touchmove(event); break;
+ case 'mousemove': this.touchmove(event); break;
+ case 'touchend': offloadFn(this.end(event)); break;
+ case 'mouseup': offloadFn(this.end(event)); break;
+ case 'webkitTransitionEnd':
+ case 'msTransitionEnd':
+ case 'oTransitionEnd':
+ case 'otransitionend':
+ case 'transitionend': offloadFn(this.transitionEnd(event)); break;
+ case 'resize': offloadFn(setup); break;
+ }
+
+ if (options.stopPropagation) event.stopPropagation();
+
+ },
+ start: function(event) {
+
+ var touches = event.touches[0];
+
+ // measure start values
+ start = {
+
+ // get initial touch coords
+ x: touches.pageX,
+ y: touches.pageY,
+
+ // store time to determine touch duration
+ time: +new Date()
+
+ };
+
+ // used for testing first move event
+ isScrolling = undefined;
+
+ // reset delta and end measurements
+ delta = {};
+
+ // attach touchmove and touchend listeners
+ if(browser.touch) {
+ element.addEventListener('touchmove', this, false);
+ element.addEventListener('touchend', this, false);
+ } else {
+ element.addEventListener('mousemove', this, false);
+ element.addEventListener('mouseup', this, false);
+ document.addEventListener('mouseup', this, false);
+ }
+ },
+ touchmove: function(event) {
+
+ // ensure swiping with one touch and not pinching
+ // ensure sliding is enabled
+ if (event.touches.length > 1 ||
+ event.scale && event.scale !== 1 ||
+ slider.slideIsDisabled) {
+ return;
+ }
+
+ if (options.disableScroll) event.preventDefault();
+
+ var touches = event.touches[0];
+
+ // measure change in x and y
+ delta = {
+ x: touches.pageX - start.x,
+ y: touches.pageY - start.y
+ };
+
+ // determine if scrolling test has run - one time test
+ if ( typeof isScrolling == 'undefined') {
+ isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) );
+ }
+
+ // if user is not trying to scroll vertically
+ if (!isScrolling) {
+
+ // prevent native scrolling
+ event.preventDefault();
+
+ // stop slideshow
+ stop();
+
+ // increase resistance if first or last slide
+ if (options.continuous) { // we don't add resistance at the end
+
+ translate(circle(index-1), delta.x + slidePos[circle(index-1)], 0);
+ translate(index, delta.x + slidePos[index], 0);
+ translate(circle(index+1), delta.x + slidePos[circle(index+1)], 0);
+
+ } else {
+
+ delta.x =
+ delta.x /
+ ( (!index && delta.x > 0 || // if first slide and sliding left
+ index == slides.length - 1 && // or if last slide and sliding right
+ delta.x < 0 // and if sliding at all
+ ) ?
+ ( Math.abs(delta.x) / width + 1 ) // determine resistance level
+ : 1 ); // no resistance if false
+
+ // translate 1:1
+ translate(index-1, delta.x + slidePos[index-1], 0);
+ translate(index, delta.x + slidePos[index], 0);
+ translate(index+1, delta.x + slidePos[index+1], 0);
+ }
+
+ }
+
+ },
+ end: function(event) {
+
+ // measure duration
+ var duration = +new Date() - start.time;
+
+ // determine if slide attempt triggers next/prev slide
+ var isValidSlide =
+ Number(duration) < 250 && // if slide duration is less than 250ms
+ Math.abs(delta.x) > 20 || // and if slide amt is greater than 20px
+ Math.abs(delta.x) > width/2; // or if slide amt is greater than half the width
+
+ // determine if slide attempt is past start and end
+ var isPastBounds = (!index && delta.x > 0) || // if first slide and slide amt is greater than 0
+ (index == slides.length - 1 && delta.x < 0); // or if last slide and slide amt is less than 0
+
+ if (options.continuous) isPastBounds = false;
+
+ // determine direction of swipe (true:right, false:left)
+ var direction = delta.x < 0;
+
+ // if not scrolling vertically
+ if (!isScrolling) {
+
+ if (isValidSlide && !isPastBounds) {
+
+ if (direction) {
+
+ if (options.continuous) { // we need to get the next in this direction in place
+
+ move(circle(index-1), -width, 0);
+ move(circle(index+2), width, 0);
+
+ } else {
+ move(index-1, -width, 0);
+ }
+
+ move(index, slidePos[index]-width, speed);
+ move(circle(index+1), slidePos[circle(index+1)]-width, speed);
+ index = circle(index+1);
+
+ } else {
+ if (options.continuous) { // we need to get the next in this direction in place
+
+ move(circle(index+1), width, 0);
+ move(circle(index-2), -width, 0);
+
+ } else {
+ move(index+1, width, 0);
+ }
+
+ move(index, slidePos[index]+width, speed);
+ move(circle(index-1), slidePos[circle(index-1)]+width, speed);
+ index = circle(index-1);
+
+ }
+
+ options.callback && options.callback(index, slides[index]);
+
+ } else {
+
+ if (options.continuous) {
+
+ move(circle(index-1), -width, speed);
+ move(index, 0, speed);
+ move(circle(index+1), width, speed);
+
+ } else {
+
+ move(index-1, -width, speed);
+ move(index, 0, speed);
+ move(index+1, width, speed);
+ }
+
+ }
+
+ }
+
+ // kill touchmove and touchend event listeners until touchstart called again
+ if(browser.touch) {
+ element.removeEventListener('touchmove', events, false);
+ element.removeEventListener('touchend', events, false);
+ } else {
+ element.removeEventListener('mousemove', events, false);
+ element.removeEventListener('mouseup', events, false);
+ document.removeEventListener('mouseup', events, false);
+ }
+
+ },
+ transitionEnd: function(event) {
+
+ if (parseInt(event.target.getAttribute('data-index'), 10) == index) {
+
+ if (delay) begin();
+
+ options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);
+
+ }
+
+ }
+
+ };
+
+ // Public API
+ this.update = function() {
+ setTimeout(setup);
+ };
+ this.setup = function() {
+ setup();
+ };
+
+ this.enableSlide = function(shouldEnable) {
+ if (arguments.length) {
+ this.slideIsDisabled = !shouldEnable;
+ }
+ return !this.slideIsDisabled;
+ },
+ this.slide = function(to, speed) {
+ // cancel slideshow
+ stop();
+
+ slide(to, speed);
+ };
+
+ this.prev = this.previous = function() {
+ // cancel slideshow
+ stop();
+
+ prev();
+ };
+
+ this.next = function() {
+ // cancel slideshow
+ stop();
+
+ next();
+ };
+
+ this.stop = function() {
+ // cancel slideshow
+ stop();
+ };
+
+ this.start = function() {
+ begin();
+ };
+
+ this.currentIndex = function() {
+ // return current index position
+ return index;
+ };
+
+ this.slidesCount = function() {
+ // return total number of slides
+ return length;
+ };
+
+ this.kill = function() {
+ // cancel slideshow
+ stop();
+
+ // reset element
+ element.style.width = '';
+ element.style.left = '';
+
+ // reset slides
+ var pos = slides.length;
+ while(pos--) {
+
+ var slide = slides[pos];
+ slide.style.width = '';
+ slide.style.left = '';
+
+ if (browser.transitions) translate(pos, 0, 0);
+
+ }
+
+ // removed event listeners
+ if (browser.addEventListener) {
+
+ // remove current event listeners
+ element.removeEventListener('touchstart', events, false);
+ element.removeEventListener('webkitTransitionEnd', events, false);
+ element.removeEventListener('msTransitionEnd', events, false);
+ element.removeEventListener('oTransitionEnd', events, false);
+ element.removeEventListener('otransitionend', events, false);
+ element.removeEventListener('transitionend', events, false);
+ window.removeEventListener('resize', events, false);
+
+ }
+ else {
+
+ window.onresize = null;
+
+ }
+ };
+
+ this.load = function() {
+ // trigger setup
+ setup();
+
+ // start auto slideshow if applicable
+ if (delay) begin();
+
+
+ // add event listeners
+ if (browser.addEventListener) {
+
+ // set touchstart event on element
+ if (browser.touch) {
+ element.addEventListener('touchstart', events, false);
+ } else {
+ element.addEventListener('mousedown', events, false);
+ }
+
+ if (browser.transitions) {
+ element.addEventListener('webkitTransitionEnd', events, false);
+ element.addEventListener('msTransitionEnd', events, false);
+ element.addEventListener('oTransitionEnd', events, false);
+ element.addEventListener('otransitionend', events, false);
+ element.addEventListener('transitionend', events, false);
+ }
+
+ // set resize event on window
+ window.addEventListener('resize', events, false);
+
+ } else {
+
+ window.onresize = function () { setup(); }; // to play nice with old IE
+
+ }
+ };
+
+ }
+});
+
+})(ionic);
+
+(function(ionic) {
+'use strict';
+
+ ionic.views.Toggle = ionic.views.View.inherit({
+ initialize: function(opts) {
+ var self = this;
+
+ this.el = opts.el;
+ this.checkbox = opts.checkbox;
+ this.track = opts.track;
+ this.handle = opts.handle;
+ this.openPercent = -1;
+ this.onChange = opts.onChange || function() {};
+
+ this.triggerThreshold = opts.triggerThreshold || 20;
+
+ this.dragStartHandler = function(e) {
+ self.dragStart(e);
+ };
+ this.dragHandler = function(e) {
+ self.drag(e);
+ };
+ this.holdHandler = function(e) {
+ self.hold(e);
+ };
+ this.releaseHandler = function(e) {
+ self.release(e);
+ };
+
+ this.dragStartGesture = ionic.onGesture('dragstart', this.dragStartHandler, this.el);
+ this.dragGesture = ionic.onGesture('drag', this.dragHandler, this.el);
+ this.dragHoldGesture = ionic.onGesture('hold', this.holdHandler, this.el);
+ this.dragReleaseGesture = ionic.onGesture('release', this.releaseHandler, this.el);
+ },
+
+ destroy: function() {
+ ionic.offGesture(this.dragStartGesture, 'dragstart', this.dragStartGesture);
+ ionic.offGesture(this.dragGesture, 'drag', this.dragGesture);
+ ionic.offGesture(this.dragHoldGesture, 'hold', this.holdHandler);
+ ionic.offGesture(this.dragReleaseGesture, 'release', this.releaseHandler);
+ },
+
+ tap: function(e) {
+ if(this.el.getAttribute('disabled') !== 'disabled') {
+ this.val( !this.checkbox.checked );
+ }
+ },
+
+ dragStart: function(e) {
+ if(this.checkbox.disabled) return;
+
+ this._dragInfo = {
+ width: this.el.offsetWidth,
+ left: this.el.offsetLeft,
+ right: this.el.offsetLeft + this.el.offsetWidth,
+ triggerX: this.el.offsetWidth / 2,
+ initialState: this.checkbox.checked
+ };
+
+ // Stop any parent dragging
+ e.gesture.srcEvent.preventDefault();
+
+ // Trigger hold styles
+ this.hold(e);
+ },
+
+ drag: function(e) {
+ var self = this;
+ if(!this._dragInfo) { return; }
+
+ // Stop any parent dragging
+ e.gesture.srcEvent.preventDefault();
+
+ ionic.requestAnimationFrame(function(amount) {
+ if (!self._dragInfo) { return; }
+
+ var slidePageLeft = self.track.offsetLeft + (self.handle.offsetWidth / 2);
+ var slidePageRight = self.track.offsetLeft + self.track.offsetWidth - (self.handle.offsetWidth / 2);
+ var dx = e.gesture.deltaX;
+
+ var px = e.gesture.touches[0].pageX - self._dragInfo.left;
+ var mx = self._dragInfo.width - self.triggerThreshold;
+
+ // The initial state was on, so "tend towards" on
+ if(self._dragInfo.initialState) {
+ if(px < self.triggerThreshold) {
+ self.setOpenPercent(0);
+ } else if(px > self._dragInfo.triggerX) {
+ self.setOpenPercent(100);
+ }
+ } else {
+ // The initial state was off, so "tend towards" off
+ if(px < self._dragInfo.triggerX) {
+ self.setOpenPercent(0);
+ } else if(px > mx) {
+ self.setOpenPercent(100);
+ }
+ }
+ });
+ },
+
+ endDrag: function(e) {
+ this._dragInfo = null;
+ },
+
+ hold: function(e) {
+ this.el.classList.add('dragging');
+ },
+ release: function(e) {
+ this.el.classList.remove('dragging');
+ this.endDrag(e);
+ },
+
+
+ setOpenPercent: function(openPercent) {
+ // only make a change if the new open percent has changed
+ if(this.openPercent < 0 || (openPercent < (this.openPercent - 3) || openPercent > (this.openPercent + 3) ) ) {
+ this.openPercent = openPercent;
+
+ if(openPercent === 0) {
+ this.val(false);
+ } else if(openPercent === 100) {
+ this.val(true);
+ } else {
+ var openPixel = Math.round( (openPercent / 100) * this.track.offsetWidth - (this.handle.offsetWidth) );
+ openPixel = (openPixel < 1 ? 0 : openPixel);
+ this.handle.style[ionic.CSS.TRANSFORM] = 'translate3d(' + openPixel + 'px,0,0)';
+ }
+ }
+ },
+
+ val: function(value) {
+ if(value === true || value === false) {
+ if(this.handle.style[ionic.CSS.TRANSFORM] !== "") {
+ this.handle.style[ionic.CSS.TRANSFORM] = "";
+ }
+ this.checkbox.checked = value;
+ this.openPercent = (value ? 100 : 0);
+ this.onChange && this.onChange();
+ }
+ return this.checkbox.checked;
+ }
+
+ });
+
+})(ionic);
+
+})();
\ No newline at end of file
diff --git a/client/lib/ionic/js/ionic.min.js b/client/lib/ionic/js/ionic.min.js
new file mode 100644
index 0000000..c916f9f
--- /dev/null
+++ b/client/lib/ionic/js/ionic.min.js
@@ -0,0 +1,17 @@
+/*!
+ * Copyright 2014 Drifty Co.
+ * http://drifty.com/
+ *
+ * Ionic, v1.0.0-beta.13
+ * A powerful HTML5 mobile app framework.
+ * http://ionicframework.com/
+ *
+ * By @maxlynch, @benjsperry, @adamdbradley <3
+ *
+ * Licensed under the MIT license. Please see LICENSE for more information.
+ *
+ */
+
+!function(){function e(e,t,n){t!==!1?k.addEventListener(e,j[e],n):k.removeEventListener(e,j[e])}function t(e){var t=T(e.target),i=E(t);if(ionic.tap.requiresNativeClick(i)||z)return!1;var r=ionic.tap.pointerCoord(e);n("click",i,r.x,r.y),_(i)}function n(e,t,n,i){var r=document.createEvent("MouseEvents");r.initMouseEvent(e,!0,!0,window,1,0,0,n,i,!1,!1,!1,!1,0,null),r.isIonicTap=!0,t.dispatchEvent(r)}function i(e){return("submit"!=e.target.type||0!==e.detail)&&(ionic.scroll.isScrolling&&ionic.tap.containsOrIsTextInput(e.target)||!e.isIonicTap&&!ionic.tap.requiresNativeClick(e.target))?(e.stopPropagation(),ionic.tap.isLabelWithTextInput(e.target)||e.preventDefault(),!1):void 0}function r(t){if(!t.isIonicTap&&!d(t)){if(X)return t.stopPropagation(),ionic.tap.isTextInput(t.target)&&q===t.target||/^(select|option)$/i.test(t.target.tagName)||t.preventDefault(),!1;z=!1,F=ionic.tap.pointerCoord(t),e("mousemove"),ionic.activator.start(t)}}function o(n){return X?(n.stopPropagation(),n.preventDefault(),!1):d(n)||/^(select|option)$/i.test(n.target.tagName)?!1:(v(n)||t(n),e("mousemove",!1),ionic.activator.end(),void(z=!1))}function s(t){return v(t)?(e("mousemove",!1),ionic.activator.end(),z=!0,!1):void 0}function a(t){if(!d(t)&&(z=!1,h(),F=ionic.tap.pointerCoord(t),e(U),ionic.activator.start(t),ionic.Platform.isIOS()&&ionic.tap.isLabelWithTextInput(t.target))){var n=E(T(t.target));n!==Y&&t.preventDefault()}}function l(e){d(e)||(h(),v(e)||(t(e),/^(select|option)$/i.test(e.target.tagName)&&e.preventDefault()),q=e.target,u())}function c(t){return v(t)?(z=!0,e(U,!1),ionic.activator.end(),!1):void 0}function u(){e(U,!1),ionic.activator.end(),z=!1}function h(){X=!0,clearTimeout(H),H=setTimeout(function(){X=!1},2e3)}function d(e){return e.isTapHandled?!0:(e.isTapHandled=!0,ionic.scroll.isScrolling&&ionic.tap.containsOrIsTextInput(e.target)?(e.preventDefault(),!0):void 0)}function _(e){W=null;var t=!1;"SELECT"==e.tagName?(n("mousedown",e,0,0),e.focus&&e.focus(),t=!0):g()===e?t=!0:/^(input|textarea)$/i.test(e.tagName)||e.isContentEditable?(t=!0,e.focus&&e.focus(),e.value=e.value,X&&(W=e)):f(),t&&(g(e),ionic.trigger("ionic.focusin",{target:e},!0))}function f(){var e=g();e&&(/^(input|textarea|select)$/i.test(e.tagName)||e.isContentEditable)&&e.blur(),g(null)}function p(e){X&&ionic.tap.isTextInput(g())&&ionic.tap.isTextInput(W)&&W!==e.target&&(W.focus(),W=null),ionic.scroll.isScrolling=!1}function m(){g(null)}function g(e){return arguments.length&&(Y=e),Y||document.activeElement}function v(e){if(!e||1!==e.target.nodeType||!F||0===F.x&&0===F.y)return!1;var t=ionic.tap.pointerCoord(e),n=!(!e.target.classList||!e.target.classList.contains),i=n&e.target.classList.contains("button")?Z:B;return Math.abs(F.x-t.x)>i||Math.abs(F.y-t.y)>i}function T(e,t){for(var n=e,i=0;6>i&&n;i++){if("LABEL"===n.tagName)return n;n=n.parentElement}return t!==!1?e:void 0}function E(e){if(e&&"LABEL"===e.tagName){if(e.control)return e.control;if(e.querySelector){var t=e.querySelector("input,textarea,select");if(t)return t}}return e}function S(){A()?(window.addEventListener("native.keyboardshow",w),window.addEventListener("native.keyboardhide",L),window.addEventListener("native.showkeyboard",w),window.addEventListener("native.hidekeyboard",L)):document.body.addEventListener("focusout",L),document.body.addEventListener("ionic.focusin",b),document.body.addEventListener("focusin",b),document.body.addEventListener("orientationchange",G),window.navigator.msPointerEnabled?document.removeEventListener("MSPointerDown",S):document.removeEventListener("touchstart",S)}function w(e){clearTimeout(K),ionic.keyboard.height=e.keyboardHeight}function b(e){e.target&&ionic.tap.isTextInput(e.target)&&!ionic.tap.isDateInput(e.target)&&P(e.target)&&(document.addEventListener("keydown",M,!1),document.body.scrollTop=0,document.body.querySelector(".scroll-content").scrollTop=0,$=e.target,y(e))}function y(e){clearTimeout(J),clearTimeout(K),J=setTimeout(function(){if(!(tt+350>Date.now())){tt=Date.now();var t,n=$.getBoundingClientRect(),i=0;Q=setInterval(function(){t=I(),i>10&&(clearInterval(Q),t=275),t&&(clearInterval(Q),D(e.target,n.top,n.bottom,et,t)),i++},100)}},32)}function D(e,t,n,i,r){var o={target:e,elementTop:Math.round(t),elementBottom:Math.round(n),keyboardHeight:r,viewportHeight:i};return o.hasPlugin=A(),o.contentHeight=i-r,o.isElementUnderKeyboard=o.elementBottom>o.contentHeight,ionic.keyboard.isOpen=!0,$=e,ionic.trigger("scrollChildIntoView",o,!0),ionic.requestAnimationFrame(function(){document.body.classList.add(nt)}),window.navigator.msPointerEnabled?document.addEventListener("MSPointerMove",N,!1):document.addEventListener("touchmove",N,!1),o}function L(){clearTimeout(K),K=setTimeout(ionic.keyboard.hide,350)}function x(){C()>et&&(et=C())}function M(e){ionic.scroll.isScrolling&&N(e)}function N(e){"TEXTAREA"!==e.target.tagName&&e.preventDefault()}function G(){var e=C();if(e===et)var t=0,n=setInterval(function(){t>10&&clearInterval(n),e=C(),e!==et&&(ionic.keyboard.landscape=et>e?!0:!1,et=e,clearInterval(n)),t++},50);else et=e}function I(){return ionic.keyboard.height?ionic.keyboard.height:ionic.Platform.isAndroid()?ionic.Platform.isFullScreen?275:C()1?t[1]:"_");R()}}function R(){var e=ot.width,t=ot.height,n=ionic.Platform,i=n.version(),r="device-width",o="device-height",s=ionic.viewport.orientation();delete ot.height,ot.width=r,n.isIPad()?i>7?delete ot.width:n.isWebView()?90==s?ot.height="0":7==i&&(ot.height=o):7>i&&(ot.height="0"):n.isIOS()&&(n.isWebView()?i>7?delete ot.width:7>i?t&&(ot.height="0"):7==i&&(ot.height=o):7>i&&t&&(ot.height="0")),(e!==ot.width||t!==ot.height)&&V()}function V(){var e,t=[];for(e in ot)ot[e]&&t.push(e+("_"==ot[e]?"":"="+ot[e]));rt.content=t.join(", ")}window.ionic=window.ionic||{},window.ionic.views={},window.ionic.version="1.0.0-beta.13",function(e,t,n){function i(){o=!0;for(var e=0;er;r++)if(n=i[r],n.nodeName&&n.nodeName.toLowerCase()==t){if(n==e)return o;o++}return Array.prototype.slice.call(e.parentNode.children).indexOf(e)},swapNodes:function(e,t){t.parentNode.insertBefore(e,t)},elementIsDescendant:function(e,t,n){var i=e;do{if(i===t)return!0;i=i.parentNode}while(i&&i!==n);return!1},getParentWithClass:function(e,t,n){for(n=n||10;e.parentNode&&n--;){if(e.parentNode.classList&&e.parentNode.classList.contains(t))return e.parentNode;e=e.parentNode}return null},getParentOrSelfWithClass:function(e,t,n){for(n=n||10;e&&n--;){if(e.classList&&e.classList.contains(t))return e;e=e.parentNode}return null},rectContains:function(e,t,n,i,r,o){return n>e||e>r?!1:i>t||t>o?!1:!0}},n.requestAnimationFrame=n.DomUtil.requestAnimationFrame,n.cancelAnimationFrame=n.DomUtil.cancelAnimationFrame,n.animationFrameThrottle=n.DomUtil.animationFrameThrottle}(window,document,ionic),function(e){e.CustomEvent=function(){if("function"==typeof window.CustomEvent)return CustomEvent;var e=function(e,t){var n;t=t||{bubbles:!1,cancelable:!1,detail:void 0};try{n=document.createEvent("CustomEvent"),n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail)}catch(i){n=document.createEvent("Event");for(var r in t)n[r]=t[r];n.initEvent(e,t.bubbles,t.cancelable)}return n};return e.prototype=window.Event.prototype,e}(),e.EventController={VIRTUALIZED_EVENTS:["tap","swipe","swiperight","swipeleft","drag","hold","release"],trigger:function(t,n,i,r){var o=new e.CustomEvent(t,{detail:n,bubbles:!!i,cancelable:!!r});n&&n.target&&n.target.dispatchEvent&&n.target.dispatchEvent(o)||window.dispatchEvent(o)},on:function(t,n,i){for(var r=i||window,o=0,s=this.VIRTUALIZED_EVENTS.length;s>o;o++)if(t==this.VIRTUALIZED_EVENTS[o]){var a=new e.Gesture(i);return a.on(t,n),a}r.addEventListener(t,n)},off:function(e,t,n){n.removeEventListener(e,t)},onGesture:function(t,n,i,r){var o=new e.Gesture(i,r);return o.on(t,n),o},offGesture:function(e,t,n){e.off(t,n)},handlePopState:function(){}},e.on=function(){e.EventController.on.apply(e.EventController,arguments)},e.off=function(){e.EventController.off.apply(e.EventController,arguments)},e.trigger=e.EventController.trigger,e.onGesture=function(){return e.EventController.onGesture.apply(e.EventController.onGesture,arguments)},e.offGesture=function(){return e.EventController.offGesture.apply(e.EventController.offGesture,arguments)}}(window.ionic),function(e){function t(){if(!e.Gestures.READY){e.Gestures.event.determineEventTypes();for(var t in e.Gestures.gestures)e.Gestures.gestures.hasOwnProperty(t)&&e.Gestures.detection.register(e.Gestures.gestures[t]);e.Gestures.event.onTouch(e.Gestures.DOCUMENT,e.Gestures.EVENT_MOVE,e.Gestures.detection.detect),e.Gestures.event.onTouch(e.Gestures.DOCUMENT,e.Gestures.EVENT_END,e.Gestures.detection.detect),e.Gestures.READY=!0}}e.Gesture=function(t,n){return new e.Gestures.Instance(t,n||{})},e.Gestures={},e.Gestures.defaults={stop_browser_behavior:"disable-user-behavior"},e.Gestures.HAS_POINTEREVENTS=window.navigator.pointerEnabled||window.navigator.msPointerEnabled,e.Gestures.HAS_TOUCHEVENTS="ontouchstart"in window,e.Gestures.MOBILE_REGEX=/mobile|tablet|ip(ad|hone|od)|android|silk/i,e.Gestures.NO_MOUSEEVENTS=e.Gestures.HAS_TOUCHEVENTS&&window.navigator.userAgent.match(e.Gestures.MOBILE_REGEX),e.Gestures.EVENT_TYPES={},e.Gestures.DIRECTION_DOWN="down",e.Gestures.DIRECTION_LEFT="left",e.Gestures.DIRECTION_UP="up",e.Gestures.DIRECTION_RIGHT="right",e.Gestures.POINTER_MOUSE="mouse",e.Gestures.POINTER_TOUCH="touch",e.Gestures.POINTER_PEN="pen",e.Gestures.EVENT_START="start",e.Gestures.EVENT_MOVE="move",e.Gestures.EVENT_END="end",e.Gestures.DOCUMENT=window.document,e.Gestures.plugins={},e.Gestures.READY=!1,e.Gestures.Instance=function(n,i){var r=this;if(null!==n)return t(),this.element=n,this.enabled=!0,this.options=e.Gestures.utils.extend(e.Gestures.utils.extend({},e.Gestures.defaults),i||{}),this.options.stop_browser_behavior&&e.Gestures.utils.stopDefaultBrowserBehavior(this.element,this.options.stop_browser_behavior),e.Gestures.event.onTouch(n,e.Gestures.EVENT_START,function(t){r.enabled&&e.Gestures.detection.startDetect(r,t)}),this},e.Gestures.Instance.prototype={on:function(e,t){for(var n=e.split(" "),i=0;i0&&o==e.Gestures.EVENT_END?o=e.Gestures.EVENT_MOVE:u||(o=e.Gestures.EVENT_END),(u||null===n)&&(n=l),s.call(e.Gestures.detection,a.collectEventData(t,o,a.getTouchList(n,o),l)),e.Gestures.HAS_POINTEREVENTS&&o==e.Gestures.EVENT_END&&(u=e.Gestures.PointerEvent.updatePointer(o,l))),u||(n=null,i=!1,r=!1,e.Gestures.PointerEvent.reset())}})},determineEventTypes:function(){var t;t=e.Gestures.HAS_POINTEREVENTS?e.Gestures.PointerEvent.getEvents():e.Gestures.NO_MOUSEEVENTS?["touchstart","touchmove","touchend touchcancel"]:["touchstart mousedown","touchmove mousemove","touchend touchcancel mouseup"],e.Gestures.EVENT_TYPES[e.Gestures.EVENT_START]=t[0],e.Gestures.EVENT_TYPES[e.Gestures.EVENT_MOVE]=t[1],e.Gestures.EVENT_TYPES[e.Gestures.EVENT_END]=t[2]},getTouchList:function(t){return e.Gestures.HAS_POINTEREVENTS?e.Gestures.PointerEvent.getTouchList():t.touches?t.touches:(t.identifier=1,[t])},collectEventData:function(t,n,i,r){var o=e.Gestures.POINTER_TOUCH;return(r.type.match(/mouse/)||e.Gestures.PointerEvent.matchType(e.Gestures.POINTER_MOUSE,r))&&(o=e.Gestures.POINTER_MOUSE),{center:e.Gestures.utils.getCenter(i),timeStamp:(new Date).getTime(),target:r.target,touches:i,eventType:n,pointerType:o,srcEvent:r,preventDefault:function(){this.srcEvent.preventManipulation&&this.srcEvent.preventManipulation(),this.srcEvent.preventDefault},stopPropagation:function(){this.srcEvent.stopPropagation()},stopDetect:function(){return e.Gestures.detection.stopDetect()}}}},e.Gestures.PointerEvent={pointers:{},getTouchList:function(){var e=this,t=[];return Object.keys(e.pointers).sort().forEach(function(n){t.push(e.pointers[n])}),t},updatePointer:function(t,n){return t==e.Gestures.EVENT_END?this.pointers={}:(n.identifier=n.pointerId,this.pointers[n.pointerId]=n),Object.keys(this.pointers).length},matchType:function(t,n){if(!n.pointerType)return!1;var i={};return i[e.Gestures.POINTER_MOUSE]=n.pointerType==n.MSPOINTER_TYPE_MOUSE||n.pointerType==e.Gestures.POINTER_MOUSE,i[e.Gestures.POINTER_TOUCH]=n.pointerType==n.MSPOINTER_TYPE_TOUCH||n.pointerType==e.Gestures.POINTER_TOUCH,i[e.Gestures.POINTER_PEN]=n.pointerType==n.MSPOINTER_TYPE_PEN||n.pointerType==e.Gestures.POINTER_PEN,i[t]},getEvents:function(){return["pointerdown MSPointerDown","pointermove MSPointerMove","pointerup pointercancel MSPointerUp MSPointerCancel"]},reset:function(){this.pointers={}}},e.Gestures.utils={extend:function(e,t,n){for(var i in t)void 0!==e[i]&&n||(e[i]=t[i]);return e},hasParent:function(e,t){for(;e;){if(e==t)return!0;e=e.parentNode}return!1},getCenter:function(e){for(var t=[],n=[],i=0,r=e.length;r>i;i++)t.push(e[i].pageX),n.push(e[i].pageY);return{pageX:(Math.min.apply(Math,t)+Math.max.apply(Math,t))/2,pageY:(Math.min.apply(Math,n)+Math.max.apply(Math,n))/2}},getVelocity:function(e,t,n){return{x:Math.abs(t/e)||0,y:Math.abs(n/e)||0}},getAngle:function(e,t){var n=t.pageY-e.pageY,i=t.pageX-e.pageX;return 180*Math.atan2(n,i)/Math.PI},getDirection:function(t,n){var i=Math.abs(t.pageX-n.pageX),r=Math.abs(t.pageY-n.pageY);return i>=r?t.pageX-n.pageX>0?e.Gestures.DIRECTION_LEFT:e.Gestures.DIRECTION_RIGHT:t.pageY-n.pageY>0?e.Gestures.DIRECTION_UP:e.Gestures.DIRECTION_DOWN},getDistance:function(e,t){var n=t.pageX-e.pageX,i=t.pageY-e.pageY;return Math.sqrt(n*n+i*i)},getScale:function(e,t){return e.length>=2&&t.length>=2?this.getDistance(t[0],t[1])/this.getDistance(e[0],e[1]):1},getRotation:function(e,t){return e.length>=2&&t.length>=2?this.getAngle(t[1],t[0])-this.getAngle(e[1],e[0]):0},isVertical:function(t){return t==e.Gestures.DIRECTION_UP||t==e.Gestures.DIRECTION_DOWN},stopDefaultBrowserBehavior:function(e,t){e&&e.classList&&(e.classList.add(t),e.onselectstart=function(){return!1})}},e.Gestures.detection={gestures:[],current:null,previous:null,stopped:!1,startDetect:function(t,n){this.current||(this.stopped=!1,this.current={inst:t,startEvent:e.Gestures.utils.extend({},n),lastEvent:!1,name:""},this.detect(n))},detect:function(t){if(this.current&&!this.stopped){t=this.extendEventData(t);for(var n=this.current.inst.options,i=0,r=this.gestures.length;r>i;i++){var o=this.gestures[i];if(!this.stopped&&n[o.name]!==!1&&o.handler.call(o,t,this.current.inst)===!1){this.stopDetect();break}}return this.current&&(this.current.lastEvent=t),t.eventType==e.Gestures.EVENT_END&&!t.touches.length-1&&this.stopDetect(),t}},stopDetect:function(){this.previous=e.Gestures.utils.extend({},this.current),this.current=null,this.stopped=!0},extendEventData:function(t){var n=this.current.startEvent;if(n&&(t.touches.length!=n.touches.length||t.touches===n.touches)){n.touches=[];for(var i=0,r=t.touches.length;r>i;i++)n.touches.push(e.Gestures.utils.extend({},t.touches[i]))}var o=t.timeStamp-n.timeStamp,s=t.center.pageX-n.center.pageX,a=t.center.pageY-n.center.pageY,l=e.Gestures.utils.getVelocity(o,s,a);return e.Gestures.utils.extend(t,{deltaTime:o,deltaX:s,deltaY:a,velocityX:l.x,velocityY:l.y,distance:e.Gestures.utils.getDistance(n.center,t.center),angle:e.Gestures.utils.getAngle(n.center,t.center),direction:e.Gestures.utils.getDirection(n.center,t.center),scale:e.Gestures.utils.getScale(n.touches,t.touches),rotation:e.Gestures.utils.getRotation(n.touches,t.touches),startEvent:n}),t},register:function(t){var n=t.defaults||{};return void 0===n[t.name]&&(n[t.name]=!0),e.Gestures.utils.extend(e.Gestures.defaults,n,!0),t.index=t.index||1e3,this.gestures.push(t),this.gestures.sort(function(e,t){return e.indext.index?1:0}),this.gestures}},e.Gestures.gestures=e.Gestures.gestures||{},e.Gestures.gestures.Hold={name:"hold",index:10,defaults:{hold_timeout:500,hold_threshold:1},timer:null,handler:function(t,n){switch(t.eventType){case e.Gestures.EVENT_START:clearTimeout(this.timer),e.Gestures.detection.current.name=this.name,this.timer=setTimeout(function(){"hold"==e.Gestures.detection.current.name&&(e.tap.cancelClick(),n.trigger("hold",t))},n.options.hold_timeout);break;case e.Gestures.EVENT_MOVE:t.distance>n.options.hold_threshold&&clearTimeout(this.timer);break;case e.Gestures.EVENT_END:clearTimeout(this.timer)}}},e.Gestures.gestures.Tap={name:"tap",index:100,defaults:{tap_max_touchtime:250,tap_max_distance:10,tap_always:!0,doubletap_distance:20,doubletap_interval:300},handler:function(t,n){if(t.eventType==e.Gestures.EVENT_END&&"touchcancel"!=t.srcEvent.type){var i=e.Gestures.detection.previous,r=!1;if(t.deltaTime>n.options.tap_max_touchtime||t.distance>n.options.tap_max_distance)return;i&&"tap"==i.name&&t.timeStamp-i.lastEvent.timeStamp0&&t.touches.length>n.options.swipe_max_touches)return;(t.velocityX>n.options.swipe_velocity||t.velocityY>n.options.swipe_velocity)&&(n.trigger(this.name,t),n.trigger(this.name+t.direction,t))}}},e.Gestures.gestures.Drag={name:"drag",index:50,defaults:{drag_min_distance:10,correct_for_drag_min_distance:!0,drag_max_touches:1,drag_block_horizontal:!0,drag_block_vertical:!0,drag_lock_to_axis:!1,drag_lock_min_distance:25},triggered:!1,handler:function(t,n){if(e.Gestures.detection.current.name!=this.name&&this.triggered)return n.trigger(this.name+"end",t),void(this.triggered=!1);if(!(n.options.drag_max_touches>0&&t.touches.length>n.options.drag_max_touches))switch(t.eventType){case e.Gestures.EVENT_START:this.triggered=!1;break;case e.Gestures.EVENT_MOVE:if(t.distancen.options.transform_min_rotation&&n.trigger("rotate",t),i>n.options.transform_min_scale&&(n.trigger("pinch",t),n.trigger("pinch"+(t.scale<1?"in":"out"),t));break;case e.Gestures.EVENT_END:this.triggered&&n.trigger(this.name+"end",t),this.triggered=!1}}},e.Gestures.gestures.Touch={name:"touch",index:-1/0,defaults:{prevent_default:!1,prevent_mouseevents:!1},handler:function(t,n){return n.options.prevent_mouseevents&&t.pointerType==e.Gestures.POINTER_MOUSE?void t.stopDetect():(n.options.prevent_default&&t.preventDefault(),void(t.eventType==e.Gestures.EVENT_START&&n.trigger(this.name,t)))}},e.Gestures.gestures.Release={name:"release",index:1/0,handler:function(t,n){t.eventType==e.Gestures.EVENT_END&&n.trigger(this.name,t)}}}(window.ionic),function(e,t,n){function i(){n.Platform.isWebView()?t.addEventListener("deviceready",r,!1):r(),l&&e.removeEventListener("load",i,!1)}function r(){n.Platform.isReady=!0,n.Platform.detect();for(var e=0;e0?i=i.replace(".","_"):i+="_0",this.platforms.push(t+i.split("_")[0]),this.platforms.push(t+i),this.isAndroid()&&4.4>n?e=4>n?"c":"b":this.isWindowsPhone()&&(e="b")}}this.setGrade(e)},isWebView:function(){return!(!e.cordova&&!e.PhoneGap&&!e.phonegap)},isIPad:function(){return/iPad/i.test(n.Platform.navigator.platform)?!0:/iPad/i.test(this.ua)},isIOS:function(){return this.is(o)},isAndroid:function(){return this.is(s)},isWindowsPhone:function(){return this.is(a)},platform:function(){return null===c&&this.setPlatform(this.device().platform),c},setPlatform:function(e){c="undefined"!=typeof e&&null!==e&&e.length?e.toLowerCase():this.ua.indexOf("Android")>0?s:this.ua.indexOf("iPhone")>-1||this.ua.indexOf("iPad")>-1||this.ua.indexOf("iPod")>-1?o:this.ua.indexOf("Windows Phone")>-1?a:n.Platform.navigator.platform&&navigator.platform.toLowerCase().split(" ")[0]||""},version:function(){return null===u&&this.setVersion(this.device().version),u},setVersion:function(e){if("undefined"!=typeof e&&null!==e&&(e=e.split("."),e=parseFloat(e[0]+"."+(e.length>1?e[1]:0)),!isNaN(e)))return void(u=e);u=0;var t=this.platform(),n={android:/Android (\d+).(\d+)?/,ios:/OS (\d+)_(\d+)?/,windowsphone:/Windows Phone (\d+).(\d+)?/};n[t]&&(e=this.ua.match(n[t]),e&&e.length>2&&(u=parseFloat(e[1]+"."+e[2])))},is:function(e){if(e=e.toLowerCase(),this.platforms)for(var t=0;t=0},exitApp:function(){this.ready(function(){navigator.app&&navigator.app.exitApp&&navigator.app.exitApp()})},showStatusBar:function(i){this._showStatusBar=i,this.ready(function(){n.requestAnimationFrame(function(){n.Platform._showStatusBar?(e.StatusBar&&e.StatusBar.show(),t.body.classList.remove("status-bar-hide")):(e.StatusBar&&e.StatusBar.hide(),t.body.classList.add("status-bar-hide"))})})},fullScreen:function(e,i){this.isFullScreen=e!==!1,n.DomUtil.ready(function(){n.requestAnimationFrame(function(){panes=t.getElementsByClassName("pane");for(var e=0;el&&(s&&1===s.nodeType);l++){if(r&&s.classList.contains("item")){r=s;break}if("A"==s.tagName||"BUTTON"==s.tagName||s.hasAttribute("ng-click")){r=s;
+break}if(s.classList.contains("button")){r=s;break}if(s.classList.contains("pane")||"BODY"==s.tagName||"ION-CONTENT"==s.tagName)break;s=s.parentElement}r&&(o[a]=r,"touchstart"===e.type?n._activateTimeout=setTimeout(i,80):t.requestAnimationFrame(i),a=a>19?0:a+1)}})},end:function(){clearTimeout(this._activateTimeout),setTimeout(n,200)}}}(document,ionic),function(e){var t=["0","0","0"];e.Utils={arrayMove:function(e,t,n){if(n>=e.length)for(var i=n-e.length;i--+1;)e.push(void 0);return e.splice(n,0,e.splice(t,1)[0]),e},proxy:function(e,t){var n=Array.prototype.slice.call(arguments,2);return function(){return e.apply(t,n.concat(Array.prototype.slice.call(arguments)))}},debounce:function(e,t,n){var i,r,o,s,a;return function(){o=this,r=arguments,s=new Date;var l=function(){var c=new Date-s;t>c?i=setTimeout(l,t-c):(i=null,n||(a=e.apply(o,r)))},c=n&&!i;return i||(i=setTimeout(l,t)),c&&(a=e.apply(o,r)),a}},throttle:function(e,t,n){var i,r,o,s=null,a=0;n||(n={});var l=function(){a=n.leading===!1?0:Date.now(),s=null,o=e.apply(i,r)};return function(){var c=Date.now();a||n.leading!==!1||(a=c);var u=t-(c-a);return i=this,r=arguments,0>=u?(clearTimeout(s),s=null,a=c,o=e.apply(i,r)):s||n.trailing===!1||(s=setTimeout(l,u)),o}},inherit:function(t,n){var i,r=this;i=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return r.apply(this,arguments)},e.extend(i,r,n);var o=function(){this.constructor=i};return o.prototype=r.prototype,i.prototype=new o,t&&e.extend(i.prototype,t),i.__super__=r.prototype,i},extend:function(e){for(var t=Array.prototype.slice.call(arguments,1),n=0;nwindow.innerHeight?90:0}},ionic.Platform.ready(function(){O(),window.addEventListener("orientationchange",function(){setTimeout(R,1e3)},!1)}),function(e){"use strict";e.views.View=function(){this.initialize.apply(this,arguments)},e.views.View.inherit=e.inherit,e.extend(e.views.View.prototype,{initialize:function(){}})}(window.ionic);var st={effect:{}};!function(e){var t=Date.now||function(){return+new Date},n=60,i=1e3,r={},o=1;st.effect.Animate={requestAnimationFrame:function(){var t=e.requestAnimationFrame||e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame,n=!!t;if(t&&!/requestAnimationFrame\(\)\s*\{\s*\[native code\]\s*\}/i.test(t.toString())&&(n=!1),n)return function(e,n){t(e,n)};var i=60,r={},o=0,s=1,a=null,l=+new Date;return function(e){var t=s++;return r[t]=e,o++,null===a&&(a=setInterval(function(){var e=+new Date,t=r;r={},o=0;for(var n in t)t.hasOwnProperty(n)&&(t[n](e),l=e);e-l>2500&&(clearInterval(a),a=null)},1e3/i)),t}}(),stop:function(e){var t=null!=r[e];return t&&(r[e]=null),t},isRunning:function(e){return null!=r[e]},start:function(e,s,a,l,c,u){var h=t(),d=h,_=0,f=0,p=o++;if(u||(u=document.body),p%20===0){var m={};for(var g in r)m[g]=!0;r=m}var v=function(o){var m=o!==!0,g=t();if(!r[p]||s&&!s(p))return r[p]=null,void(a&&a(n-f/((g-h)/i),p,!1));if(m)for(var T=Math.round((g-d)/(i/n))-1,E=0;E1&&(_=1));var S=c?c(_):_;e(S,g,m)!==!1&&1!==_||!m?m&&(d=g,st.effect.Animate.requestAnimationFrame(v,u)):(r[p]=null,a&&a(n-f/((g-h)/i),p,1===_||null==l))};return r[p]=!0,st.effect.Animate.requestAnimationFrame(v,u),p}}}(this);!function(e){var t=function(){},n=function(e){return Math.pow(e-1,3)+1},i=function(e){return(e/=.5)<1?.5*Math.pow(e,3):.5*(Math.pow(e-2,3)+2)};e.views.Scroll=e.views.View.inherit({initialize:function(n){var i=this;this.__container=n.el,this.__content=n.el.firstElementChild,setTimeout(function(){i.__container&&i.__content&&(i.__container.scrollTop=0,i.__content.scrollTop=0)}),this.options={scrollingX:!1,scrollbarX:!0,scrollingY:!0,scrollbarY:!0,startX:0,startY:0,wheelDampen:6,minScrollbarSizeX:5,minScrollbarSizeY:5,scrollbarsFade:!0,scrollbarFadeDelay:300,scrollbarResizeFadeDelay:1e3,animating:!0,animationDuration:250,bouncing:!0,locking:!0,paging:!1,snapping:!1,zooming:!1,minZoom:.5,maxZoom:3,speedMultiplier:1,deceleration:.97,scrollingComplete:t,penetrationDeceleration:.03,penetrationAcceleration:.08,scrollEventInterval:10,getContentWidth:function(){return Math.max(i.__content.scrollWidth,i.__content.offsetWidth)},getContentHeight:function(){return Math.max(i.__content.scrollHeight,i.__content.offsetHeight+2*i.__content.offsetTop)}};for(var r in n)this.options[r]=n[r];this.hintResize=e.debounce(function(){i.resize()},1e3,!0),this.onScroll=function(){e.scroll.isScrolling?(clearTimeout(i.scrollTimer),i.scrollTimer=setTimeout(i.setScrollStop,80)):setTimeout(i.setScrollStart,50)},this.setScrollStart=function(){e.scroll.isScrolling=Math.abs(e.scroll.lastTop-i.__scrollTop)>1,clearTimeout(i.scrollTimer),i.scrollTimer=setTimeout(i.setScrollStop,80)},this.setScrollStop=function(){e.scroll.isScrolling=!1,e.scroll.lastTop=i.__scrollTop},this.triggerScrollEvent=e.throttle(function(){i.onScroll(),e.trigger("scroll",{scrollTop:i.__scrollTop,scrollLeft:i.__scrollLeft,target:i.__container})},this.options.scrollEventInterval),this.triggerScrollEndEvent=function(){e.trigger("scrollend",{scrollTop:i.__scrollTop,scrollLeft:i.__scrollLeft,target:i.__container})},this.__scrollLeft=this.options.startX,this.__scrollTop=this.options.startY,this.__callback=this.getRenderFn(),this.__initEventHandlers(),this.__createScrollbars()},run:function(){this.resize(),this.__fadeScrollbars("out",this.options.scrollbarResizeFadeDelay)},__isSingleTouch:!1,__isTracking:!1,__didDecelerationComplete:!1,__isGesturing:!1,__isDragging:!1,__isDecelerating:!1,__isAnimating:!1,__clientLeft:0,__clientTop:0,__clientWidth:0,__clientHeight:0,__contentWidth:0,__contentHeight:0,__snapWidth:100,__snapHeight:100,__refreshHeight:null,__refreshActive:!1,__refreshActivate:null,__refreshDeactivate:null,__refreshStart:null,__zoomLevel:1,__scrollLeft:0,__scrollTop:0,__maxScrollLeft:0,__maxScrollTop:0,__scheduledLeft:0,__scheduledTop:0,__scheduledZoom:0,__lastTouchLeft:null,__lastTouchTop:null,__lastTouchMove:null,__positions:null,__minDecelerationScrollLeft:null,__minDecelerationScrollTop:null,__maxDecelerationScrollLeft:null,__maxDecelerationScrollTop:null,__decelerationVelocityX:null,__decelerationVelocityY:null,__transformProperty:null,__perspectiveProperty:null,__indicatorX:null,__indicatorY:null,__scrollbarFadeTimeout:null,__didWaitForSize:null,__sizerTimeout:null,__initEventHandlers:function(){function t(e){return e.touches&&e.touches.length?e.touches:[{pageX:e.pageX,pageY:e.pageY}]}var n=this,i=this.__container;if(n.scrollChildIntoView=function(t){var r;if(!n.isScrolledIntoView){if(e.Platform.isIOS()||e.Platform.isFullScreen){r=i.getBoundingClientRect().bottom;var o=t.detail.viewportHeight-r,s=Math.max(0,t.detail.keyboardHeight-o);i.style.height=i.clientHeight-s+"px",i.style.overflow="visible",n.resize()}n.isScrolledIntoView=!0}if(t.detail.isElementUnderKeyboard){var a;a=e.Platform.isAndroid()&&!e.Platform.isFullScreen?e.Platform.version()<4.4?500:350:80,e.scroll.isScrolling=!0,setTimeout(function(){var o=.5*i.clientHeight;r=i.getBoundingClientRect().bottom;var s=t.detail.elementTop-r,a=s+o;a>0&&(e.tap.cloneFocusedInput(i,n),n.scrollBy(0,a,!0),n.onScroll())},a)}t.stopPropagation()},n.resetScrollView=function(){n.isScrolledIntoView&&(n.isScrolledIntoView=!1,i.style.height="",i.style.overflow="",n.resize(),e.scroll.isScrolling=!1)},i.addEventListener("scrollChildIntoView",n.scrollChildIntoView),i.addEventListener("resetScrollView",n.resetScrollView),n.touchStart=function(i){if(n.startCoordinates=e.tap.pointerCoord(i),!e.tap.ignoreScrollStart(i)){if(n.__isDown=!0,e.tap.containsOrIsTextInput(i.target)||"SELECT"===i.target.tagName)return void(n.__hasStarted=!1);n.__isSelectable=!0,n.__enableScrollY=!0,n.__hasStarted=!0,n.doTouchStart(t(i),i.timeStamp),i.preventDefault()}},n.touchMove=function(r){if(!(!n.__isDown||r.defaultPrevented||"TEXTAREA"===r.target.tagName&&r.target.parentElement.querySelector(":focus"))){if(!n.__hasStarted&&(e.tap.containsOrIsTextInput(r.target)||"SELECT"===r.target.tagName))return n.__hasStarted=!0,n.doTouchStart(t(r),r.timeStamp),void r.preventDefault();if(n.startCoordinates){var o=e.tap.pointerCoord(r);n.__isSelectable&&e.tap.isTextInput(r.target)&&Math.abs(n.startCoordinates.x-o.x)>20&&(n.__enableScrollY=!1,n.__isSelectable=!0),n.__enableScrollY&&Math.abs(n.startCoordinates.y-o.y)>10&&(n.__isSelectable=!1,e.tap.cloneFocusedInput(i,n))}n.doTouchMove(t(r),r.timeStamp,r.scale),n.__isDown=!0}},n.touchEnd=function(t){n.__isDown&&(n.doTouchEnd(t.timeStamp),n.__isDown=!1,n.__hasStarted=!1,n.__isSelectable=!0,n.__enableScrollY=!0,n.__isDragging||n.__isDecelerating||n.__isAnimating||e.tap.removeClonedInputs(i,n))},"ontouchstart"in window)i.addEventListener("touchstart",n.touchStart,!1),document.addEventListener("touchmove",n.touchMove,!1),document.addEventListener("touchend",n.touchEnd,!1),document.addEventListener("touchcancel",n.touchEnd,!1);else if(window.navigator.pointerEnabled)i.addEventListener("pointerdown",n.touchStart,!1),document.addEventListener("pointermove",n.touchMove,!1),document.addEventListener("pointerup",n.touchEnd,!1),document.addEventListener("pointercancel",n.touchEnd,!1);else if(window.navigator.msPointerEnabled)i.addEventListener("MSPointerDown",n.touchStart,!1),document.addEventListener("MSPointerMove",n.touchMove,!1),document.addEventListener("MSPointerUp",n.touchEnd,!1),document.addEventListener("MSPointerCancel",n.touchEnd,!1);else{var r=!1;n.mouseDown=function(i){e.tap.ignoreScrollStart(i)||"SELECT"===i.target.tagName||(n.doTouchStart(t(i),i.timeStamp),e.tap.isTextInput(i.target)||i.preventDefault(),r=!0)},n.mouseMove=function(e){r&&!e.defaultPrevented&&(n.doTouchMove(t(e),e.timeStamp),r=!0)},n.mouseUp=function(e){r&&(n.doTouchEnd(e.timeStamp),r=!1)},n.mouseWheel=e.animationFrameThrottle(function(t){var i=e.DomUtil.getParentOrSelfWithClass(t.target,"ionic-scroll");i===n.__container&&(n.hintResize(),n.scrollBy(t.wheelDeltaX/n.options.wheelDampen,-t.wheelDeltaY/n.options.wheelDampen),n.__fadeScrollbars("in"),clearTimeout(n.__wheelHideBarTimeout),n.__wheelHideBarTimeout=setTimeout(function(){n.__fadeScrollbars("out")},100))}),i.addEventListener("mousedown",n.mouseDown,!1),document.addEventListener("mousemove",n.mouseMove,!1),document.addEventListener("mouseup",n.mouseUp,!1),document.addEventListener("mousewheel",n.mouseWheel,!1)}},__cleanup:function(){var t=this.__container,n=this;t.removeEventListener("touchstart",n.touchStart),document.removeEventListener("touchmove",n.touchMove),document.removeEventListener("touchend",n.touchEnd),document.removeEventListener("touchcancel",n.touchCancel),t.removeEventListener("pointerdown",n.touchStart),document.removeEventListener("pointermove",n.touchMove),document.removeEventListener("pointerup",n.touchEnd),document.removeEventListener("pointercancel",n.touchEnd),t.removeEventListener("MSPointerDown",n.touchStart),document.removeEventListener("MSPointerMove",n.touchMove),document.removeEventListener("MSPointerUp",n.touchEnd),document.removeEventListener("MSPointerCancel",n.touchEnd),t.removeEventListener("mousedown",n.mouseDown),document.removeEventListener("mousemove",n.mouseMove),document.removeEventListener("mouseup",n.mouseUp),document.removeEventListener("mousewheel",n.mouseWheel),t.removeEventListener("scrollChildIntoView",n.scrollChildIntoView),t.removeEventListener("resetScrollView",n.resetScrollView),e.tap.removeClonedInputs(t,n),delete this.__container,delete this.__content,delete this.__indicatorX,delete this.__indicatorY,delete this.options.el,this.__callback=this.scrollChildIntoView=this.resetScrollView=angular.noop,this.mouseMove=this.mouseDown=this.mouseUp=this.mouseWheel=this.touchStart=this.touchMove=this.touchEnd=this.touchCancel=angular.noop,this.resize=this.scrollTo=this.zoomTo=this.__scrollingComplete=angular.noop,t=null},__createScrollbar:function(e){var t=document.createElement("div"),n=document.createElement("div");return n.className="scroll-bar-indicator scroll-bar-fade-out",t.className="h"==e?"scroll-bar scroll-bar-h":"scroll-bar scroll-bar-v",t.appendChild(n),t},__createScrollbars:function(){var e,t;this.options.scrollingX&&(e={el:this.__createScrollbar("h"),sizeRatio:1},e.indicator=e.el.children[0],this.options.scrollbarX&&this.__container.appendChild(e.el),this.__indicatorX=e),this.options.scrollingY&&(t={el:this.__createScrollbar("v"),sizeRatio:1},t.indicator=t.el.children[0],this.options.scrollbarY&&this.__container.appendChild(t.el),this.__indicatorY=t)},__resizeScrollbars:function(){var e=this;if(e.__indicatorX){var t=Math.max(Math.round(e.__clientWidth*e.__clientWidth/e.__contentWidth),20);t>e.__contentWidth&&(t=0),e.__indicatorX.size=t,e.__indicatorX.minScale=this.options.minScrollbarSizeX/t,e.__indicatorX.indicator.style.width=t+"px",e.__indicatorX.maxPos=e.__clientWidth-t,e.__indicatorX.sizeRatio=e.__maxScrollLeft?e.__indicatorX.maxPos/e.__maxScrollLeft:1}if(e.__indicatorY){var n=Math.max(Math.round(e.__clientHeight*e.__clientHeight/e.__contentHeight),20);n>e.__contentHeight&&(n=0),e.__indicatorY.size=n,e.__indicatorY.minScale=this.options.minScrollbarSizeY/n,e.__indicatorY.maxPos=e.__clientHeight-n,e.__indicatorY.indicator.style.height=n+"px",e.__indicatorY.sizeRatio=e.__maxScrollTop?e.__indicatorY.maxPos/e.__maxScrollTop:1}},__repositionScrollbars:function(){var e,t,n,i,r,o=this,s=0,a=0;o.__indicatorX&&(o.__indicatorY&&(s=10),i=Math.round(o.__indicatorX.sizeRatio*o.__scrollLeft)||0,t=o.__scrollLeft-(o.__maxScrollLeft-s),o.__scrollLeft<0?(widthScale=Math.max(o.__indicatorX.minScale,(o.__indicatorX.size-Math.abs(o.__scrollLeft))/o.__indicatorX.size),i=0,o.__indicatorX.indicator.style[o.__transformOriginProperty]="left center"):t>0?(widthScale=Math.max(o.__indicatorX.minScale,(o.__indicatorX.size-t)/o.__indicatorX.size),i=o.__indicatorX.maxPos-s,o.__indicatorX.indicator.style[o.__transformOriginProperty]="right center"):(i=Math.min(o.__maxScrollLeft,Math.max(0,i)),widthScale=1),o.__indicatorX.indicator.style[o.__transformProperty]="translate3d("+i+"px, 0, 0) scaleX("+widthScale+")"),o.__indicatorY&&(r=Math.round(o.__indicatorY.sizeRatio*o.__scrollTop)||0,o.__indicatorX&&(a=10),n=o.__scrollTop-(o.__maxScrollTop-a),o.__scrollTop<0?(e=Math.max(o.__indicatorY.minScale,(o.__indicatorY.size-Math.abs(o.__scrollTop))/o.__indicatorY.size),r=0,o.__indicatorY.indicator.style[o.__transformOriginProperty]="center top"):n>0?(e=Math.max(o.__indicatorY.minScale,(o.__indicatorY.size-n)/o.__indicatorY.size),r=o.__indicatorY.maxPos-a,o.__indicatorY.indicator.style[o.__transformOriginProperty]="center bottom"):(r=Math.min(o.__maxScrollTop,Math.max(0,r)),e=1),o.__indicatorY.indicator.style[o.__transformProperty]="translate3d(0,"+r+"px, 0) scaleY("+e+")")},__fadeScrollbars:function(e,t){var n=this;if(this.options.scrollbarsFade){var i="scroll-bar-fade-out";n.options.scrollbarsFade===!0&&(clearTimeout(n.__scrollbarFadeTimeout),"in"==e?(n.__indicatorX&&n.__indicatorX.indicator.classList.remove(i),n.__indicatorY&&n.__indicatorY.indicator.classList.remove(i)):n.__scrollbarFadeTimeout=setTimeout(function(){n.__indicatorX&&n.__indicatorX.indicator.classList.add(i),n.__indicatorY&&n.__indicatorY.indicator.classList.add(i)},t||n.options.scrollbarFadeDelay))}},__scrollingComplete:function(){var t=this;t.options.scrollingComplete(),e.tap.removeClonedInputs(t.__container,t),t.__fadeScrollbars("out")},resize:function(){this.__container&&this.options&&this.setDimensions(this.__container.clientWidth,this.__container.clientHeight,this.options.getContentWidth(),this.options.getContentHeight())},getRenderFn:function(){var e,t=this,n=this.__content,i=document.documentElement.style;"MozAppearance"in i?e="gecko":"WebkitAppearance"in i?e="webkit":"string"==typeof navigator.cpuClass&&(e="trident");var r,o={trident:"ms",gecko:"Moz",webkit:"Webkit",presto:"O"}[e],s=document.createElement("div"),a=o+"Perspective",l=o+"Transform",c=o+"TransformOrigin";return t.__perspectiveProperty=l,t.__transformProperty=l,t.__transformOriginProperty=c,s.style[a]!==r?function(e,i,r,o){n.style[l]="translate3d("+-e+"px,"+-i+"px,0) scale("+r+")",t.__repositionScrollbars(),o||t.triggerScrollEvent()}:s.style[l]!==r?function(e,i,r,o){n.style[l]="translate("+-e+"px,"+-i+"px) scale("+r+")",t.__repositionScrollbars(),o||t.triggerScrollEvent()}:function(e,i,r,o){n.style.marginLeft=e?-e/r+"px":"",n.style.marginTop=i?-i/r+"px":"",n.style.zoom=r||"",t.__repositionScrollbars(),o||t.triggerScrollEvent()}},setDimensions:function(e,t,n,i){var r=this;e===+e&&(r.__clientWidth=e),t===+t&&(r.__clientHeight=t),n===+n&&(r.__contentWidth=n),i===+i&&(r.__contentHeight=i),r.__computeScrollMax(),r.__resizeScrollbars(),r.scrollTo(r.__scrollLeft,r.__scrollTop,!0,null,!0)},setPosition:function(e,t){var n=this;n.__clientLeft=e||0,n.__clientTop=t||0},setSnapSize:function(e,t){var n=this;n.__snapWidth=e,n.__snapHeight=t},activatePullToRefresh:function(t,n,i,r,o,s,a){var l=this;l.__refreshHeight=t,l.__refreshActivate=function(){e.requestAnimationFrame(n)},l.__refreshDeactivate=function(){e.requestAnimationFrame(i)},l.__refreshStart=function(){e.requestAnimationFrame(r)},l.__refreshShow=function(){e.requestAnimationFrame(o)},l.__refreshHide=function(){e.requestAnimationFrame(s)},l.__refreshTail=function(){e.requestAnimationFrame(a)},l.__refreshTailTime=100,l.__minSpinTime=600},triggerPullToRefresh:function(){this.__publish(this.__scrollLeft,-this.__refreshHeight,this.__zoomLevel,!0);var e=new Date;self.refreshStartTime=e.getTime(),this.__refreshStart&&this.__refreshStart()},finishPullToRefresh:function(){var e=this,t=new Date,n=0;e.refreshStartTime+e.__minSpinTime>t.getTime()&&(n=e.refreshStartTime+e.__minSpinTime-t.getTime()),setTimeout(function(){e.__refreshTail&&e.__refreshTail(),setTimeout(function(){e.__refreshActive=!1,e.__refreshDeactivate&&e.__refreshDeactivate(),e.scrollTo(e.__scrollLeft,e.__scrollTop,!0)},e.__refreshTailTime)},n)},getValues:function(){var e=this;return{left:e.__scrollLeft,top:e.__scrollTop,zoom:e.__zoomLevel}},getScrollMax:function(){var e=this;return{left:e.__maxScrollLeft,top:e.__maxScrollTop}},zoomTo:function(e,t,n,i){var r=this;if(!r.options.zooming)throw new Error("Zooming is not enabled!");r.__isDecelerating&&(st.effect.Animate.stop(r.__isDecelerating),r.__isDecelerating=!1);var o=r.__zoomLevel;null==n&&(n=r.__clientWidth/2),null==i&&(i=r.__clientHeight/2),e=Math.max(Math.min(e,r.options.maxZoom),r.options.minZoom),r.__computeScrollMax(e);var s=(n+r.__scrollLeft)*e/o-n,a=(i+r.__scrollTop)*e/o-i;s>r.__maxScrollLeft?s=r.__maxScrollLeft:0>s&&(s=0),a>r.__maxScrollTop?a=r.__maxScrollTop:0>a&&(a=0),r.__publish(s,a,e,t)},zoomBy:function(e,t,n,i){var r=this;r.zoomTo(r.__zoomLevel*e,t,n,i)},scrollTo:function(e,t,n,i,r){var o=this;if(o.__isDecelerating&&(st.effect.Animate.stop(o.__isDecelerating),o.__isDecelerating=!1),null!=i&&i!==o.__zoomLevel){if(!o.options.zooming)throw new Error("Zooming is not enabled!");e*=i,t*=i,o.__computeScrollMax(i)}else i=o.__zoomLevel;o.options.scrollingX?o.options.paging?e=Math.round(e/o.__clientWidth)*o.__clientWidth:o.options.snapping&&(e=Math.round(e/o.__snapWidth)*o.__snapWidth):e=o.__scrollLeft,o.options.scrollingY?o.options.paging?t=Math.round(t/o.__clientHeight)*o.__clientHeight:o.options.snapping&&(t=Math.round(t/o.__snapHeight)*o.__snapHeight):t=o.__scrollTop,e=Math.max(Math.min(o.__maxScrollLeft,e),0),t=Math.max(Math.min(o.__maxScrollTop,t),0),e===o.__scrollLeft&&t===o.__scrollTop&&(n=!1),o.__publish(e,t,i,n,r)},scrollBy:function(e,t,n){var i=this,r=i.__isAnimating?i.__scheduledLeft:i.__scrollLeft,o=i.__isAnimating?i.__scheduledTop:i.__scrollTop;i.scrollTo(r+(e||0),o+(t||0),n)},doMouseZoom:function(e,t,n,i){var r=this,o=e>0?.97:1.03;return r.zoomTo(r.__zoomLevel*o,!1,n-r.__clientLeft,i-r.__clientTop)},doTouchStart:function(e,t){this.hintResize(),t instanceof Date&&(t=t.valueOf()),"number"!=typeof t&&(t=Date.now());var n=this;n.__interruptedAnimation=!0,n.__isDecelerating&&(st.effect.Animate.stop(n.__isDecelerating),n.__isDecelerating=!1,n.__interruptedAnimation=!0),n.__isAnimating&&(st.effect.Animate.stop(n.__isAnimating),n.__isAnimating=!1,n.__interruptedAnimation=!0);var i,r,o=1===e.length;o?(i=e[0].pageX,r=e[0].pageY):(i=Math.abs(e[0].pageX+e[1].pageX)/2,r=Math.abs(e[0].pageY+e[1].pageY)/2),n.__initialTouchLeft=i,n.__initialTouchTop=r,n.__initialTouches=e,n.__zoomLevelStart=n.__zoomLevel,n.__lastTouchLeft=i,n.__lastTouchTop=r,n.__lastTouchMove=t,n.__lastScale=1,n.__enableScrollX=!o&&n.options.scrollingX,n.__enableScrollY=!o&&n.options.scrollingY,n.__isTracking=!0,n.__didDecelerationComplete=!1,n.__isDragging=!o,n.__isSingleTouch=o,n.__positions=[]},doTouchMove:function(e,t,n){t instanceof Date&&(t=t.valueOf()),"number"!=typeof t&&(t=Date.now());var i=this;if(i.__isTracking){var r,o;2===e.length?(r=Math.abs(e[0].pageX+e[1].pageX)/2,o=Math.abs(e[0].pageY+e[1].pageY)/2,!n&&i.options.zooming&&(n=i.__getScale(i.__initialTouches,e))):(r=e[0].pageX,o=e[0].pageY);var s=i.__positions;if(i.__isDragging){var a=r-i.__lastTouchLeft,l=o-i.__lastTouchTop,c=i.__scrollLeft,u=i.__scrollTop,h=i.__zoomLevel;if(null!=n&&i.options.zooming){var d=h;if(h=h/i.__lastScale*n,h=Math.max(Math.min(h,i.options.maxZoom),i.options.minZoom),d!==h){var _=r-i.__clientLeft,f=o-i.__clientTop;c=(_+c)*h/d-_,u=(f+u)*h/d-f,i.__computeScrollMax(h)}}if(i.__enableScrollX){c-=a*this.options.speedMultiplier;var p=i.__maxScrollLeft;(c>p||0>c)&&(i.options.bouncing?c+=a/2*this.options.speedMultiplier:c=c>p?p:0)}if(i.__enableScrollY){u-=l*this.options.speedMultiplier;var m=i.__maxScrollTop;u>m||0>u?i.options.bouncing||i.__refreshHeight&&0>u?(u+=l/2*this.options.speedMultiplier,i.__enableScrollX||null==i.__refreshHeight||(0>u?(i.__refreshHidden=!1,i.__refreshShow()):(i.__refreshHide(),i.__refreshHidden=!0),!i.__refreshActive&&u<=-i.__refreshHeight?(i.__refreshActive=!0,i.__refreshActivate&&i.__refreshActivate()):i.__refreshActive&&u>-i.__refreshHeight&&(i.__refreshActive=!1,i.__refreshDeactivate&&i.__refreshDeactivate()))):u=u>m?m:0:i.__refreshHeight&&!i.__refreshHidden&&(i.__refreshHide(),i.__refreshHidden=!0)}s.length>60&&s.splice(0,30),s.push(c,u,t),i.__publish(c,u,h)}else{var g=i.options.locking?3:0,v=5,T=Math.abs(r-i.__initialTouchLeft),E=Math.abs(o-i.__initialTouchTop);i.__enableScrollX=i.options.scrollingX&&T>=g,i.__enableScrollY=i.options.scrollingY&&E>=g,s.push(i.__scrollLeft,i.__scrollTop,t),i.__isDragging=(i.__enableScrollX||i.__enableScrollY)&&(T>=v||E>=v),i.__isDragging&&(i.__interruptedAnimation=!1,i.__fadeScrollbars("in"))}i.__lastTouchLeft=r,i.__lastTouchTop=o,i.__lastTouchMove=t,i.__lastScale=n}},doTouchEnd:function(t){t instanceof Date&&(t=t.valueOf()),"number"!=typeof t&&(t=Date.now());var n=this;if(n.__isTracking){if(n.__isTracking=!1,n.__isDragging)if(n.__isDragging=!1,n.__isSingleTouch&&n.options.animating&&t-n.__lastTouchMove<=100){for(var i=n.__positions,r=i.length-1,o=r,s=r;s>0&&i[s]>n.__lastTouchMove-100;s-=3)o=s;if(o!==r){var a=i[r]-i[o],l=n.__scrollLeft-i[o-2],c=n.__scrollTop-i[o-1];n.__decelerationVelocityX=l/a*(1e3/60),n.__decelerationVelocityY=c/a*(1e3/60);var u=n.options.paging||n.options.snapping?4:1;(Math.abs(n.__decelerationVelocityX)>u||Math.abs(n.__decelerationVelocityY)>u)&&(n.__refreshActive||n.__startDeceleration(t))}else n.__scrollingComplete()}else t-n.__lastTouchMove>100&&n.__scrollingComplete();if(!n.__isDecelerating)if(n.__refreshActive&&n.__refreshStart){n.__publish(n.__scrollLeft,-n.__refreshHeight,n.__zoomLevel,!0);var h=new Date;n.refreshStartTime=h.getTime(),n.__refreshStart&&n.__refreshStart(),e.Platform.isAndroid()||n.__startDeceleration()}else(n.__interruptedAnimation||n.__isDragging)&&n.__scrollingComplete(),n.scrollTo(n.__scrollLeft,n.__scrollTop,!0,n.__zoomLevel),n.__refreshActive&&(n.__refreshActive=!1,n.__refreshDeactivate&&n.__refreshDeactivate());n.__positions.length=0}},__publish:function(e,t,r,o,s){var a=this,l=a.__isAnimating;if(l&&(st.effect.Animate.stop(l),a.__isAnimating=!1),o&&a.options.animating){a.__scheduledLeft=e,a.__scheduledTop=t,a.__scheduledZoom=r;var c=a.__scrollLeft,u=a.__scrollTop,h=a.__zoomLevel,d=e-c,_=t-u,f=r-h,p=function(e,t,n){n&&(a.__scrollLeft=c+d*e,a.__scrollTop=u+_*e,a.__zoomLevel=h+f*e,a.__callback&&a.__callback(a.__scrollLeft,a.__scrollTop,a.__zoomLevel,s))},m=function(e){return a.__isAnimating===e},g=function(e,t,n){t===a.__isAnimating&&(a.__isAnimating=!1),(a.__didDecelerationComplete||n)&&a.__scrollingComplete(),a.options.zooming&&a.__computeScrollMax()};a.__isAnimating=st.effect.Animate.start(p,m,g,a.options.animationDuration,l?n:i)}else a.__scheduledLeft=a.__scrollLeft=e,a.__scheduledTop=a.__scrollTop=t,a.__scheduledZoom=a.__zoomLevel=r,a.__callback&&a.__callback(e,t,r,s),a.options.zooming&&a.__computeScrollMax()},__computeScrollMax:function(e){var t=this;null==e&&(e=t.__zoomLevel),t.__maxScrollLeft=Math.max(t.__contentWidth*e-t.__clientWidth,0),t.__maxScrollTop=Math.max(t.__contentHeight*e-t.__clientHeight,0),t.__didWaitForSize||t.__maxScrollLeft||t.__maxScrollTop||(t.__didWaitForSize=!0,t.__waitForSize())},__waitForSize:function(){var e=this;clearTimeout(e.__sizerTimeout);var t=function(){e.resize(),e.options.scrollingX&&!e.__maxScrollLeft||e.options.scrollingY&&!e.__maxScrollTop};t(),e.__sizerTimeout=setTimeout(t,1e3)},__startDeceleration:function(){var e=this;if(e.options.paging){var t=Math.max(Math.min(e.__scrollLeft,e.__maxScrollLeft),0),n=Math.max(Math.min(e.__scrollTop,e.__maxScrollTop),0),i=e.__clientWidth,r=e.__clientHeight;e.__minDecelerationScrollLeft=Math.floor(t/i)*i,e.__minDecelerationScrollTop=Math.floor(n/r)*r,e.__maxDecelerationScrollLeft=Math.ceil(t/i)*i,e.__maxDecelerationScrollTop=Math.ceil(n/r)*r}else e.__minDecelerationScrollLeft=0,e.__minDecelerationScrollTop=0,e.__maxDecelerationScrollLeft=e.__maxScrollLeft,e.__maxDecelerationScrollTop=e.__maxScrollTop,e.__refreshActive&&(e.__minDecelerationScrollTop=-1*e.__refreshHeight);var o=function(t,n,i){e.__stepThroughDeceleration(i)};e.__minVelocityToKeepDecelerating=e.options.snapping?4:.1;var s=function(){var t=Math.abs(e.__decelerationVelocityX)>=e.__minVelocityToKeepDecelerating||Math.abs(e.__decelerationVelocityY)>=e.__minVelocityToKeepDecelerating;return t||(e.__didDecelerationComplete=!0,e.options.bouncing&&!e.__refreshActive&&e.scrollTo(Math.min(Math.max(e.__scrollLeft,0),e.__maxScrollLeft),Math.min(Math.max(e.__scrollTop,0),e.__maxScrollTop),e.__refreshActive)),t},a=function(){e.__isDecelerating=!1,e.__didDecelerationComplete&&e.__scrollingComplete(),e.options.paging&&e.scrollTo(e.__scrollLeft,e.__scrollTop,e.options.snapping)};e.__isDecelerating=st.effect.Animate.start(o,s,a)},__stepThroughDeceleration:function(e){var t=this,n=t.__scrollLeft+t.__decelerationVelocityX,i=t.__scrollTop+t.__decelerationVelocityY;if(!t.options.bouncing){var r=Math.max(Math.min(t.__maxDecelerationScrollLeft,n),t.__minDecelerationScrollLeft);r!==n&&(n=r,t.__decelerationVelocityX=0);var o=Math.max(Math.min(t.__maxDecelerationScrollTop,i),t.__minDecelerationScrollTop);o!==i&&(i=o,t.__decelerationVelocityY=0)}if(e?t.__publish(n,i,t.__zoomLevel):(t.__scrollLeft=n,t.__scrollTop=i),!t.options.paging){var s=t.options.deceleration;t.__decelerationVelocityX*=s,t.__decelerationVelocityY*=s}if(t.options.bouncing){var a=0,l=0,c=t.options.penetrationDeceleration,u=t.options.penetrationAcceleration;if(nt.__maxDecelerationScrollLeft&&(a=t.__maxDecelerationScrollLeft-n),it.__maxDecelerationScrollTop&&(l=t.__maxDecelerationScrollTop-i),0!==a){var h=a*t.__decelerationVelocityX<=t.__minDecelerationScrollLeft;h&&(t.__decelerationVelocityX+=a*c);var d=Math.abs(t.__decelerationVelocityX)<=t.__minVelocityToKeepDecelerating;(!h||d)&&(t.__decelerationVelocityX=a*u)}if(0!==l){var _=l*t.__decelerationVelocityY<=t.__minDecelerationScrollTop;_&&(t.__decelerationVelocityY+=l*c);var f=Math.abs(t.__decelerationVelocityY)<=t.__minVelocityToKeepDecelerating;(!_||f)&&(t.__decelerationVelocityY=l*u)}}},__getDistance:function(e,t){var n=t.pageX-e.pageX,i=t.pageY-e.pageY;return Math.sqrt(n*n+i*i)},__getScale:function(e,t){var n=this;return e.length>=2&&t.length>=2?n.__getDistance(t[0],t[1])/n.__getDistance(e[0],e[1]):1}}),e.scroll={isScrolling:!1,lastTop:0}}(ionic),function(e){"use strict";e.views.HeaderBar=e.views.View.inherit({initialize:function(t){this.el=t.el,e.extend(this,{alignTitle:"center"},t),this.align()},align:function(t){t||(t=this.alignTitle);var n=this.el.querySelector(".title");if(n){var i=this;e.requestAnimationFrame(function(){var r,o,s,a=i.el.childNodes,l=0,c=0,u=!1;for(r=0;r10&&(n.style.left=d+"px",n.style.right=d+"px"),n.offsetWidth0&&(n.style.right=c+5+"px")):"left"==t?(n.classList.add("title-left"),l>0&&(n.style.left=l+15+"px")):"right"==t&&(n.classList.add("title-right"),c>0&&(n.style.right=c+15+"px"))})}}})}(ionic),function(e){"use strict";var t="item",n="item-content",i="item-sliding",r="item-options",o="item-placeholder",s="item-reordering",a="item-reorder",l=function(){};l.prototype={start:function(){},drag:function(){},end:function(){},isSameItem:function(){return!1}};var c=function(e){this.dragThresholdX=e.dragThresholdX||10,this.el=e.el,this.canSwipe=e.canSwipe};c.prototype=new l,c.prototype.start=function(o){var s,a,l,c;this.canSwipe()&&(s=o.target.classList.contains(n)?o.target:o.target.classList.contains(t)?o.target.querySelector("."+n):e.DomUtil.getParentWithClass(o.target,n),s&&(s.classList.remove(i),l=parseFloat(s.style[e.CSS.TRANSFORM].replace("translate3d(","").split(",")[0])||0,a=s.parentNode.querySelector("."+r),a&&(a.classList.remove("invisible"),c=a.offsetWidth,this._currentDrag={buttons:a,buttonsWidth:c,content:s,startOffsetX:l})))},c.prototype.isSameItem=function(e){return e._lastDrag&&this._currentDrag?this._currentDrag.content==e._lastDrag.content:!1},c.prototype.clean=function(){var t=this._lastDrag;t&&(t.content.style[e.CSS.TRANSITION]="",t.content.style[e.CSS.TRANSFORM]="",e.requestAnimationFrame(function(){setTimeout(function(){t.buttons&&t.buttons.classList.add("invisible")},250)}))},c.prototype.drag=e.animationFrameThrottle(function(t){var n;if(this._currentDrag&&(!this._isDragging&&(Math.abs(t.gesture.deltaX)>this.dragThresholdX||Math.abs(this._currentDrag.startOffsetX)>0)&&(this._isDragging=!0),this._isDragging)){n=this._currentDrag.buttonsWidth;var i=Math.min(0,this._currentDrag.startOffsetX+t.gesture.deltaX);-n>i&&(i=Math.min(-n,-n+.4*(t.gesture.deltaX+n))),this._currentDrag.content.style[e.CSS.TRANSFORM]="translate3d("+i+"px, 0, 0)",this._currentDrag.content.style[e.CSS.TRANSITION]="none"
+}}),c.prototype.end=function(t,n){var i=this;if(!this._currentDrag)return void(n&&n());var r=-this._currentDrag.buttonsWidth;t.gesture.deltaX>-(this._currentDrag.buttonsWidth/2)&&("left"==t.gesture.direction&&Math.abs(t.gesture.velocityX)<.3?r=0:"right"==t.gesture.direction&&(r=0)),e.requestAnimationFrame(function(){if(0===r){i._currentDrag.content.style[e.CSS.TRANSFORM]="";var t=i._currentDrag.buttons;setTimeout(function(){t&&t.classList.add("invisible")},250)}else i._currentDrag.content.style[e.CSS.TRANSFORM]="translate3d("+r+"px, 0, 0)";i._currentDrag.content.style[e.CSS.TRANSITION]="",i._lastDrag=i._currentDrag,i._currentDrag=null,n&&n()})};var u=function(e){if(this.dragThresholdY=e.dragThresholdY||0,this.onReorder=e.onReorder,this.listEl=e.listEl,this.el=e.el,this.scrollEl=e.scrollEl,this.scrollView=e.scrollView,this.listElTrueTop=0,this.listEl.offsetParent){var t=this.listEl;do this.listElTrueTop+=t.offsetTop,t=t.offsetParent;while(t)}};u.prototype=new l,u.prototype._moveElement=function(t){var n=t.gesture.center.pageY+this.scrollView.getValues().top-this._currentDrag.elementHeight/2-this.listElTrueTop;this.el.style[e.CSS.TRANSFORM]="translate3d(0, "+n+"px, 0)"},u.prototype.start=function(t){var n=e.DomUtil.getChildIndex(this.el,this.el.nodeName.toLowerCase()),i=this.el.scrollHeight,r=this.el.cloneNode(!0);r.classList.add(o),this.el.parentNode.insertBefore(r,this.el),this.el.classList.add(s),this._currentDrag={elementHeight:i,startIndex:n,placeholder:r,scrollHeight:scroll,list:r.parentNode},this._moveElement(t)},u.prototype.drag=e.animationFrameThrottle(function(t){var n=this;if(this._currentDrag){var i=0,r=t.gesture.center.pageY,o=this.listElTrueTop;if(this.scrollView){var s=this.scrollView.__container;i=this.scrollView.getValues().top;var a=s.offsetTop,l=a-r+this._currentDrag.elementHeight/2,c=r+this._currentDrag.elementHeight/2-a-s.offsetHeight;t.gesture.deltaY<0&&l>0&&i>0&&(this.scrollView.scrollBy(null,-l),e.requestAnimationFrame(function(){n.drag(t)})),t.gesture.deltaY>0&&c>0&&ithis.dragThresholdY&&(this._isDragging=!0),this._isDragging&&(this._moveElement(t),this._currentDrag.currentY=i+r-o)}}),u.prototype._getReorderIndex=function(){for(var e,t=this,n=(this._currentDrag.placeholder,Array.prototype.slice.call(this._currentDrag.placeholder.parentNode.children).filter(function(e){return e.nodeName===t.el.nodeName&&e!==t.el})),i=this._currentDrag.currentY,r=0,o=n.length;o>r;r++)if(e=n[r],r===o-1){if(i>e.offsetTop)return r}else if(0===r){if(ie.offsetTop-e.offsetHeight/2&&i5&&(i=this._getItem(t.target),i&&i.querySelector(".item-options")&&(this._dragOp=new c({el:this.el,canSwipe:this.canSwipe}),this._dragOp.start(t),t.preventDefault())):(i=this._getItem(t.target),i&&(this._dragOp=new u({listEl:this.el,el:i,scrollEl:this.scrollEl,scrollView:this.scrollView,onReorder:function(e,t,i){n.onReorder&&n.onReorder(e,t,i)}}),this._dragOp.start(t),t.preventDefault())),r&&this._dragOp&&!this._dragOp.isSameItem(r)&&t.defaultPrevented&&r.clean&&r.clean()},_handleEndDrag:function(e){var t=this;this._didDragUpOrDown=!1,this._dragOp&&this._dragOp.end(e,function(){t._initDrag()})},_handleDrag:function(e){Math.abs(e.gesture.deltaY)>5&&(this._didDragUpOrDown=!0),this.isDragging||this._dragOp||this._startDrag(e),this._dragOp&&(e.gesture.srcEvent.preventDefault(),this._dragOp.drag(e))}})}(ionic),function(e){"use strict";e.views.Modal=e.views.View.inherit({initialize:function(t){t=e.extend({focusFirstInput:!1,unfocusOnHide:!0,focusFirstDelay:600,backdropClickToClose:!0,hardwareBackButtonClose:!0},t),e.extend(this,t),this.el=t.el},show:function(){var e=this;e.focusFirstInput&&window.setTimeout(function(){var t=e.el.querySelector("input, textarea");t&&t.focus&&t.focus()},e.focusFirstDelay)},hide:function(){if(this.unfocusOnHide){var e=this.el.querySelectorAll("input, textarea");window.setTimeout(function(){for(var t=0;tt?-v:t>S?v:0,0))}e.continuous&&f.transitions&&(s(r(S-1),-v,0),s(r(S+1),v,0)),f.transitions||(E.style.left=S*-v+"px"),p.style.visibility="visible",e.slidesChanged&&e.slidesChanged()}function n(){e.continuous?o(S-1):S&&o(S-1)}function i(){e.continuous?o(S+1):SS?t:S)-a-1),v*i,0);t=r(t),s(S,v*i,n||w),s(t,0,n||w),e.continuous&&s(r(t-i),-(v*i),0)}else t=r(t),l(S*-v,t*-v,n||w);S=t,_(e.callback&&e.callback(S,m[S]))}}function s(e,t,n){a(e,t,n),g[e]=t}function a(e,t,n){var i=m[e],r=i&&i.style;r&&(r.webkitTransitionDuration=r.MozTransitionDuration=r.msTransitionDuration=r.OTransitionDuration=r.transitionDuration=n+"ms",r.webkitTransform="translate("+t+"px,0)translateZ(0)",r.msTransform=r.MozTransform=r.OTransform="translateX("+t+"px)")}function l(t,n,i){if(!i)return void(E.style.left=n+"px");var r=+new Date,o=setInterval(function(){var s=+new Date-r;return s>i?(E.style.left=n+"px",D&&c(),e.transitionEnd&&e.transitionEnd.call(event,S,m[S]),void clearInterval(o)):void(E.style.left=(n-t)*(Math.floor(s/i*100)/100)+t+"px")},4)}function c(){b=setTimeout(i,D)}function u(){D=e.auto||0,clearTimeout(b)}var h=this,d=function(){},_=function(e){setTimeout(e||d,0)},f={addEventListener:!!window.addEventListener,touch:"ontouchstart"in window||window.DocumentTouch&&document instanceof DocumentTouch,transitions:function(e){var t=["transitionProperty","WebkitTransition","MozTransition","OTransition","msTransition"];for(var n in t)if(void 0!==e.style[t[n]])return!0;return!1}(document.createElement("swipe"))},p=e.el;if(p){var m,g,v,T,E=p.children[0];e=e||{};var S=parseInt(e.startSlide,10)||0,w=e.speed||300;e.continuous=void 0!==e.continuous?e.continuous:!0;var b,y,D=e.auto||0,L={},x={},M={handleEvent:function(n){switch(("mousedown"==n.type||"mouseup"==n.type||"mousemove"==n.type)&&(n.touches=[{pageX:n.pageX,pageY:n.pageY}]),n.type){case"mousedown":this.start(n);break;case"touchstart":this.start(n);break;case"touchmove":this.touchmove(n);break;case"mousemove":this.touchmove(n);break;case"touchend":_(this.end(n));break;case"mouseup":_(this.end(n));break;case"webkitTransitionEnd":case"msTransitionEnd":case"oTransitionEnd":case"otransitionend":case"transitionend":_(this.transitionEnd(n));break;case"resize":_(t)}e.stopPropagation&&n.stopPropagation()},start:function(e){var t=e.touches[0];L={x:t.pageX,y:t.pageY,time:+new Date},y=void 0,x={},f.touch?(E.addEventListener("touchmove",this,!1),E.addEventListener("touchend",this,!1)):(E.addEventListener("mousemove",this,!1),E.addEventListener("mouseup",this,!1),document.addEventListener("mouseup",this,!1))},touchmove:function(t){if(!(t.touches.length>1||t.scale&&1!==t.scale||h.slideIsDisabled)){e.disableScroll&&t.preventDefault();var n=t.touches[0];x={x:n.pageX-L.x,y:n.pageY-L.y},"undefined"==typeof y&&(y=!!(y||Math.abs(x.x)0||S==m.length-1&&x.x<0?Math.abs(x.x)/v+1:1),a(S-1,x.x+g[S-1],0),a(S,x.x+g[S],0),a(S+1,x.x+g[S+1],0)))}},end:function(){var t=+new Date-L.time,n=Number(t)<250&&Math.abs(x.x)>20||Math.abs(x.x)>v/2,i=!S&&x.x>0||S==m.length-1&&x.x<0;e.continuous&&(i=!1);var o=x.x<0;y||(n&&!i?(o?(e.continuous?(s(r(S-1),-v,0),s(r(S+2),v,0)):s(S-1,-v,0),s(S,g[S]-v,w),s(r(S+1),g[r(S+1)]-v,w),S=r(S+1)):(e.continuous?(s(r(S+1),v,0),s(r(S-2),-v,0)):s(S+1,v,0),s(S,g[S]+v,w),s(r(S-1),g[r(S-1)]+v,w),S=r(S-1)),e.callback&&e.callback(S,m[S])):e.continuous?(s(r(S-1),-v,w),s(S,0,w),s(r(S+1),v,w)):(s(S-1,-v,w),s(S,0,w),s(S+1,v,w))),f.touch?(E.removeEventListener("touchmove",M,!1),E.removeEventListener("touchend",M,!1)):(E.removeEventListener("mousemove",M,!1),E.removeEventListener("mouseup",M,!1),document.removeEventListener("mouseup",M,!1))},transitionEnd:function(t){parseInt(t.target.getAttribute("data-index"),10)==S&&(D&&c(),e.transitionEnd&&e.transitionEnd.call(t,S,m[S]))}};this.update=function(){setTimeout(t)},this.setup=function(){t()},this.enableSlide=function(e){return arguments.length&&(this.slideIsDisabled=!e),!this.slideIsDisabled},this.slide=function(e,t){u(),o(e,t)},this.prev=this.previous=function(){u(),n()},this.next=function(){u(),i()},this.stop=function(){u()},this.start=function(){c()},this.currentIndex=function(){return S},this.slidesCount=function(){return T},this.kill=function(){u(),E.style.width="",E.style.left="";for(var e=m.length;e--;){var t=m[e];t.style.width="",t.style.left="",f.transitions&&a(e,0,0)}f.addEventListener?(E.removeEventListener("touchstart",M,!1),E.removeEventListener("webkitTransitionEnd",M,!1),E.removeEventListener("msTransitionEnd",M,!1),E.removeEventListener("oTransitionEnd",M,!1),E.removeEventListener("otransitionend",M,!1),E.removeEventListener("transitionend",M,!1),window.removeEventListener("resize",M,!1)):window.onresize=null},this.load=function(){t(),D&&c(),f.addEventListener?(f.touch?E.addEventListener("touchstart",M,!1):E.addEventListener("mousedown",M,!1),f.transitions&&(E.addEventListener("webkitTransitionEnd",M,!1),E.addEventListener("msTransitionEnd",M,!1),E.addEventListener("oTransitionEnd",M,!1),E.addEventListener("otransitionend",M,!1),E.addEventListener("transitionend",M,!1)),window.addEventListener("resize",M,!1)):window.onresize=function(){t()}}}}})}(ionic),function(e){"use strict";e.views.Toggle=e.views.View.inherit({initialize:function(t){var n=this;this.el=t.el,this.checkbox=t.checkbox,this.track=t.track,this.handle=t.handle,this.openPercent=-1,this.onChange=t.onChange||function(){},this.triggerThreshold=t.triggerThreshold||20,this.dragStartHandler=function(e){n.dragStart(e)},this.dragHandler=function(e){n.drag(e)},this.holdHandler=function(e){n.hold(e)},this.releaseHandler=function(e){n.release(e)},this.dragStartGesture=e.onGesture("dragstart",this.dragStartHandler,this.el),this.dragGesture=e.onGesture("drag",this.dragHandler,this.el),this.dragHoldGesture=e.onGesture("hold",this.holdHandler,this.el),this.dragReleaseGesture=e.onGesture("release",this.releaseHandler,this.el)},destroy:function(){e.offGesture(this.dragStartGesture,"dragstart",this.dragStartGesture),e.offGesture(this.dragGesture,"drag",this.dragGesture),e.offGesture(this.dragHoldGesture,"hold",this.holdHandler),e.offGesture(this.dragReleaseGesture,"release",this.releaseHandler)},tap:function(){"disabled"!==this.el.getAttribute("disabled")&&this.val(!this.checkbox.checked)},dragStart:function(e){this.checkbox.disabled||(this._dragInfo={width:this.el.offsetWidth,left:this.el.offsetLeft,right:this.el.offsetLeft+this.el.offsetWidth,triggerX:this.el.offsetWidth/2,initialState:this.checkbox.checked},e.gesture.srcEvent.preventDefault(),this.hold(e))},drag:function(t){var n=this;this._dragInfo&&(t.gesture.srcEvent.preventDefault(),e.requestAnimationFrame(function(){if(n._dragInfo){var e=(n.track.offsetLeft+n.handle.offsetWidth/2,n.track.offsetLeft+n.track.offsetWidth-n.handle.offsetWidth/2,t.gesture.deltaX,t.gesture.touches[0].pageX-n._dragInfo.left),i=n._dragInfo.width-n.triggerThreshold;n._dragInfo.initialState?en._dragInfo.triggerX&&n.setOpenPercent(100):ei&&n.setOpenPercent(100)}}))},endDrag:function(){this._dragInfo=null},hold:function(){this.el.classList.add("dragging")},release:function(e){this.el.classList.remove("dragging"),this.endDrag(e)},setOpenPercent:function(t){if(this.openPercent<0||tthis.openPercent+3)if(this.openPercent=t,0===t)this.val(!1);else if(100===t)this.val(!0);else{var n=Math.round(t/100*this.track.offsetWidth-this.handle.offsetWidth);n=1>n?0:n,this.handle.style[e.CSS.TRANSFORM]="translate3d("+n+"px,0,0)"}},val:function(t){return(t===!0||t===!1)&&(""!==this.handle.style[e.CSS.TRANSFORM]&&(this.handle.style[e.CSS.TRANSFORM]=""),this.checkbox.checked=t,this.openPercent=t?100:0,this.onChange&&this.onChange()),this.checkbox.checked}})}(ionic)}();
\ No newline at end of file
diff --git a/client/lib/ionic/scss/_action-sheet.scss b/client/lib/ionic/scss/_action-sheet.scss
new file mode 100644
index 0000000..9287125
--- /dev/null
+++ b/client/lib/ionic/scss/_action-sheet.scss
@@ -0,0 +1,92 @@
+/**
+ * Action Sheets
+ * --------------------------------------------------
+ */
+
+.action-sheet-backdrop {
+ @include transition(background-color 300ms ease-in-out);
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: $z-index-action-sheet;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0,0,0,0);
+
+ &.active {
+ background-color: rgba(0,0,0,0.5);
+ }
+}
+
+.action-sheet-wrapper {
+ @include translate3d(0, 100%, 0);
+ @include transition(all ease-in-out 300ms);
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+}
+
+.action-sheet-up {
+ @include translate3d(0, 0, 0);
+}
+
+.action-sheet {
+ margin-left: 15px;
+ margin-right: 15px;
+ width: auto;
+ z-index: $z-index-action-sheet;
+ overflow: hidden;
+
+ .button {
+ display: block;
+ padding: 1px;
+ width: 100%;
+ border-radius: 0;
+
+ background-color: transparent;
+
+ color: $positive;
+ font-size: 18px;
+
+ &.destructive {
+ color: $assertive;
+ }
+ }
+}
+
+.action-sheet-title {
+ padding: 10px;
+ color: lighten($base-color, 40%);
+ text-align: center;
+ font-size: 12px;
+}
+
+.action-sheet-group {
+ margin-bottom: 5px;
+ border-radius: $sheet-border-radius;
+ background-color: #fff;
+ .button {
+ border-width: 1px 0px 0px 0px;
+ border-radius: 0;
+
+ &.active {
+ background-color: transparent;
+ color: inherit;
+ }
+ }
+ .button:first-child:last-child {
+ border-width: 0;
+ }
+}
+
+.action-sheet-open {
+ pointer-events: none;
+
+ &.modal-open .modal {
+ pointer-events: none;
+ }
+
+ .action-sheet-backdrop {
+ pointer-events: auto;
+ }
+}
diff --git a/client/lib/ionic/scss/_animations.scss b/client/lib/ionic/scss/_animations.scss
new file mode 100644
index 0000000..d79ad7d
--- /dev/null
+++ b/client/lib/ionic/scss/_animations.scss
@@ -0,0 +1,950 @@
+/**
+ * Animations
+ * --------------------------------------------------
+ * The animations in this file are "simple" - not too complex
+ * and pretty easy on performance. They can be overidden
+ * and enhanced easily.
+ */
+
+$transition-duration: 250ms;
+$slide-in-up-function: cubic-bezier(.1, .7, .1, 1);
+
+
+/**
+ * Keyframes
+ * --------------------------------------------------
+ */
+
+// Slide In From The Bottom To The Top
+// -------------------------------
+
+@-webkit-keyframes slideInUp {
+ 0% { -webkit-transform: translate3d(0, 100%, 0); }
+ 100% { -webkit-transform: translate3d(0, 0, 0); }
+}
+@-moz-keyframes slideInUp {
+ 0% { -moz-transform: translate3d(0, 100%, 0); }
+ 100% { -moz-transform: translate3d(0, 0, 0); }
+}
+@keyframes slideInUp {
+ 0% { transform: translate3d(0, 100%, 0); }
+ 100% { transform: translate3d(0, 0, 0); }
+}
+
+
+// Slide Out From The Top To Bottom
+// -------------------------------
+
+@-webkit-keyframes slideOutUp {
+ 0% { -webkit-transform: translate3d(0, 0, 0); }
+ 100% { -webkit-transform: translate3d(0, 100%, 0); }
+}
+@-moz-keyframes slideOutUp {
+ 0% { -moz-transform: translate3d(0, 0, 0); }
+ 100% { -moz-transform: translate3d(0, 100%, 0); }
+}
+@keyframes slideOutUp {
+ 0% { transform: translate3d(0, 0, 0); }
+ 100% { transform: translate3d(0, 100%, 0); }
+}
+
+
+// Slide In From Left
+// -------------------------------
+
+@-webkit-keyframes slideInFromLeft {
+ from { -webkit-transform: translate3d(-100%, 0, 0); }
+ to { -webkit-transform: translate3d(0, 0, 0); }
+}
+@-moz-keyframes slideInFromLeft {
+ from { -moz-transform: translateX(-100%); }
+ to { -moz-transform: translateX(0); }
+}
+@keyframes slideInFromLeft {
+ from { transform: translateX(-100%); }
+ to { transform: translateX(0); }
+}
+
+
+// Slide In From Right
+// -------------------------------
+
+@-webkit-keyframes slideInFromRight {
+ from { -webkit-transform: translate3d(100%, 0, 0); }
+ to { -webkit-transform: translate3d(0, 0, 0); }
+}
+@-moz-keyframes slideInFromRight {
+ from { -moz-transform: translateX(100%); }
+ to { -moz-transform: translateX(0); }
+}
+@keyframes slideInFromRight {
+ from { transform: translateX(100%); }
+ to { transform: translateX(0); }
+}
+
+
+// Slide Out To Left
+// -------------------------------
+
+@-webkit-keyframes slideOutToLeft {
+ from { -webkit-transform: translate3d(0, 0, 0); }
+ to { -webkit-transform: translate3d(-100%, 0, 0); }
+}
+@-moz-keyframes slideOutToLeft {
+ from { -moz-transform: translateX(0); }
+ to { -moz-transform: translateX(-100%); }
+}
+@keyframes slideOutToLeft {
+ from { transform: translateX(0); }
+ to { transform: translateX(-100%); }
+}
+
+
+// Slide Out To Right
+// -------------------------------
+
+@-webkit-keyframes slideOutToRight {
+ from { -webkit-transform: translate3d(0, 0, 0); }
+ to { -webkit-transform: translate3d(100%, 0, 0); }
+}
+@-moz-keyframes slideOutToRight {
+ from { -moz-transform: translateX(0); }
+ to { -moz-transform: translateX(100%); }
+}
+@keyframes slideOutToRight {
+ from { transform: translateX(0); }
+ to { transform: translateX(100%); }
+}
+
+
+// Fade Out
+// -------------------------------
+
+@-webkit-keyframes fadeOut {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+@-moz-keyframes fadeOut {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+@keyframes fadeOut {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+
+
+// Fade In
+// -------------------------------
+
+@-webkit-keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+@-moz-keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+
+// Fade Half In
+// -------------------------------
+
+@-webkit-keyframes fadeInHalf {
+ from { background-color: rgba(0,0,0,0); }
+ to { background-color: rgba(0,0,0,0.5); }
+}
+@-moz-keyframes fadeInHalf {
+ from { background-color: rgba(0,0,0,0); }
+ to { background-color: rgba(0,0,0,0.5); }
+}
+@keyframes fadeInHalf {
+ from { background-color: rgba(0,0,0,0); }
+ to { background-color: rgba(0,0,0,0.5); }
+}
+
+
+// Fade Half Out
+// -------------------------------
+
+@-webkit-keyframes fadeOutHalf {
+ from { background-color: rgba(0,0,0,0.5); }
+ to { background-color: rgba(0,0,0,0); }
+}
+@-moz-keyframes fadeOutHalf {
+ from { background-color: rgba(0,0,0,0.5); }
+ to { background-color: rgba(0,0,0,0); }
+}
+@keyframes fadeOutHalf {
+ from { background-color: rgba(0,0,0,0.5); }
+ to { background-color: rgba(0,0,0,0); }
+}
+
+// Scale Out
+// Scale from hero (1 in this case) to zero
+// -------------------------------
+
+@-webkit-keyframes scaleOut {
+ from { -webkit-transform: scale(1); opacity: 1; }
+ to { -webkit-transform: scale(0.8); opacity: 0; }
+}
+@-moz-keyframes scaleOut {
+ from { -moz-transform: scale(1); opacity: 1; }
+ to { -moz-transform: scale(0.8); opacity: 0; }
+}
+@keyframes scaleOut {
+ from { transform: scale(1); opacity: 1; }
+ to { transform: scale(0.8); opacity: 0; }
+}
+
+// Scale In
+// Scale from 0 to hero (1 in this case)
+// -------------------------------
+
+@-webkit-keyframes scaleIn {
+ from { -webkit-transform: scale(0); }
+ to { -webkit-transform: scale(1); }
+}
+@-moz-keyframes scaleIn {
+ from { -moz-transform: scale(0); }
+ to { -moz-transform: scale(1); }
+}
+@keyframes scaleIn {
+ from { transform: scale(0); }
+ to { transform: scale(1); }
+}
+
+// Super Scale In
+// Scale from super (1.x) to duper (1 in this case)
+// -------------------------------
+
+@-webkit-keyframes superScaleIn {
+ from { -webkit-transform: scale(1.2); opacity: 0; }
+ to { -webkit-transform: scale(1); opacity: 1 }
+}
+@-moz-keyframes superScaleIn {
+ from { -moz-transform: scale(1.2); opacity: 0; }
+ to { -moz-transform: scale(1); opacity: 1; }
+}
+@keyframes superScaleIn {
+ from { transform: scale(1.2); opacity: 0; }
+ to { transform: scale(1); opacity: 1; }
+}
+
+// Spin
+// -------------------------------
+
+@-webkit-keyframes spin {
+ 100% { -webkit-transform: rotate(360deg); }
+}
+@-moz-keyframes spin {
+ 100% { -moz-transform: rotate(360deg); }
+}
+@keyframes spin {
+ 100% { transform: rotate(360deg); }
+}
+
+.no-animation {
+ > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave {
+ @include transition(none);
+ }
+}
+.noop-animation {
+ > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave {
+ @include transition(all cubic-bezier(0.250, 0.460, 0.450, 0.940) $transition-duration);
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+}
+
+
+// required for Android
+.ng-animate .pane {
+ position: absolute;
+}
+
+
+/**
+ * Slide Left-Right, and Right-Left, each with the reserve
+ * --------------------------------------------------
+ * NEW content slides IN from the RIGHT, OLD slides OUT to the LEFT
+ * Reverse: NEW content slides IN from the LEFT, OLD slides OUT to the RIGHT
+ */
+
+.slide-left-right,
+.slide-right-left.reverse {
+ > .ng-enter, &.ng-enter,
+ > .ng-leave, &.ng-leave {
+ @include transition(all ease-in-out $transition-duration);
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+ > .ng-enter, &.ng-enter {
+ /* NEW content placed far RIGHT BEFORE it slides IN from the RIGHT */
+ @include translate3d(100%, 0, 0);
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ /* NEW content ACTIVELY sliding IN from the RIGHT */
+ @include translate3d(0, 0, 0);
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ /* OLD content ACTIVELY sliding OUT to the LEFT */
+ @include translate3d(-100%, 0, 0);
+ }
+}
+
+.slide-left-right.reverse,
+.slide-right-left {
+ > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave {
+ @include transition(all ease-in-out $transition-duration);
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+ > .ng-enter, &.ng-enter {
+ /* NEW content placed far LEFT BEFORE it slides IN from the LEFT */
+ @include translate3d(-100%, 0, 0);
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ /* NEW content ACTIVELY sliding IN from the LEFT */
+ @include translate3d(0, 0, 0);
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ /* OLD content ACTIVELY sliding OUT to the RIGHT */
+ @include translate3d(100%, 0, 0);
+ }
+}
+
+
+/**
+ * iOS style slide left to right
+ * --------------------------------------------------
+ */
+$ios-timing-function: cubic-bezier(0.4, 0.6, 0.2, 1);
+$ios-transition-duration: 400ms;
+$ios-transition-box-shadow: 0px 0px 12px rgba(0,0,0,0.5);
+/*
+$ios-transition-box-shadow-start: -200px 0px 200px rgba(0,0,0,0), -5px 0px 5px rgba(0,0,0,0.01);
+$ios-transition-box-shadow-end: -200px 0px 200px rgba(0,0,0,0.15), -5px 0px 5px rgba(0,0,0,0.18);
+*/
+
+.slide-ios,
+.slide-left-right-ios7,
+.slide-right-left-ios7.reverse {
+ > .ng-enter, &.ng-enter,
+ > .ng-leave, &.ng-leave {
+ @include transition(all $ios-timing-function $ios-transition-duration);
+ position: absolute;
+ top: 0;
+ //right: -1px;
+ bottom: 0;
+ //left: -1px;
+ width: auto;
+ &:not(.bar) {
+ border-right: none;
+ border-left: none;
+ }
+ border-right: none;
+ border-left: none;
+ }
+ > .ng-enter, &.ng-enter {
+ /* NEW content placed far RIGHT BEFORE it slides IN from the RIGHT */
+ @include translate3d(100%, 0, 0);
+ }
+ > .ng-leave, &.ng-leave {
+ z-index: 1;
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ /* NEW content ACTIVELY sliding IN from the RIGHT */
+ @include translate3d(0, 0, 0);
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ /* OLD content ACTIVELY sliding OUT to the LEFT */
+ @include translate3d(-20%, 0, 0);
+ }
+}
+.slide-ios.reverse,
+.slide-left-right-ios7.reverse,
+.slide-right-left-ios7 {
+ > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave {
+ @include transition(all $ios-timing-function $ios-transition-duration);
+ position: absolute;
+ top: 0;
+ //right: -1px;
+ bottom: 0;
+ //left: -1px;
+ width: auto;
+ border-right: none;
+ border-left: none;
+ }
+ > .ng-enter, &.ng-enter {
+ /* NEW content placed far LEFT BEFORE it slides IN from the LEFT */
+ @include translate3d(-20%, 0, 0);
+ }
+ > .ng-leave, &.ng-leave {
+ z-index: 2;
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ /* NEW content ACTIVELY sliding IN from the LEFT */
+ @include translate3d(0, 0, 0);
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ /* OLD content ACTIVELY sliding OUT to the RIGHT */
+ @include translate3d(100%, 0, 0);
+ }
+}
+
+/**
+ * iPad doesn't like box shadows
+ */
+.grade-a {
+ .slide-ios,
+ .slide-left-right-ios7, .slide-right-left-ios7.reverse {
+ > .ng-enter, &.ng-enter {
+ &:not(.platform-ipad) {
+ box-shadow: none;
+ }
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ &:not(.platform-ipad) {
+ box-shadow: $ios-transition-box-shadow;
+ }
+ }
+ > .ng-leave, &.ng-leave {
+ opacity: 1;
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ opacity: 0.9;
+ }
+ }
+ .slide-ios.reverse,
+ .slide-left-right-ios7.reverse, .slide-right-left-ios7 {
+ > .ng-enter, &.ng-enter {
+ opacity: 0.9;
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ opacity: 1;
+ }
+ > .ng-leave, &.ng-leave {
+ box-shadow: 0px 0px 12px rgba(0,0,0,0.5);
+ opacity: 1;
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ box-shadow: none;
+ }
+ }
+}
+
+$full-slide-timing-function: ease-in-out;
+$full-slide-transition-duration: 400ms;
+
+.slide-full {
+ > .ng-enter, &.ng-enter,
+ > .ng-leave, &.ng-leave {
+ @include transition(all $full-slide-timing-function $full-slide-transition-duration);
+ position: absolute;
+ top: 0;
+ right: -1px;
+ bottom: 0;
+ left: -1px;
+ width: auto;
+ &:not(.bar) {
+ border-right: none;
+ border-left: none;
+ }
+ border-right: none;
+ border-left: none;
+ }
+ > .ng-enter, &.ng-enter {
+ /* NEW content placed far RIGHT BEFORE it slides IN from the RIGHT */
+ @include translate3d(100%, 0, 0);
+ }
+ > .ng-leave, &.ng-leave {
+ z-index: 1;
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ /* NEW content ACTIVELY sliding IN from the RIGHT */
+ @include translate3d(0, 0, 0);
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ /* OLD content ACTIVELY sliding OUT to the LEFT */
+ @include translate3d(-100%, 0, 0);
+ }
+}
+.slide-full.reverse {
+ > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave {
+ @include transition(all $full-slide-timing-function $full-slide-transition-duration);
+ position: absolute;
+ top: 0;
+ right: -1px;
+ bottom: 0;
+ left: -1px;
+ width: auto;
+ border-right: none;
+ border-left: none;
+ }
+ > .ng-enter, &.ng-enter {
+ /* NEW content placed far LEFT BEFORE it slides IN from the LEFT */
+ @include translate3d(-100%, 0, 0);
+ }
+ > .ng-leave, &.ng-leave {
+ z-index: 2;
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ /* NEW content ACTIVELY sliding IN from the LEFT */
+ @include translate3d(0, 0, 0);
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ /* OLD content ACTIVELY sliding OUT to the RIGHT */
+ @include translate3d(100%, 0, 0);
+ }
+}
+
+
+.fade-explode.reverse {
+ > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave {
+ @include transition(all ease-out 300ms);
+ position: absolute;
+ top: 0;
+ right: -1px;
+ bottom: 0;
+ left: -1px;
+ width: auto;
+ &:not(.bar) {
+ border-right: 1px solid #ddd;
+ border-left: 1px solid #ddd;
+ }
+ }
+ > .ng-enter, &.ng-enter {
+ /* NEW content placed far LEFT BEFORE it slides IN from the LEFT */
+ @include scale(0.95);
+ opacity: 0;
+ z-index: 1;
+ }
+ > .ng-leave, &.ng-leave {
+ @include scale(1);
+ opacity: 1;
+ z-index: 2;
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ @include scale(1);
+ opacity: 1;
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ @include scale(1.6);
+ opacity: 0;
+ }
+}
+
+/**
+ * Android style "pop in" with fade and scale
+ */
+.fade-implode {
+ > .ng-enter, &.ng-enter,
+ > .ng-leave, &.ng-leave {
+ @include transition(all ease-out 200ms);
+ position: absolute;
+ top: 0;
+ right: -1px;
+ bottom: 0;
+ left: -1px;
+ width: auto;
+ &:not(.bar) {
+ border-right: 1px solid #ddd;
+ border-left: 1px solid #ddd;
+ }
+ }
+ > .ng-enter, &.ng-enter {
+ /* NEW content placed far RIGHT BEFORE it slides IN from the RIGHT */
+ @include scale(0.8);
+ opacity: 0;
+ z-index: 2;
+ }
+ > .ng-leave, &.ng-leave {
+ z-index: 1;
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ /* NEW content */
+ @include scale(1);
+ opacity: 1;
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ }
+}
+
+.fade-implode.reverse {
+ > .ng-enter, &.ng-enter, > .ng-leave, &.ng-leave {
+ @include transition(all ease-out 200ms);
+ position: absolute;
+ top: 0;
+ right: -1px;
+ bottom: 0;
+ left: -1px;
+ width: auto;
+ border-right: 1px solid #ddd;
+ border-left: 1px solid #ddd;
+ }
+ > .ng-enter, &.ng-enter {
+ @include scale(1);
+ opacity: 1;
+ z-index: 1;
+ }
+ > .ng-leave, &.ng-leave {
+ @include scale(1);
+ opacity: 1;
+ z-index: 2;
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ opacity: 1;
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ @include scale(0.8);
+ opacity: 0;
+ }
+}
+
+/**
+ * Simple slide-in animation
+ */
+.slide-in-left {
+ @include translate3d(0%,0,0);
+ &.ng-enter, > .ng-enter {
+ @include animation-name(slideInFromLeft);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
+ &.ng-leave, > .ng-leave {
+ @include animation-name(slideOutToLeft);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
+}
+
+
+.slide-in-left-add {
+ @include translate3d(100%,0,0);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+}
+.slide-in-left-add-active {
+ @include animation-name(slideInFromLeft);
+}
+
+.slide-out-left {
+ @include translate3d(-100%,0,0);
+ &.ng-enter, > .ng-enter {
+ @include animation-name(slideOutToLeft);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
+ &.ng-leave, > .ng-leave {
+ @include animation-name(slideOutToLeft);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
+}
+
+.slide-out-left {
+}
+
+.slide-out-left-add {
+ @include translate3d(0,0,0);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+}
+.slide-out-left-add-active {
+ @include animation-name(slideOutToLeft);
+}
+
+.slide-in-right {
+ @include translate3d(0%,0,0);
+ &.ng-enter, > .ng-enter {
+ @include animation-name(slideInFromRight);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
+ &.ng-leave, > .ng-leave {
+ @include animation-name(slideOutToRight);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
+}
+
+.slide-in-right-add {
+ @include translate3d(-100%,0,0);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+}
+.slide-in-right-add-active {
+ @include animation-name(slideInFromRight);
+}
+
+.slide-out-right {
+ @include translate3d(100%,0,0);
+ &.ng-enter, > .ng-enter {
+ @include animation-name(slideOutToRight);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
+ &.ng-leave, > .ng-leave {
+ @include animation-name(slideOutToRight);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
+}
+
+.slide-out-right-add {
+ @include translate3d(0,0,0);
+ @include animation-duration($transition-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+}
+.slide-out-right-add-active {
+ @include animation-name(slideOutToRight);
+}
+
+
+/**
+ * Slide up from the bottom, used for modals
+ * --------------------------------------------------
+ */
+
+.slide-in-up {
+ @include translate3d(0, 100%, 0);
+}
+.slide-in-up.ng-enter,
+.slide-in-up > .ng-enter {
+ @include transition(all $slide-in-up-function 400ms);
+}
+.slide-in-up.ng-enter-active,
+.slide-in-up > .ng-enter-active {
+ @include translate3d(0, 0, 0);
+}
+
+.slide-in-up.ng-leave,
+.slide-in-up > .ng-leave {
+ @include transition(all ease-in-out 250ms);
+}
+
+
+.fade-in {
+ @include animation(fadeOut 0.3s);
+ &.active {
+ @include animation(fadeIn 0.3s);
+ }
+}
+
+.fade-in-not-out {
+ &.ng-enter, .ng-enter {
+ @include animation(fadeIn 0.3s);
+ position: relative;
+ }
+ &.ng-leave, .ng-leave {
+ display: none;
+ }
+}
+
+
+
+/**
+ * Some component specific animations
+ */
+$nav-title-slide-ios-delay: $ios-transition-duration;
+.nav-title-slide-ios,
+.nav-title-slide-ios7 {
+ &:not(.no-animation) .button.back-button {
+ @include transition(all $nav-title-slide-ios-delay);
+ @include transition-timing-function($ios-timing-function);
+
+ @include translate3d(0%, 0, 0);
+ opacity: 1;
+ &.active, &.activated {
+ opacity: 0.5;
+ }
+ &.ng-hide {
+ opacity: 0;
+ @include translate3d(30%, 0, 0);
+ }
+ &.ng-hide-add,
+ &.ng-hide-remove {
+ display: block !important;
+ }
+ &.ng-hide-add {
+ position: absolute;
+ }
+ }
+ > .ng-enter, &.ng-enter,
+ > .ng-leave, &.ng-leave {
+ @include transition(all $nav-title-slide-ios-delay);
+ @include transition-timing-function($ios-timing-function);
+ opacity: 1;
+ }
+ > .ng-enter, &.ng-enter {
+ @include translate3d(30%, 0, 0);
+ opacity: 0;
+ &.title {
+ @include translate3d(100%, 0, 0);
+ }
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ @include translate3d(0, 0, 0);
+ opacity: 1;
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ @include translate3d(-30%, 0, 0);
+ opacity: 0;
+ }
+
+ &.reverse {
+ > .ng-enter, &.ng-enter,
+ > .ng-leave, &.ng-leave {
+ @include transition(all $nav-title-slide-ios-delay);
+ @include transition-timing-function($ios-timing-function);
+ opacity: 1;
+ }
+ > .ng-enter, &.ng-enter {
+ @include translate3d(-30%, 0, 0);
+ opacity: 0;
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ @include translate3d(0, 0, 0);
+ opacity: 1;
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ @include translate3d(100%, 0, 0);
+ opacity: 0;
+ }
+ }
+}
+
+
+/**
+ * Some component specific animations
+ */
+$nav-title-slide-full-duration: $full-slide-transition-duration;
+.nav-title-slide-full {
+ &:not(.no-animation) .button.back-button {
+ @include transition(all $nav-title-slide-full-duration);
+ @include transition-timing-function($full-slide-timing-function);
+
+ @include translate3d(0%, 0, 0);
+ opacity: 1;
+ &.active, &.activated {
+ opacity: 0.5;
+ }
+ &.ng-hide {
+ opacity: 0;
+ @include translate3d(30%, 0, 0);
+ }
+ &.ng-hide-add,
+ &.ng-hide-remove {
+ display: block !important;
+ }
+ &.ng-hide-add {
+ position: absolute;
+ }
+ }
+ > .ng-enter, &.ng-enter,
+ > .ng-leave, &.ng-leave {
+ @include transition(all $nav-title-slide-full-duration);
+ @include transition-timing-function($full-slide-timing-function);
+ opacity: 1;
+ }
+ > .ng-enter, &.ng-enter {
+ @include translate3d(30%, 0, 0);
+ opacity: 0;
+ &.title {
+ @include translate3d(100%, 0, 0);
+ }
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ @include translate3d(0, 0, 0);
+ opacity: 1;
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ @include translate3d(-30%, 0, 0);
+ opacity: 0;
+ }
+
+ &.reverse {
+ > .ng-enter, &.ng-enter,
+ > .ng-leave, &.ng-leave {
+ @include transition(all $nav-title-slide-full-duration);
+ @include transition-timing-function($full-slide-timing-function);
+ opacity: 1;
+ }
+ > .ng-enter, &.ng-enter {
+ @include translate3d(-30%, 0, 0);
+ opacity: 0;
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ @include translate3d(0, 0, 0);
+ opacity: 1;
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ @include translate3d(100%, 0, 0);
+ opacity: 0;
+ }
+ }
+}
+
+
+$nav-title-android-delay: 200ms;
+$nav-title-android-timing-function: linear;
+
+.nav-title-android {
+ &:not(.no-animation) .button.back-button {
+ @include transition(all $nav-title-android-delay);
+ @include transition-timing-function($nav-title-android-timing-function);
+ opacity: 1;
+ &.ng-hide {
+ opacity: 0;
+ }
+ &.ng-hide-add,
+ &.ng-hide-remove {
+ display: block !important;
+ }
+ &.ng-hide-add {
+ position: absolute;
+ }
+ &.ng-hide-remove {
+ }
+ }
+ > .ng-enter, &.ng-enter,
+ > .ng-leave, &.ng-leave {
+ @include transition(all $nav-title-android-delay);
+ @include transition-timing-function($nav-title-android-timing-function);
+ }
+ > .ng-enter, &.ng-enter {
+ opacity: 0;
+ }
+ > .ng-enter.ng-enter-active, &.ng-enter.ng-enter-active {
+ opacity: 1;
+ }
+ > .ng-leave.ng-leave-active, &.ng-leave.ng-leave-active {
+ opacity: 0;
+ }
+}
diff --git a/client/lib/ionic/scss/_backdrop.scss b/client/lib/ionic/scss/_backdrop.scss
new file mode 100644
index 0000000..2dc36bd
--- /dev/null
+++ b/client/lib/ionic/scss/_backdrop.scss
@@ -0,0 +1,24 @@
+
+.backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: $z-index-backdrop;
+
+ width: 100%;
+ height: 100%;
+
+ background-color: rgba(0,0,0,0.4);
+
+ visibility: hidden;
+ opacity: 0;
+
+ &.visible {
+ visibility: visible;
+ }
+ &.active {
+ opacity: 1;
+ }
+
+ @include transition(0.1s opacity linear);
+}
diff --git a/client/lib/ionic/scss/_badge.scss b/client/lib/ionic/scss/_badge.scss
new file mode 100644
index 0000000..37298be
--- /dev/null
+++ b/client/lib/ionic/scss/_badge.scss
@@ -0,0 +1,62 @@
+
+/**
+ * Badges
+ * --------------------------------------------------
+ */
+
+.badge {
+ @include badge-style($badge-default-bg, $badge-default-text);
+ z-index: $z-index-badge;
+ display: inline-block;
+ padding: 3px 8px;
+ min-width: 10px;
+ border-radius: $badge-border-radius;
+ vertical-align: baseline;
+ text-align: center;
+ white-space: nowrap;
+ font-weight: $badge-font-weight;
+ font-size: $badge-font-size;
+ line-height: $badge-line-height;
+
+ &:empty {
+ display: none;
+ }
+}
+
+//Be sure to override specificity of rule that 'badge color matches tab color by default'
+.tabs .tab-item .badge,
+.badge {
+ &.badge-light {
+ @include badge-style($badge-light-bg, $badge-light-text);
+ }
+ &.badge-stable {
+ @include badge-style($badge-stable-bg, $badge-stable-text);
+ }
+ &.badge-positive {
+ @include badge-style($badge-positive-bg, $badge-positive-text);
+ }
+ &.badge-calm {
+ @include badge-style($badge-calm-bg, $badge-calm-text);
+ }
+ &.badge-assertive {
+ @include badge-style($badge-assertive-bg, $badge-assertive-text);
+ }
+ &.badge-balanced {
+ @include badge-style($badge-balanced-bg, $badge-balanced-text);
+ }
+ &.badge-energized {
+ @include badge-style($badge-energized-bg, $badge-energized-text);
+ }
+ &.badge-royal {
+ @include badge-style($badge-royal-bg, $badge-royal-text);
+ }
+ &.badge-dark {
+ @include badge-style($badge-dark-bg, $badge-dark-text);
+ }
+}
+
+// Quick fix for labels/badges in buttons
+.button .badge {
+ position: relative;
+ top: -1px;
+}
diff --git a/client/lib/ionic/scss/_bar.scss b/client/lib/ionic/scss/_bar.scss
new file mode 100644
index 0000000..7177c7f
--- /dev/null
+++ b/client/lib/ionic/scss/_bar.scss
@@ -0,0 +1,348 @@
+
+/**
+ * Bar (Headers and Footers)
+ * --------------------------------------------------
+ */
+
+.bar {
+ @include display-flex();
+ @include translate3d(0,0,0);
+ @include user-select(none);
+ position: absolute;
+ right: 0;
+ left: 0;
+ z-index: $z-index-bar;
+
+ box-sizing: border-box;
+ padding: $bar-padding-portrait;
+
+ width: 100%;
+ height: $bar-height;
+ border-width: 0;
+ border-style: solid;
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid $bar-default-border;
+
+ background-color: $bar-default-bg;
+
+ /* border-width: 1px will actually create 2 device pixels on retina */
+ /* this nifty trick sets an actual 1px border on hi-res displays */
+ background-size: 0;
+ @media (min--moz-device-pixel-ratio: 1.5),
+ (-webkit-min-device-pixel-ratio: 1.5),
+ (min-device-pixel-ratio: 1.5),
+ (min-resolution: 144dpi),
+ (min-resolution: 1.5dppx) {
+ border: none;
+ background-image: linear-gradient(0deg, $bar-default-border, $bar-default-border 50%, transparent 50%);
+ background-position: bottom;
+ background-size: 100% 1px;
+ background-repeat: no-repeat;
+ }
+
+ &.bar-clear {
+ border: none;
+ background: none;
+ color: #fff;
+
+ .button {
+ color: #fff;
+ }
+ .title {
+ color: #fff;
+ }
+ }
+
+ &.item-input-inset {
+ .item-input-wrapper {
+ margin-top: -1px;
+
+ input {
+ padding-left: 8px;
+ width: 94%;
+ height: 28px;
+ background: transparent;
+ }
+ }
+ }
+
+ &.bar-light {
+ @include bar-style($bar-light-bg, $bar-light-border, $bar-light-text);
+ &.bar-footer{
+ background-image: linear-gradient(180deg, $bar-light-border, $bar-light-border 50%, transparent 50%);
+ }
+ }
+ &.bar-stable {
+ @include bar-style($bar-stable-bg, $bar-stable-border, $bar-stable-text);
+ &.bar-footer{
+ background-image: linear-gradient(180deg, $bar-stable-border, $bar-stable-border 50%, transparent 50%);
+ }
+ }
+ &.bar-positive {
+ @include bar-style($bar-positive-bg, $bar-positive-border, $bar-positive-text);
+ &.bar-footer{
+ background-image: linear-gradient(180deg, $bar-positive-border, $bar-positive-border 50%, transparent 50%);
+ }
+ }
+ &.bar-calm {
+ @include bar-style($bar-calm-bg, $bar-calm-border, $bar-calm-text);
+ &.bar-footer{
+ background-image: linear-gradient(180deg, $bar-calm-border, $bar-calm-border 50%, transparent 50%);
+ }
+ }
+ &.bar-assertive {
+ @include bar-style($bar-assertive-bg, $bar-assertive-border, $bar-assertive-text);
+ &.bar-footer{
+ background-image: linear-gradient(180deg, $bar-assertive-border, $bar-assertive-border 50%, transparent 50%);
+ }
+ }
+ &.bar-balanced {
+ @include bar-style($bar-balanced-bg, $bar-balanced-border, $bar-balanced-text);
+ &.bar-footer{
+ background-image: linear-gradient(180deg, $bar-balanced-border, $bar-positive-border 50%, transparent 50%);
+ }
+ }
+ &.bar-energized {
+ @include bar-style($bar-energized-bg, $bar-energized-border, $bar-energized-text);
+ &.bar-footer{
+ background-image: linear-gradient(180deg, $bar-energized-border, $bar-energized-border 50%, transparent 50%);
+ }
+ }
+ &.bar-royal {
+ @include bar-style($bar-royal-bg, $bar-royal-border, $bar-royal-text);
+ &.bar-footer{
+ background-image: linear-gradient(180deg, $bar-royal-border, $bar-royal-border 50%, transparent 50%);
+ }
+ }
+ &.bar-dark {
+ @include bar-style($bar-dark-bg, $bar-dark-border, $bar-dark-text);
+ &.bar-footer{
+ background-image: linear-gradient(180deg, $bar-dark-border, $bar-dark-border 50%, transparent 50%);
+ }
+ }
+
+ // Title inside of a bar is centered
+ .title {
+ position: absolute;
+
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: $z-index-bar-title;
+ overflow: hidden;
+
+ margin: 0 10px;
+
+ min-width: 30px;
+ height: $bar-height - 1;
+
+ text-align: center;
+
+ // Go into ellipsis if too small
+ text-overflow: ellipsis;
+ white-space: nowrap;
+
+ font-size: $bar-title-font-size;
+
+ line-height: $bar-height;
+
+ &.title-left {
+ text-align: left;
+ }
+ &.title-right {
+ text-align: right;
+ }
+ }
+
+ .title a {
+ color: inherit;
+ }
+
+ .button {
+ z-index: $z-index-bar-button;
+ padding: 0 $button-bar-button-padding;
+ min-width: initial;
+ min-height: $button-bar-button-height - 1;
+ font-weight: 400;
+ font-size: $button-bar-button-font-size;
+ line-height: $button-bar-button-height;
+
+ &.button-icon:before,
+ .icon:before,
+ &.icon:before,
+ &.icon-left:before,
+ &.icon-right:before {
+ padding-right: 2px;
+ padding-left: 2px;
+ font-size: $button-bar-button-icon-size;
+ line-height: $button-bar-button-height;
+ }
+
+ &.button-icon {
+ font-size: $bar-title-font-size;
+ .icon:before,
+ &:before,
+ &.icon-left:before,
+ &.icon-right:before {
+ vertical-align: top;
+ font-size: $button-large-icon-size;
+ line-height: $button-bar-button-height;
+ }
+ }
+ &.button-clear {
+ padding-right: 2px;
+ padding-left: 2px;
+ font-weight: 300;
+ font-size: $bar-title-font-size;
+
+ .icon:before,
+ &.icon:before,
+ &.icon-left:before,
+ &.icon-right:before {
+ font-size: $button-large-icon-size;
+ line-height: $button-bar-button-height;
+ }
+ }
+
+ &.back-button {
+ padding: 0;
+ opacity: 0.8;
+ .back-button-title {
+ display: inline-block;
+ vertical-align: middle;
+ margin-left: 4px;
+ }
+ }
+
+ &.back-button.active,
+ &.back-button.activated {
+ opacity: 1;
+ }
+ }
+
+ .button-bar > .button,
+ .buttons > .button {
+ min-height: $button-bar-button-height - 1;
+ line-height: $button-bar-button-height;
+ }
+
+ .button-bar + .button,
+ .button + .button-bar {
+ margin-left: 5px;
+ }
+
+ // Android 4.4 messes with the display property
+ .buttons,
+ .buttons.left-buttons,
+ .buttons.right-buttons {
+ display: inherit;
+ }
+ .buttons span {
+ display: inline-flex;
+ }
+
+ // Place the last button in a bar on the right of the bar
+ .title + .button:last-child,
+ > .button + .button:last-child,
+ > .button.pull-right,
+ .buttons.pull-right,
+ .title + .buttons {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ bottom: 5px;
+ }
+
+}
+
+// Default styles for buttons inside of styled bars
+.bar-light {
+ .button {
+ @include button-style($bar-light-bg, $bar-light-border, $bar-light-active-bg, $bar-light-active-border, $bar-light-text);
+ @include button-clear($bar-light-text, $bar-title-font-size);
+ }
+}
+.bar-stable {
+ .button {
+ @include button-style($bar-stable-bg, $bar-stable-border, $bar-stable-active-bg, $bar-stable-active-border, $bar-stable-text);
+ @include button-clear($bar-stable-text, $bar-title-font-size);
+ }
+}
+.bar-positive {
+ .button {
+ @include button-style($bar-positive-bg, $bar-positive-border, $bar-positive-active-bg, $bar-positive-active-border, $bar-positive-text);
+ @include button-clear(#fff, $bar-title-font-size);
+ }
+}
+.bar-calm {
+ .button {
+ @include button-style($bar-calm-bg, $bar-calm-border, $bar-calm-active-bg, $bar-calm-active-border, $bar-calm-text);
+ @include button-clear(#fff, $bar-title-font-size);
+ }
+}
+.bar-assertive {
+ .button {
+ @include button-style($bar-assertive-bg, $bar-assertive-border, $bar-assertive-active-bg, $bar-assertive-active-border, $bar-assertive-text);
+ @include button-clear(#fff, $bar-title-font-size);
+ }
+}
+.bar-balanced {
+ .button {
+ @include button-style($bar-balanced-bg, $bar-balanced-border, $bar-balanced-active-bg, $bar-balanced-active-border, $bar-balanced-text);
+ @include button-clear(#fff, $bar-title-font-size);
+ }
+}
+.bar-energized {
+ .button {
+ @include button-style($bar-energized-bg, $bar-energized-border, $bar-energized-active-bg, $bar-energized-active-border, $bar-energized-text);
+ @include button-clear(#fff, $bar-title-font-size);
+ }
+}
+.bar-royal {
+ .button {
+ @include button-style($bar-royal-bg, $bar-royal-border, $bar-royal-active-bg, $bar-royal-active-border, $bar-royal-text);
+ @include button-clear(#fff, $bar-title-font-size);
+ }
+}
+.bar-dark {
+ .button {
+ @include button-style($bar-dark-bg, $bar-dark-border, $bar-dark-active-bg, $bar-dark-active-border, $bar-dark-text);
+ @include button-clear(#fff, $bar-title-font-size);
+ }
+}
+
+// Header at top
+.bar-header {
+ top: 0;
+ border-top-width: 0;
+ border-bottom-width: 1px;
+ &.has-tabs-top{
+ border-bottom-width: 0px;
+ }
+}
+
+// Footer at bottom
+.bar-footer {
+ bottom: 0;
+ border-top-width: 1px;
+ border-bottom-width: 0;
+ background-position: top;
+
+ &.item-input-inset {
+ position: absolute;
+ }
+}
+
+// Don't render padding if the bar is just for tabs
+.bar-tabs {
+ padding: 0;
+}
+
+.bar-subheader {
+ top: $bar-height;
+ display: block;
+}
+.bar-subfooter {
+ bottom: $bar-height;
+ display: block;
+}
diff --git a/client/lib/ionic/scss/_button-bar.scss b/client/lib/ionic/scss/_button-bar.scss
new file mode 100644
index 0000000..8be6488
--- /dev/null
+++ b/client/lib/ionic/scss/_button-bar.scss
@@ -0,0 +1,54 @@
+
+/**
+ * Button Bar
+ * --------------------------------------------------
+ */
+
+.button-bar {
+ @include display-flex();
+ @include flex(1);
+ width: 100%;
+
+ &.button-bar-inline {
+ display: block;
+ width: auto;
+
+ @include clearfix();
+
+ > .button {
+ width: auto;
+ display: inline-block;
+ float: left;
+ }
+ }
+}
+
+.button-bar > .button {
+ @include flex(1);
+ display: block;
+
+ overflow: hidden;
+
+ padding: 0 16px;
+
+ width: 0;
+
+ border-width: 1px 0px 1px 1px;
+ border-radius: 0;
+ text-align: center;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+
+ &:before,
+ .icon:before {
+ line-height: 44px;
+ }
+
+ &:first-child {
+ border-radius: 2px 0px 0px 2px;
+ }
+ &:last-child {
+ border-right-width: 1px;
+ border-radius: 0px 2px 2px 0px;
+ }
+}
diff --git a/client/lib/ionic/scss/_button.scss b/client/lib/ionic/scss/_button.scss
new file mode 100644
index 0000000..5ec5ffe
--- /dev/null
+++ b/client/lib/ionic/scss/_button.scss
@@ -0,0 +1,252 @@
+
+/**
+ * Buttons
+ * --------------------------------------------------
+ */
+
+.button {
+ // set the color defaults
+ @include button-style($button-default-bg, $button-default-border, $button-default-active-bg, $button-default-active-border, $button-default-text);
+
+ position: relative;
+ display: inline-block;
+ margin: 0;
+ padding: 0 $button-padding;
+
+ min-width: ($button-padding * 3) + $button-font-size;
+ min-height: $button-height + 5px;
+
+ border-width: $button-border-width;
+ border-style: solid;
+ border-radius: $button-border-radius;
+
+ vertical-align: top;
+ text-align: center;
+
+ text-overflow: ellipsis;
+ font-size: $button-font-size;
+ line-height: $button-height - $button-border-width + 1px;
+
+ cursor: pointer;
+
+ &:after {
+ // used to create a larger button "hit" area
+ position: absolute;
+ top: -6px;
+ right: -6px;
+ bottom: -6px;
+ left: -6px;
+ content: ' ';
+ }
+
+ .icon {
+ vertical-align: top;
+ pointer-events: none;
+ }
+
+ .icon:before,
+ &.icon:before,
+ &.icon-left:before,
+ &.icon-right:before {
+ display: inline-block;
+ padding: 0 0 $button-border-width 0;
+ vertical-align: inherit;
+ font-size: $button-icon-size;
+ line-height: $button-height - $button-border-width;
+ pointer-events: none;
+ }
+ &.icon-left:before {
+ float: left;
+ padding-right: .2em;
+ padding-left: 0;
+ }
+ &.icon-right:before {
+ float: right;
+ padding-right: 0;
+ padding-left: .2em;
+ }
+
+ &.button-block, &.button-full {
+ margin-top: $button-block-margin;
+ margin-bottom: $button-block-margin;
+ }
+
+ &.button-light {
+ @include button-style($button-light-bg, $button-light-border, $button-light-active-bg, $button-light-active-border, $button-light-text);
+ @include button-clear($button-light-border);
+ @include button-outline($button-light-border);
+ }
+
+ &.button-stable {
+ @include button-style($button-stable-bg, $button-stable-border, $button-stable-active-bg, $button-stable-active-border, $button-stable-text);
+ @include button-clear($button-stable-border);
+ @include button-outline($button-stable-border);
+ }
+
+ &.button-positive {
+ @include button-style($button-positive-bg, $button-positive-border, $button-positive-active-bg, $button-positive-active-border, $button-positive-text);
+ @include button-clear($button-positive-bg);
+ @include button-outline($button-positive-bg);
+ }
+
+ &.button-calm {
+ @include button-style($button-calm-bg, $button-calm-border, $button-calm-active-bg, $button-calm-active-border, $button-calm-text);
+ @include button-clear($button-calm-bg);
+ @include button-outline($button-calm-bg);
+ }
+
+ &.button-assertive {
+ @include button-style($button-assertive-bg, $button-assertive-border, $button-assertive-active-bg, $button-assertive-active-border, $button-assertive-text);
+ @include button-clear($button-assertive-bg);
+ @include button-outline($button-assertive-bg);
+ }
+
+ &.button-balanced {
+ @include button-style($button-balanced-bg, $button-balanced-border, $button-balanced-active-bg, $button-balanced-active-border, $button-balanced-text);
+ @include button-clear($button-balanced-bg);
+ @include button-outline($button-balanced-bg);
+ }
+
+ &.button-energized {
+ @include button-style($button-energized-bg, $button-energized-border, $button-energized-active-bg, $button-energized-active-border, $button-energized-text);
+ @include button-clear($button-energized-bg);
+ @include button-outline($button-energized-bg);
+ }
+
+ &.button-royal {
+ @include button-style($button-royal-bg, $button-royal-border, $button-royal-active-bg, $button-royal-active-border, $button-royal-text);
+ @include button-clear($button-royal-bg);
+ @include button-outline($button-royal-bg);
+ }
+
+ &.button-dark {
+ @include button-style($button-dark-bg, $button-dark-border, $button-dark-active-bg, $button-dark-active-border, $button-dark-text);
+ @include button-clear($button-dark-bg);
+ @include button-outline($button-dark-bg);
+ }
+}
+
+.button-small {
+ padding: 2px $button-small-padding 1px;
+ min-width: $button-small-height;
+ min-height: $button-small-height + 2;
+ font-size: $button-small-font-size;
+ line-height: $button-small-height - $button-border-width - 1;
+
+ .icon:before,
+ &.icon:before,
+ &.icon-left:before,
+ &.icon-right:before {
+ font-size: $button-small-icon-size;
+ line-height: $button-small-icon-size + 3;
+ margin-top: 3px;
+ }
+}
+
+.button-large {
+ padding: 0 $button-large-padding;
+ min-width: ($button-large-padding * 3) + $button-large-font-size;
+ min-height: $button-large-height + 5;
+ font-size: $button-large-font-size;
+ line-height: $button-large-height - $button-border-width;
+
+ .icon:before,
+ &.icon:before,
+ &.icon-left:before,
+ &.icon-right:before {
+ padding-bottom: ($button-border-width * 2);
+ font-size: $button-large-icon-size;
+ line-height: $button-large-height - ($button-border-width * 2) - 1;
+ }
+}
+
+.button-icon {
+ @include transition(opacity .1s);
+ padding: 0 6px;
+ min-width: initial;
+ border-color: transparent;
+ background: none;
+
+ &.button.active,
+ &.button.activated {
+ border-color: transparent;
+ background: none;
+ box-shadow: none;
+ opacity: 0.3;
+ }
+
+ .icon:before,
+ &.icon:before {
+ font-size: $button-large-icon-size;
+ }
+}
+
+.button-clear {
+ @include button-clear($button-default-border);
+ @include transition(opacity .1s);
+ padding: 0 $button-clear-padding;
+ max-height: $button-height;
+ border-color: transparent;
+ background: none;
+ box-shadow: none;
+
+ &.active,
+ &.activated {
+ opacity: 0.3;
+ }
+}
+
+.button-outline {
+ @include button-outline($button-default-border);
+ @include transition(opacity .1s);
+ background: none;
+ box-shadow: none;
+}
+
+.padding > .button.button-block:first-child {
+ margin-top: 0;
+}
+
+.button-block {
+ display: block;
+ clear: both;
+
+ &:after {
+ clear: both;
+ }
+}
+
+.button-full,
+.button-full > .button {
+ display: block;
+ margin-right: 0;
+ margin-left: 0;
+ border-right-width: 0;
+ border-left-width: 0;
+ border-radius: 0;
+}
+
+button.button-block,
+button.button-full,
+.button-full > button.button,
+input.button.button-block {
+ width: 100%;
+}
+
+a.button {
+ text-decoration: none;
+
+ .icon:before,
+ &.icon:before,
+ &.icon-left:before,
+ &.icon-right:before {
+ margin-top: 2px;
+ }
+}
+
+.button.disabled,
+.button[disabled] {
+ opacity: .4;
+ cursor: default !important;
+ pointer-events: none;
+}
diff --git a/client/lib/ionic/scss/_checkbox.scss b/client/lib/ionic/scss/_checkbox.scss
new file mode 100644
index 0000000..c1117c6
--- /dev/null
+++ b/client/lib/ionic/scss/_checkbox.scss
@@ -0,0 +1,153 @@
+
+/**
+ * Checkbox
+ * --------------------------------------------------
+ */
+
+.checkbox {
+ // set the color defaults
+ @include checkbox-style($checkbox-off-border-default, $checkbox-on-bg-default);
+
+ position: relative;
+ display: inline-block;
+ padding: ($checkbox-height / 4) ($checkbox-width / 4);
+ cursor: pointer;
+}
+.checkbox-light {
+ @include checkbox-style($checkbox-off-border-light, $checkbox-on-bg-light);
+}
+.checkbox-stable {
+ @include checkbox-style($checkbox-off-border-stable, $checkbox-on-bg-stable);
+}
+.checkbox-positive {
+ @include checkbox-style($checkbox-off-border-positive, $checkbox-on-bg-positive);
+}
+.checkbox-calm {
+ @include checkbox-style($checkbox-off-border-calm, $checkbox-on-bg-calm);
+}
+.checkbox-assertive {
+ @include checkbox-style($checkbox-off-border-assertive, $checkbox-on-bg-assertive);
+}
+.checkbox-balanced {
+ @include checkbox-style($checkbox-off-border-balanced, $checkbox-on-bg-balanced);
+}
+.checkbox-energized{
+ @include checkbox-style($checkbox-off-border-energized, $checkbox-on-bg-energized);
+}
+.checkbox-royal {
+ @include checkbox-style($checkbox-off-border-royal, $checkbox-on-bg-royal);
+}
+.checkbox-dark {
+ @include checkbox-style($checkbox-off-border-dark, $checkbox-on-bg-dark);
+}
+
+.checkbox input:disabled:before,
+.checkbox input:disabled + .checkbox-icon:before {
+ border-color: $checkbox-off-border-light;
+}
+
+.checkbox input:disabled:checked:before,
+.checkbox input:disabled:checked + .checkbox-icon:before {
+ background: $checkbox-on-bg-light;
+}
+
+
+.checkbox.checkbox-input-hidden input {
+ display: none !important;
+}
+
+.checkbox input,
+.checkbox-icon {
+ position: relative;
+ width: $checkbox-width;
+ height: $checkbox-height;
+ display: block;
+ border: 0;
+ background: transparent;
+ cursor: pointer;
+ -webkit-appearance: none;
+
+ &:before {
+ // what the checkbox looks like when its not checked
+ display: table;
+ width: 100%;
+ height: 100%;
+ border-width: $checkbox-border-width;
+ border-style: solid;
+ border-radius: $checkbox-border-radius;
+ background: $checkbox-off-bg-color;
+ content: ' ';
+ transition: background-color 20ms ease-in-out;
+ }
+}
+
+.checkbox input:checked:before,
+input:checked + .checkbox-icon:before {
+ border-width: $checkbox-border-width + 1;
+}
+
+// the checkmark within the box
+.checkbox input:after,
+.checkbox-icon:after {
+ @include transition(opacity .05s ease-in-out);
+ @include rotate(-45deg);
+ position: absolute;
+ top: 30%;
+ left: 26%;
+ display: table;
+ width: ($checkbox-width / 2) + 1;
+ height: ($checkbox-width / 3) + 1;
+ border: $checkbox-check-width solid $checkbox-check-color;
+ border-top: 0;
+ border-right: 0;
+ content: ' ';
+ opacity: 0;
+}
+
+.grade-c .checkbox input:after,
+.grade-c .checkbox-icon:after {
+ @include rotate(0);
+ top: 3px;
+ left: 4px;
+ border: none;
+ color: $checkbox-check-color;
+ content: '\2713';
+ font-weight: bold;
+ font-size: 20px;
+}
+
+// what the checkmark looks like when its checked
+.checkbox input:checked:after,
+input:checked + .checkbox-icon:after {
+ opacity: 1;
+}
+
+// make sure item content have enough padding on left to fit the checkbox
+.item-checkbox {
+ padding-left: ($item-padding * 2) + $checkbox-width;
+
+ &.active {
+ box-shadow: none;
+ }
+}
+
+// position the checkbox to the left within an item
+.item-checkbox .checkbox {
+ position: absolute;
+ top: 50%;
+ right: $item-padding / 2;
+ left: $item-padding / 2;
+ z-index: $z-index-item-checkbox;
+ margin-top: (($checkbox-height + ($checkbox-height / 2)) / 2) * -1;
+}
+
+
+.item-checkbox.item-checkbox-right {
+ padding-right: ($item-padding * 2) + $checkbox-width;
+ padding-left: $item-padding;
+}
+
+.item-checkbox-right .checkbox input,
+.item-checkbox-right .checkbox-icon {
+ float: right;
+}
\ No newline at end of file
diff --git a/client/lib/ionic/scss/_form.scss b/client/lib/ionic/scss/_form.scss
new file mode 100644
index 0000000..ce735af
--- /dev/null
+++ b/client/lib/ionic/scss/_form.scss
@@ -0,0 +1,310 @@
+/**
+ * Forms
+ * --------------------------------------------------
+ */
+
+// Make all forms have space below them
+form {
+ margin: 0 0 $line-height-base;
+}
+
+// Groups of fields with labels on top (legends)
+legend {
+ display: block;
+ margin-bottom: $line-height-base;
+ padding: 0;
+ width: 100%;
+ border: $input-border-width solid $input-border;
+ color: $dark;
+ font-size: $font-size-base * 1.5;
+ line-height: $line-height-base * 2;
+
+ small {
+ color: $stable;
+ font-size: $line-height-base * .75;
+ }
+}
+
+// Set font for forms
+label,
+input,
+button,
+select,
+textarea {
+ @include font-shorthand($font-size-base, normal, $line-height-base); // Set size, weight, line-height here
+}
+input,
+button,
+select,
+textarea {
+ font-family: $font-family-base; // And only set font-family here for those that need it (note the missing label element)
+}
+
+
+// Input List
+// -------------------------------
+
+.item-input {
+ @include display-flex();
+ @include align-items(center);
+ position: relative;
+ overflow: hidden;
+ padding: 6px 0 5px 16px;
+
+ input {
+ @include border-radius(0);
+ @include flex(1, 0, 220px);
+ @include appearance(none);
+ margin: 0;
+ padding-right: 24px;
+ background-color: transparent;
+ }
+
+ .button .icon {
+ @include flex(0, 0, 24px);
+ position: static;
+ display: inline-block;
+ height: auto;
+ text-align: center;
+ font-size: 16px;
+ }
+
+ .button-bar {
+ @include border-radius(0);
+ @include flex(1, 0, 220px);
+ @include appearance(none);
+ }
+
+ .icon {
+ min-width: 14px;
+ }
+}
+
+.item-input-inset {
+ @include display-flex();
+ @include align-items(center);
+ position: relative;
+ overflow: hidden;
+ padding: ($item-padding / 3) * 2;
+}
+
+.item-input-wrapper {
+ @include display-flex();
+ @include flex(1, 0);
+ @include align-items(center);
+ @include border-radius(4px);
+ padding-right: 8px;
+ padding-left: 8px;
+ background: #eee;
+}
+
+.item-input-inset .item-input-wrapper input {
+ padding-left: 4px;
+ height: 29px;
+ background: transparent;
+ line-height: 18px;
+}
+
+.item-input-wrapper ~ .button {
+ margin-left: ($item-padding / 3) * 2;
+}
+
+.input-label {
+ @include flex(1, 0, 100px);
+ display: table;
+ padding: 7px 10px 7px 0px;
+ max-width: 200px;
+ width: 35%;
+ color: $input-label-color;
+ font-size: 16px;
+}
+
+.placeholder-icon {
+ color: #aaa;
+ &:first-child {
+ padding-right: 6px;
+ }
+ &:last-child {
+ padding-left: 6px;
+ }
+}
+
+.item-stacked-label {
+ display: block;
+ background-color: transparent;
+ box-shadow: none;
+
+ .input-label, .icon {
+ display: inline-block;
+ padding: 4px 0 0 0px;
+ vertical-align: middle;
+ }
+}
+
+.item-stacked-label input,
+.item-stacked-label textarea {
+ @include border-radius(2px);
+ padding: 4px 8px 3px 0;
+ border: none;
+ background-color: $input-bg;
+}
+.item-stacked-label input {
+ overflow: hidden;
+ height: $line-height-computed + $font-size-base + 12px;
+}
+
+.item-floating-label {
+ display: block;
+ background-color: transparent;
+ box-shadow: none;
+
+ .input-label {
+ position: relative;
+ padding: 5px 0 0 0;
+ opacity: 0;
+ top: 10px;
+ @include transition(opacity .15s ease-in, top .2s linear);
+
+ &.has-input {
+ opacity: 1;
+ top: 0;
+ @include transition(opacity .15s ease-in, top .2s linear);
+ }
+ }
+}
+
+
+// Form Controls
+// -------------------------------
+
+// Shared size and type resets
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"] {
+ display: block;
+ padding-top: 2px;
+ padding-left: 0;
+ height: $line-height-computed + $font-size-base;
+ color: $input-color;
+ vertical-align: middle;
+ font-size: $font-size-base;
+ line-height: $font-size-base + 2;
+}
+
+.platform-ios,
+.platform-android {
+ input[type="datetime-local"],
+ input[type="date"],
+ input[type="month"],
+ input[type="time"],
+ input[type="week"] {
+ padding-top: 8px;
+ }
+}
+
+input,
+textarea {
+ width: 100%;
+}
+textarea {
+ padding-left: 0;
+ @include placeholder($input-color-placeholder, -3px);
+}
+
+// Reset height since textareas have rows
+textarea {
+ height: auto;
+}
+
+// Everything else
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"] {
+ border: 0;
+}
+
+// Position radios and checkboxes better
+input[type="radio"],
+input[type="checkbox"] {
+ margin: 0;
+ line-height: normal;
+}
+
+// Reset width of input images, buttons, radios, checkboxes
+input[type="file"],
+input[type="image"],
+input[type="submit"],
+input[type="reset"],
+input[type="button"],
+input[type="radio"],
+input[type="checkbox"] {
+ width: auto; // Override of generic input selector
+}
+
+// Set the height of file to match text inputs
+input[type="file"] {
+ line-height: $input-height-base;
+}
+
+// Text input classes to hide text caret during scroll
+.previous-input-focus,
+.cloned-text-input + input,
+.cloned-text-input + textarea {
+ position: absolute !important;
+ left: -9999px;
+ width: 200px;
+}
+
+
+// Placeholder
+// -------------------------------
+input,
+textarea {
+ @include placeholder();
+}
+
+
+// DISABLED STATE
+// -------------------------------
+
+// Disabled and read-only inputs
+input[disabled],
+select[disabled],
+textarea[disabled],
+input[readonly]:not(.cloned-text-input),
+textarea[readonly]:not(.cloned-text-input),
+select[readonly] {
+ background-color: $input-bg-disabled;
+ cursor: not-allowed;
+}
+// Explicitly reset the colors here
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"][readonly],
+input[type="checkbox"][readonly] {
+ background-color: transparent;
+}
diff --git a/client/lib/ionic/scss/_grid.scss b/client/lib/ionic/scss/_grid.scss
new file mode 100644
index 0000000..0653b97
--- /dev/null
+++ b/client/lib/ionic/scss/_grid.scss
@@ -0,0 +1,143 @@
+/**
+ * Grid
+ * --------------------------------------------------
+ * Using flexbox for the grid, inspired by Philip Walton:
+ * http://philipwalton.github.io/solved-by-flexbox/demos/grids/
+ * By default each .col within a .row will evenly take up
+ * available width, and the height of each .col with take
+ * up the height of the tallest .col in the same .row.
+ */
+
+.row {
+ @include display-flex();
+ padding: ($grid-padding-width / 2);
+ width: 100%;
+}
+
+.row-wrap {
+ @include flex-wrap(wrap);
+}
+
+.row + .row {
+ margin-top: ($grid-padding-width / 2) * -1;
+ padding-top: 0;
+}
+
+.col {
+ @include flex(1);
+ display: block;
+ padding: ($grid-padding-width / 2);
+ width: 100%;
+}
+
+
+/* Vertically Align Columns */
+/* .row-* vertically aligns every .col in the .row */
+.row-top {
+ @include align-items(flex-start);
+}
+.row-bottom {
+ @include align-items(flex-end);
+}
+.row-center {
+ @include align-items(center);
+}
+.row-stretch {
+ @include align-items(stretch);
+}
+.row-baseline {
+ @include align-items(baseline);
+}
+
+/* .col-* vertically aligns an individual .col */
+.col-top {
+ @include align-self(flex-start);
+}
+.col-bottom {
+ @include align-self(flex-end);
+}
+.col-center {
+ @include align-self(center);
+}
+
+/* Column Offsets */
+.col-offset-10 {
+ margin-left: 10%;
+}
+.col-offset-20 {
+ margin-left: 20%;
+}
+.col-offset-25 {
+ margin-left: 25%;
+}
+.col-offset-33, .col-offset-34 {
+ margin-left: 33.3333%;
+}
+.col-offset-50 {
+ margin-left: 50%;
+}
+.col-offset-66, .col-offset-67 {
+ margin-left: 66.6666%;
+}
+.col-offset-75 {
+ margin-left: 75%;
+}
+.col-offset-80 {
+ margin-left: 80%;
+}
+.col-offset-90 {
+ margin-left: 90%;
+}
+
+
+/* Explicit Column Percent Sizes */
+/* By default each grid column will evenly distribute */
+/* across the grid. However, you can specify individual */
+/* columns to take up a certain size of the available area */
+.col-10 {
+ @include flex(0, 0, 10%);
+ max-width: 10%;
+}
+.col-20 {
+ @include flex(0, 0, 20%);
+ max-width: 20%;
+}
+.col-25 {
+ @include flex(0, 0, 25%);
+ max-width: 25%;
+}
+.col-33, .col-34 {
+ @include flex(0, 0, 33.3333%);
+ max-width: 33.3333%;
+}
+.col-50 {
+ @include flex(0, 0, 50%);
+ max-width: 50%;
+}
+.col-66, .col-67 {
+ @include flex(0, 0, 66.6666%);
+ max-width: 66.6666%;
+}
+.col-75 {
+ @include flex(0, 0, 75%);
+ max-width: 75%;
+}
+.col-80 {
+ @include flex(0, 0, 80%);
+ max-width: 80%;
+}
+.col-90 {
+ @include flex(0, 0, 90%);
+ max-width: 90%;
+}
+
+
+/* Responsive Grid Classes */
+/* Adding a class of responsive-X to a row */
+/* will trigger the flex-direction to */
+/* change to column and add some margin */
+/* to any columns in the row for clearity */
+
+@include responsive-grid-break('.responsive-sm', $grid-responsive-sm-break);
+@include responsive-grid-break('.responsive-md', $grid-responsive-md-break);
+@include responsive-grid-break('.responsive-lg', $grid-responsive-lg-break);
diff --git a/client/lib/ionic/scss/_items.scss b/client/lib/ionic/scss/_items.scss
new file mode 100644
index 0000000..05edc80
--- /dev/null
+++ b/client/lib/ionic/scss/_items.scss
@@ -0,0 +1,758 @@
+/**
+ * Items
+ * --------------------------------------------------
+ */
+
+.item {
+ @include item-style($item-default-bg, $item-default-border, $item-default-text);
+
+ position: relative;
+ z-index: $z-index-item; // Make sure the borders and stuff don't get hidden by children
+ display: block;
+
+ margin: $item-border-width * -1;
+ padding: $item-padding;
+
+ border-width: $item-border-width;
+ border-style: solid;
+ font-size: $item-font-size;
+
+ h2 {
+ margin: 0 0 4px 0;
+ font-size: 16px;
+ }
+ h3 {
+ margin: 0 0 4px 0;
+ font-size: 14px;
+ }
+ h4 {
+ margin: 0 0 4px 0;
+ font-size: 12px;
+ }
+ h5, h6 {
+ margin: 0 0 3px 0;
+ font-size: 10px;
+ }
+ p {
+ color: #666;
+ font-size: 14px;
+ }
+
+ h1:last-child,
+ h2:last-child,
+ h3:last-child,
+ h4:last-child,
+ h5:last-child,
+ h6:last-child,
+ p:last-child {
+ margin-bottom: 0;
+ }
+
+ // Align badges within items
+ .badge {
+ @include display-flex();
+ position: absolute;
+ top: $item-padding;
+ right: ($item-padding * 2);
+ }
+ &.item-button-right .badge {
+ right: ($item-padding * 2) + 35;
+ }
+ &.item-divider .badge {
+ top: ceil($item-padding / 2);
+ }
+ .badge + .badge {
+ margin-right: 5px;
+ }
+
+ // Different themes for items
+ &.item-light {
+ @include item-style($item-light-bg, $item-light-border, $item-light-text);
+ }
+ &.item-stable {
+ @include item-style($item-stable-bg, $item-stable-border, $item-stable-text);
+ }
+ &.item-positive {
+ @include item-style($item-positive-bg, $item-positive-border, $item-positive-text);
+ }
+ &.item-calm {
+ @include item-style($item-calm-bg, $item-calm-border, $item-calm-text);
+ }
+ &.item-assertive {
+ @include item-style($item-assertive-bg, $item-assertive-border, $item-assertive-text);
+ }
+ &.item-balanced {
+ @include item-style($item-balanced-bg, $item-balanced-border, $item-balanced-text);
+ }
+ &.item-energized {
+ @include item-style($item-energized-bg, $item-energized-border, $item-energized-text);
+ }
+ &.item-royal {
+ @include item-style($item-royal-bg, $item-royal-border, $item-royal-text);
+ }
+ &.item-dark {
+ @include item-style($item-dark-bg, $item-dark-border, $item-dark-text);
+ }
+
+ &[ng-click]:hover {
+ cursor: pointer;
+ }
+
+}
+
+// Link and Button Active States
+.item.active,
+.item.activated,
+.item-complex.active .item-content,
+.item-complex.activated .item-content,
+.item .item-content.active,
+.item .item-content.activated {
+ @include item-active-style($item-default-active-bg, $item-default-active-border);
+
+ // Different active themes for and items
+ &.item-light {
+ @include item-active-style($item-light-active-bg, $item-light-active-border);
+ }
+ &.item-stable {
+ @include item-active-style($item-stable-active-bg, $item-stable-active-border);
+ }
+ &.item-positive {
+ @include item-active-style($item-positive-active-bg, $item-positive-active-border);
+ }
+ &.item-calm {
+ @include item-active-style($item-calm-active-bg, $item-calm-active-border);
+ }
+ &.item-assertive {
+ @include item-active-style($item-assertive-active-bg, $item-assertive-active-border);
+ }
+ &.item-balanced {
+ @include item-active-style($item-balanced-active-bg, $item-balanced-active-border);
+ }
+ &.item-energized {
+ @include item-active-style($item-energized-active-bg, $item-energized-active-border);
+ }
+ &.item-royal {
+ @include item-active-style($item-royal-active-bg, $item-royal-active-border);
+ }
+ &.item-dark {
+ @include item-active-style($item-dark-active-bg, $item-dark-active-border);
+ }
+}
+
+// Handle text overflow
+.item,
+.item h1,
+.item h2,
+.item h3,
+.item h4,
+.item h5,
+.item h6,
+.item p,
+.item-content,
+.item-content h1,
+.item-content h2,
+.item-content h3,
+.item-content h4,
+.item-content h5,
+.item-content h6,
+.item-content p {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+// Linked list items
+a.item {
+ color: inherit;
+ text-decoration: none;
+
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ }
+}
+
+
+/**
+ * Complex Items
+ * --------------------------------------------------
+ * Adding .item-complex allows the .item to be slidable and
+ * have options underneath the button, but also requires an
+ * additional .item-content element inside .item.
+ * Basically .item-complex removes any default settings which
+ * .item added, so that .item-content looks them as just .item.
+ */
+
+.item-complex,
+a.item.item-complex,
+button.item.item-complex {
+ padding: 0;
+}
+.item-complex .item-content,
+.item-radio .item-content {
+ position: relative;
+ z-index: $z-index-item;
+ padding: $item-padding (ceil( ($item-padding * 3) + ($item-padding / 3) ) - 5) $item-padding $item-padding;
+ border: none;
+ background-color: white;
+}
+
+a.item-content {
+ display: block;
+ color: inherit;
+ text-decoration: none;
+}
+
+.item-text-wrap .item,
+.item-text-wrap .item-content,
+.item-text-wrap,
+.item-text-wrap h1,
+.item-text-wrap h2,
+.item-text-wrap h3,
+.item-text-wrap h4,
+.item-text-wrap h5,
+.item-text-wrap h6,
+.item-text-wrap p,
+.item-complex.item-text-wrap .item-content,
+.item-body h1,
+.item-body h2,
+.item-body h3,
+.item-body h4,
+.item-body h5,
+.item-body h6,
+.item-body p {
+ overflow: visible;
+ white-space: normal;
+}
+.item-complex.item-text-wrap,
+.item-complex.item-text-wrap h1,
+.item-complex.item-text-wrap h2,
+.item-complex.item-text-wrap h3,
+.item-complex.item-text-wrap h4,
+.item-complex.item-text-wrap h5,
+.item-complex.item-text-wrap h6,
+.item-complex.item-text-wrap p {
+ overflow: visible;
+ white-space: normal;
+}
+
+// Link and Button Active States
+
+.item-complex{
+ // Stylized items
+ &.item-light > .item-content{
+ @include item-style($item-light-bg, $item-light-border, $item-light-text);
+ &.active, &:active {
+ @include item-active-style($item-light-active-bg, $item-light-active-border);
+ }
+ }
+ &.item-stable > .item-content{
+ @include item-style($item-stable-bg, $item-stable-border, $item-stable-text);
+ &.active, &:active {
+ @include item-active-style($item-stable-active-bg, $item-stable-active-border);
+ }
+ }
+ &.item-positive > .item-content{
+ @include item-style($item-positive-bg, $item-positive-border, $item-positive-text);
+ &.active, &:active {
+ @include item-active-style($item-positive-active-bg, $item-positive-active-border);
+ }
+ }
+ &.item-calm > .item-content{
+ @include item-style($item-calm-bg, $item-calm-border, $item-calm-text);
+ &.active, &:active {
+ @include item-active-style($item-calm-active-bg, $item-calm-active-border);
+ }
+ }
+ &.item-assertive > .item-content{
+ @include item-style($item-assertive-bg, $item-assertive-border, $item-assertive-text);
+ &.active, &:active {
+ @include item-active-style($item-assertive-active-bg, $item-assertive-active-border);
+ }
+ }
+ &.item-balanced > .item-content{
+ @include item-style($item-balanced-bg, $item-balanced-border, $item-balanced-text);
+ &.active, &:active {
+ @include item-active-style($item-balanced-active-bg, $item-balanced-active-border);
+ }
+ }
+ &.item-energized > .item-content{
+ @include item-style($item-energized-bg, $item-energized-border, $item-energized-text);
+ &.active, &:active {
+ @include item-active-style($item-energized-active-bg, $item-energized-active-border);
+ }
+ }
+ &.item-royal > .item-content{
+ @include item-style($item-royal-bg, $item-royal-border, $item-royal-text);
+ &.active, &:active {
+ @include item-active-style($item-royal-active-bg, $item-royal-active-border);
+ }
+ }
+ &.item-dark > .item-content{
+ @include item-style($item-dark-bg, $item-dark-border, $item-dark-text);
+ &.active, &:active {
+ @include item-active-style($item-dark-active-bg, $item-dark-active-border);
+ }
+ }
+}
+
+
+/**
+ * Item Icons
+ * --------------------------------------------------
+ */
+
+.item-icon-left .icon,
+.item-icon-right .icon {
+ @include display-flex();
+ @include align-items(center);
+ position: absolute;
+ top: 0;
+ height: 100%;
+ font-size: $item-icon-font-size;
+
+ &:before {
+ display: block;
+ width: $item-icon-font-size;
+ text-align: center;
+ }
+}
+
+.item .fill-icon {
+ min-width: $item-icon-fill-font-size + 2;
+ min-height: $item-icon-fill-font-size + 2;
+ font-size: $item-icon-fill-font-size;
+}
+
+.item-icon-left {
+ padding-left: ceil( ($item-padding * 3) + ($item-padding / 3) );
+
+ .icon {
+ left: ceil( ($item-padding / 3) * 2);
+ }
+}
+.item-complex.item-icon-left {
+ padding-left: 0;
+
+ .item-content {
+ padding-left: ceil( ($item-padding * 3) + ($item-padding / 3) );
+ }
+}
+
+.item-icon-right {
+ padding-right: ceil( ($item-padding * 3) + ($item-padding / 3) );
+
+ .icon {
+ right: ceil( ($item-padding / 3) * 2);
+ }
+}
+.item-complex.item-icon-right {
+ padding-right: 0;
+
+ .item-content {
+ padding-right: ceil( ($item-padding * 3) + ($item-padding / 3) );
+ }
+}
+
+.item-icon-left.item-icon-right .icon:first-child {
+ right: auto;
+}
+.item-icon-left.item-icon-right .icon:last-child,
+.item-icon-left .item-delete .icon {
+ left: auto;
+}
+
+.item-icon-left .icon-accessory,
+.item-icon-right .icon-accessory {
+ color: $item-icon-accessory-color;
+ font-size: $item-icon-accessory-font-size;
+}
+.item-icon-left .icon-accessory {
+ left: floor($item-padding / 5);
+}
+.item-icon-right .icon-accessory {
+ right: floor($item-padding / 5);
+}
+
+
+/**
+ * Item Button
+ * --------------------------------------------------
+ * An item button is a child button inside an .item (not the entire .item)
+ */
+
+.item-button-left {
+ padding-left: ceil($item-padding * 4.5);
+}
+
+.item-button-left > .button,
+.item-button-left .item-content > .button {
+ @include display-flex();
+ @include align-items(center);
+ position: absolute;
+ top: ceil($item-padding / 2);
+ left: ceil( ($item-padding / 3) * 2);
+ min-width: $item-icon-font-size + ($button-border-width * 2);
+ min-height: $item-icon-font-size + ($button-border-width * 2);
+ font-size: $item-button-font-size;
+ line-height: $item-button-line-height;
+
+ .icon:before {
+ position: relative;
+ left: auto;
+ width: auto;
+ line-height: $item-icon-font-size - 1;
+ }
+
+ > .button {
+ margin: 0px 2px;
+ min-height: $item-icon-font-size + ($button-border-width * 2);
+ font-size: $item-button-font-size;
+ line-height: $item-button-line-height;
+ }
+}
+
+.item-button-right,
+a.item.item-button-right,
+button.item.item-button-right {
+ padding-right: $item-padding * 5;
+}
+
+.item-button-right > .button,
+.item-button-right .item-content > .button,
+.item-button-right > .buttons,
+.item-button-right .item-content > .buttons {
+ @include display-flex();
+ @include align-items(center);
+ position: absolute;
+ top: ceil($item-padding / 2);
+ right: $item-padding;
+ min-width: $item-icon-font-size + ($button-border-width * 2);
+ min-height: $item-icon-font-size + ($button-border-width * 2);
+ font-size: $item-button-font-size;
+ line-height: $item-button-line-height;
+
+ .icon:before {
+ position: relative;
+ left: auto;
+ width: auto;
+ line-height: $item-icon-font-size - 1;
+ }
+
+ > .button {
+ margin: 0px 2px;
+ min-width: $item-icon-font-size + ($button-border-width * 2);
+ min-height: $item-icon-font-size + ($button-border-width * 2);
+ font-size: $item-button-font-size;
+ line-height: $item-button-line-height;
+ }
+}
+
+
+// Item Avatar
+// -------------------------------
+
+.item-avatar,
+.item-avatar .item-content,
+.item-avatar-left,
+.item-avatar-left .item-content {
+ padding-left: $item-avatar-width + ($item-padding * 2);
+ min-height: $item-avatar-width + ($item-padding * 2);
+
+ > img:first-child,
+ .item-image {
+ position: absolute;
+ top: $item-padding;
+ left: $item-padding;
+ max-width: $item-avatar-width;
+ max-height: $item-avatar-height;
+ width: 100%;
+ border-radius: $item-avatar-border-radius;
+ }
+}
+
+.item-avatar-right,
+.item-avatar-right .item-content {
+ padding-right: $item-avatar-width + ($item-padding * 2);
+ min-height: $item-avatar-width + ($item-padding * 2);
+
+ > img:first-child,
+ .item-image {
+ position: absolute;
+ top: $item-padding;
+ right: $item-padding;
+ max-width: $item-avatar-width;
+ max-height: $item-avatar-height;
+ width: 100%;
+ border-radius: $item-avatar-border-radius;
+ }
+}
+
+
+// Item Thumbnails
+// -------------------------------
+
+.item-thumbnail-left,
+.item-thumbnail-left .item-content {
+ padding-left: $item-thumbnail-width + $item-thumbnail-margin + $item-padding;
+ min-height: $item-thumbnail-height + ($item-thumbnail-margin * 2);
+
+ > img:first-child,
+ .item-image {
+ position: absolute;
+ top: $item-thumbnail-margin;
+ left: $item-thumbnail-margin;
+ max-width: $item-thumbnail-width;
+ max-height: $item-thumbnail-height;
+ width: 100%;
+ }
+}
+.item-avatar.item-complex,
+.item-avatar-left.item-complex,
+.item-thumbnail-left.item-complex {
+ padding-left: 0;
+}
+
+.item-thumbnail-right,
+.item-thumbnail-right .item-content {
+ padding-right: $item-thumbnail-width + $item-thumbnail-margin + $item-padding;
+ min-height: $item-thumbnail-height + ($item-thumbnail-margin * 2);
+
+ > img:first-child,
+ .item-image {
+ position: absolute;
+ top: $item-thumbnail-margin;
+ right: $item-thumbnail-margin;
+ max-width: $item-thumbnail-width;
+ max-height: $item-thumbnail-height;
+ width: 100%;
+ }
+}
+.item-avatar-right.item-complex,
+.item-thumbnail-right.item-complex {
+ padding-right: 0;
+}
+
+
+// Item Image
+// -------------------------------
+
+.item-image {
+ padding: 0;
+ text-align: center;
+
+ img:first-child, .list-img {
+ width: 100%;
+ vertical-align: middle;
+ }
+}
+
+
+// Item Body
+// -------------------------------
+
+.item-body {
+ overflow: auto;
+ padding: $item-padding;
+ text-overflow: inherit;
+ white-space: normal;
+
+ h1, h2, h3, h4, h5, h6, p {
+ margin-top: $item-padding;
+ margin-bottom: $item-padding;
+ }
+}
+
+
+// Item Divider
+// -------------------------------
+
+.item-divider {
+ padding-top: ceil($item-padding / 2);
+ padding-bottom: ceil($item-padding / 2);
+ min-height: 30px;
+ background-color: $item-divider-bg;
+ color: $item-divider-color;
+ font-weight: bold;
+}
+
+
+// Item Note
+// -------------------------------
+
+.item-note {
+ float: right;
+ color: #aaa;
+ font-size: 14px;
+}
+
+
+// Item Editing
+// -------------------------------
+
+.item-left-editable .item-content,
+.item-right-editable .item-content {
+ // setup standard transition settings
+ @include transition-duration( $item-edit-transition-duration );
+ @include transition-timing-function( $item-edit-transition-function );
+ -webkit-transition-property: -webkit-transform;
+ -moz-transition-property: -moz-transform;
+ transition-property: transform;
+}
+
+.list-left-editing .item-left-editable .item-content,
+.item-left-editing.item-left-editable .item-content {
+ // actively editing the left side of the item
+ @include translate3d($item-left-edit-open-width, 0, 0);
+}
+
+.list-right-editing .item-right-editable .item-content,
+.item-right-editing.item-right-editable .item-content {
+ // actively editing the left side of the item
+ @include translate3d(-$item-right-edit-open-width, 0, 0);
+}
+
+
+// Item Left Edit Button
+// -------------------------------
+
+.item-left-edit {
+ @include transition(all $item-edit-transition-function $item-edit-transition-duration / 2);
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: $z-index-item-edit;
+ width: $item-left-edit-open-width;
+ height: 100%;
+ line-height: 100%;
+
+ .button {
+ height: 100%;
+
+ &.icon {
+ @include display-flex();
+ @include align-items(center);
+ position: absolute;
+ top: 0;
+ height: 100%;
+ }
+ }
+
+ display: none;
+ opacity: 0;
+ @include translate3d( ($item-left-edit-left - $item-left-edit-open-width) / 2, 0, 0);
+ &.visible {
+ display: block;
+ &.active {
+ opacity: 1;
+ @include translate3d($item-left-edit-left, 0, 0);
+ }
+ }
+}
+.list-left-editing .item-left-edit {
+ @include transition-delay($item-edit-transition-duration / 2);
+}
+
+// Item Delete (Left side edit button)
+// -------------------------------
+
+.item-delete .button.icon {
+ color: $item-delete-icon-color;
+ font-size: $item-delete-icon-size;
+
+ &:hover {
+ opacity: .7;
+ }
+}
+
+
+// Item Right Edit Button
+// -------------------------------
+
+.item-right-edit {
+ @include transition(all $item-edit-transition-function $item-edit-transition-duration / 2);
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: 0;
+ width: $item-right-edit-open-width * 1.5;
+ height: 100%;
+ background: inherit;
+ padding-left: 20px;
+
+ .button {
+ min-width: $item-right-edit-open-width;
+ height: 100%;
+
+ &.icon {
+ @include display-flex();
+ @include align-items(center);
+ position: absolute;
+ top: 0;
+ height: 100%;
+ font-size: $item-reorder-icon-size;
+ }
+ }
+
+ display: none;
+ opacity: 0;
+ @include translate3d($item-right-edit-open-width / 2, 0, 0);
+ &.visible {
+ display: block;
+ z-index: $z-index-item-reorder;
+ &.active {
+ opacity: 1;
+ @include translate3d(0, 0, 0);
+ }
+ }
+}
+.list-right-editing .item-right-edit {
+ @include transition-delay($item-edit-transition-duration / 2);
+}
+
+
+// Item Reordering (Right side edit button)
+// -------------------------------
+
+.item-reorder .button.icon {
+ color: $item-reorder-icon-color;
+ font-size: $item-reorder-icon-size;
+}
+
+.item-reordering {
+ // item is actively being reordered
+ position: absolute;
+ left: 0;
+ top: 0;
+ z-index: $z-index-item-reordering;
+ width: 100%;
+ box-shadow: 0px 0px 10px 0px #aaa;
+
+ .item-reorder {
+ z-index: 1;
+ }
+}
+
+.item-placeholder {
+ // placeholder for the item that's being reordered
+ opacity: 0.7;
+}
+
+
+/**
+ * The hidden right-side buttons that can be exposed under a list item
+ * with dragging.
+ */
+.item-options {
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: $z-index-item-options;
+ height: 100%;
+
+ .button {
+ height: 100%;
+ border: none;
+ border-radius: 0;
+ }
+}
diff --git a/client/lib/ionic/scss/_list.scss b/client/lib/ionic/scss/_list.scss
new file mode 100644
index 0000000..70fb97e
--- /dev/null
+++ b/client/lib/ionic/scss/_list.scss
@@ -0,0 +1,114 @@
+
+/**
+ * Lists
+ * --------------------------------------------------
+ */
+
+.list {
+ position: relative;
+ padding-top: $item-border-width;
+ padding-bottom: $item-border-width;
+ padding-left: 0; // reset padding because ul and ol
+ margin-bottom: 20px;
+}
+.list:last-child {
+ margin-bottom: 0px;
+ &.card{
+ margin-bottom:40px;
+ }
+}
+
+
+/**
+ * List Header
+ * --------------------------------------------------
+ */
+
+.list-header {
+ margin-top: $list-header-margin-top;
+ padding: $list-header-padding;
+ background-color: $list-header-bg;
+ color: $list-header-color;
+ font-weight: bold;
+}
+
+// when its a card make sure it doesn't duplicate top and bottom borders
+.card.list .list-item {
+ padding-right: 1px;
+ padding-left: 1px;
+}
+
+
+/**
+ * Cards and Inset Lists
+ * --------------------------------------------------
+ * A card and list-inset are close to the same thing, except a card as a box shadow.
+ */
+
+.card,
+.list-inset {
+ overflow: hidden;
+ margin: ($content-padding * 2) $content-padding;
+ border-radius: $card-border-radius;
+ background-color: $card-body-bg;
+}
+
+.card {
+ padding-top: $item-border-width;
+ padding-bottom: $item-border-width;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .10);
+}
+
+.padding {
+ .card, .list-inset {
+ margin-left: 0;
+ margin-right: 0;
+ }
+}
+
+.card .item,
+.list-inset .item,
+.padding > .list .item
+{
+ &:first-child {
+ border-top-left-radius: $card-border-radius;
+ border-top-right-radius: $card-border-radius;
+
+ .item-content {
+ border-top-left-radius: $card-border-radius;
+ border-top-right-radius: $card-border-radius;
+ }
+ }
+ &:last-child {
+ border-bottom-right-radius: $card-border-radius;
+ border-bottom-left-radius: $card-border-radius;
+
+ .item-content {
+ border-bottom-right-radius: $card-border-radius;
+ border-bottom-left-radius: $card-border-radius;
+ }
+ }
+}
+
+.card .item:last-child,
+.list-inset .item:last-child {
+ margin-bottom: $item-border-width * -1;
+}
+
+.card .item,
+.list-inset .item,
+.padding > .list .item,
+.padding-horizontal > .list .item {
+ margin-right: 0;
+ margin-left: 0;
+
+ &.item-input input {
+ padding-right: 44px;
+ }
+}
+.padding-left > .list .item {
+ margin-left: 0;
+}
+.padding-right > .list .item {
+ margin-right: 0;
+}
diff --git a/client/lib/ionic/scss/_loading.scss b/client/lib/ionic/scss/_loading.scss
new file mode 100644
index 0000000..e15ac77
--- /dev/null
+++ b/client/lib/ionic/scss/_loading.scss
@@ -0,0 +1,50 @@
+
+/**
+ * Loading
+ * --------------------------------------------------
+ */
+
+.loading-container {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+
+ z-index: $z-index-loading;
+
+ @include display-flex();
+ @include justify-content(center);
+ @include align-items(center);
+
+ @include transition(0.2s opacity linear);
+ visibility: hidden;
+ opacity: 0;
+
+ &:not(.visible) .icon {
+ display: none;
+ }
+ &.visible {
+ visibility: visible;
+ }
+ &.active {
+ opacity: 1;
+ }
+
+ .loading {
+ padding: $loading-padding;
+
+ border-radius: $loading-border-radius;
+ background-color: $loading-bg-color;
+
+ color: $loading-text-color;
+
+ text-align: center;
+ text-overflow: ellipsis;
+ font-size: $loading-font-size;
+
+ h1, h2, h3, h4, h5, h6 {
+ color: $loading-text-color;
+ }
+ }
+}
diff --git a/client/lib/ionic/scss/_menu.scss b/client/lib/ionic/scss/_menu.scss
new file mode 100644
index 0000000..c2bf286
--- /dev/null
+++ b/client/lib/ionic/scss/_menu.scss
@@ -0,0 +1,64 @@
+
+/**
+ * Menus
+ * --------------------------------------------------
+ * Side panel structure
+ */
+
+.menu {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ z-index: $z-index-menu;
+ overflow: hidden;
+
+ min-height: 100%;
+ max-height: 100%;
+ width: $menu-width;
+
+ background-color: $menu-bg;
+
+ .scroll-content {
+ z-index: $z-index-menu-scroll-content;
+ }
+
+ .bar-header {
+ z-index: $z-index-menu-bar-header;
+ }
+}
+
+.menu-content {
+ @include transform(none);
+ box-shadow: $menu-side-shadow;
+}
+
+.menu-open .menu-content .pane,
+.menu-open .menu-content .scroll-content {
+ pointer-events: none;
+}
+
+.grade-b .menu-content,
+.grade-c .menu-content {
+ @include box-sizing(content-box);
+ right: -1px;
+ left: -1px;
+ border-right: 1px solid #ccc;
+ border-left: 1px solid #ccc;
+ box-shadow: none;
+}
+
+.menu-left {
+ left: 0;
+}
+
+.menu-right {
+ right: 0;
+}
+
+.aside-open.aside-resizing .menu-right {
+ display: none;
+}
+
+.menu-animated {
+ @include transition-transform($menu-animation-speed ease);
+}
diff --git a/client/lib/ionic/scss/_mixins.scss b/client/lib/ionic/scss/_mixins.scss
new file mode 100644
index 0000000..1b297cb
--- /dev/null
+++ b/client/lib/ionic/scss/_mixins.scss
@@ -0,0 +1,639 @@
+
+// Button Mixins
+// --------------------------------------------------
+
+@mixin button-style($bg-color, $border-color, $active-bg-color, $active-border-color, $color) {
+ border-color: $border-color;
+ background-color: $bg-color;
+ color: $color;
+
+ // Give desktop users something to play with
+ &:hover {
+ color: $color;
+ text-decoration: none;
+ }
+ &.active,
+ &.activated {
+ border-color: $active-border-color;
+ background-color: $active-bg-color;
+ box-shadow: inset 0px 1px 3px rgba(0,0,0,0.15);
+ }
+}
+
+@mixin button-clear($color, $font-size:"") {
+ &.button-clear {
+ border-color: transparent;
+ background: none;
+ box-shadow: none;
+ color: $color;
+
+ @if $font-size != "" {
+ font-size: $font-size;
+ }
+ }
+ &.button-icon {
+ border-color: transparent;
+ background: none;
+ }
+}
+
+@mixin button-outline($color, $text-color:"") {
+ &.button-outline {
+ border-color: $color;
+ background: transparent;
+ @if $text-color == "" {
+ $text-color: $color;
+ }
+ color: $text-color;
+ &.active,
+ &.activated {
+ background-color: $color;
+ box-shadow: none;
+ color: #fff;
+ }
+ }
+}
+
+
+// Bar Mixins
+// --------------------------------------------------
+
+@mixin bar-style($bg-color, $border-color, $color) {
+ border-color: $border-color;
+ background-color: $bg-color;
+ background-image: linear-gradient(0deg, $border-color, $border-color 50%, transparent 50%);
+ color: $color;
+
+ .title {
+ color: $color;
+ }
+}
+
+
+// Tab Mixins
+// --------------------------------------------------
+
+@mixin tab-style($bg-color, $border-color, $color) {
+ border-color: $border-color;
+ background-color: $bg-color;
+ background-image: linear-gradient(0deg, $border-color, $border-color 50%, transparent 50%);
+ color: $color;
+}
+
+@mixin tab-badge-style($bg-color, $color) {
+ .tab-item .badge {
+ background-color: $bg-color;
+ color: $color;
+ }
+}
+
+
+// Item Mixins
+// --------------------------------------------------
+
+@mixin item-style($bg-color, $border-color, $color) {
+ border-color: $border-color;
+ background-color: $bg-color;
+ color: $color;
+}
+
+@mixin item-active-style($active-bg-color, $active-border-color) {
+ border-color: $active-border-color;
+ background-color: $active-bg-color;
+}
+
+
+// Badge Mixins
+// --------------------------------------------------
+
+@mixin badge-style($bg-color, $color) {
+ background-color: $bg-color;
+ color: $color;
+}
+
+
+// Range Mixins
+// --------------------------------------------------
+
+@mixin range-style($track-bg-color) {
+ &::-webkit-slider-thumb:before {
+ background: $track-bg-color;
+ }
+}
+
+
+// Checkbox Mixins
+// --------------------------------------------------
+
+@mixin checkbox-style($off-border-color, $on-bg-color) {
+ & input:before,
+ & .checkbox-icon:before {
+ border-color: $off-border-color;
+ }
+
+ // what the background looks like when its checked
+ & input:checked:before,
+ & input:checked + .checkbox-icon:before {
+ background: $on-bg-color;
+ }
+}
+
+
+// Toggle Mixins
+// --------------------------------------------------
+
+@mixin toggle-style($on-border-color, $on-bg-color) {
+ // the track when the toggle is "on"
+ & input:checked + .track {
+ border-color: $on-border-color;
+ background-color: $on-bg-color;
+ }
+}
+
+
+// Clearfix
+// --------------------------------------------------
+
+@mixin clearfix {
+ *zoom: 1;
+ &:before,
+ &:after {
+ display: table;
+ content: "";
+ line-height: 0;
+ }
+ &:after {
+ clear: both;
+ }
+}
+
+
+// Placeholder text
+// --------------------------------------------------
+
+@mixin placeholder($color: $input-color-placeholder, $text-indent: 0) {
+ &::-moz-placeholder { /* Firefox 19+ */
+ color: $color;
+ }
+ &:-ms-input-placeholder {
+ color: $color;
+ }
+ &::-webkit-input-placeholder {
+ color: $color;
+ // Safari placeholder margin issue
+ text-indent: $text-indent;
+ }
+}
+
+
+// Text Mixins
+// --------------------------------------------------
+
+@mixin text-size-adjust($value: none) {
+ -webkit-text-size-adjust: $value;
+ -moz-text-size-adjust: $value;
+ text-size-adjust: $value;
+}
+@mixin tap-highlight-transparent() {
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
+ -webkit-tap-highlight-color: transparent; // For some Androids
+}
+@mixin touch-callout($value: none) {
+ -webkit-touch-callout: $value;
+}
+
+
+// Font Mixins
+// --------------------------------------------------
+
+@mixin font-family-serif() {
+ font-family: $serif-font-family;
+}
+@mixin font-family-sans-serif() {
+ font-family: $sans-font-family;
+}
+@mixin font-family-monospace() {
+ font-family: $mono-font-family;
+}
+@mixin font-shorthand($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
+ font-weight: $weight;
+ font-size: $size;
+ line-height: $line-height;
+}
+@mixin font-serif($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
+ @include font-family-serif();
+ @include font-shorthand($size, $weight, $line-height);
+}
+@mixin font-sans-serif($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
+ @include font-family-sans-serif();
+ @include font-shorthand($size, $weight, $line-height);
+}
+@mixin font-monospace($size: $base-font-size, $weight: normal, $line-height: $base-line-height) {
+ @include font-family-monospace();
+ @include font-shorthand($size, $weight, $line-height);
+}
+@mixin font-smoothing($font-smoothing) {
+ -webkit-font-smoothing: $font-smoothing;
+ font-smoothing: $font-smoothing;
+}
+
+
+// Appearance
+// --------------------------------------------------
+
+@mixin appearance($val) {
+ -webkit-appearance: $val;
+ -moz-appearance: $val;
+ appearance: $val;
+}
+
+
+// Border Radius Mixins
+// --------------------------------------------------
+
+@mixin border-radius($radius) {
+ -webkit-border-radius: $radius;
+ -moz-border-radius: $radius;
+ border-radius: $radius;
+}
+
+// Single Corner Border Radius
+@mixin border-top-left-radius($radius) {
+ -webkit-border-top-left-radius: $radius;
+ -moz-border-radius-topleft: $radius;
+ border-top-left-radius: $radius;
+}
+@mixin border-top-right-radius($radius) {
+ -webkit-border-top-right-radius: $radius;
+ -moz-border-radius-topright: $radius;
+ border-top-right-radius: $radius;
+}
+@mixin border-bottom-right-radius($radius) {
+ -webkit-border-bottom-right-radius: $radius;
+ -moz-border-radius-bottomright: $radius;
+ border-bottom-right-radius: $radius;
+}
+@mixin border-bottom-left-radius($radius) {
+ -webkit-border-bottom-left-radius: $radius;
+ -moz-border-radius-bottomleft: $radius;
+ border-bottom-left-radius: $radius;
+}
+
+// Single Side Border Radius
+@mixin border-top-radius($radius) {
+ @include border-top-right-radius($radius);
+ @include border-top-left-radius($radius);
+}
+@mixin border-right-radius($radius) {
+ @include border-top-right-radius($radius);
+ @include border-bottom-right-radius($radius);
+}
+@mixin border-bottom-radius($radius) {
+ @include border-bottom-right-radius($radius);
+ @include border-bottom-left-radius($radius);
+}
+@mixin border-left-radius($radius) {
+ @include border-top-left-radius($radius);
+ @include border-bottom-left-radius($radius);
+}
+
+
+// Box shadows
+// --------------------------------------------------
+
+@mixin box-shadow($shadow...) {
+ -webkit-box-shadow: $shadow;
+ -moz-box-shadow: $shadow;
+ box-shadow: $shadow;
+}
+
+
+// Transition Mixins
+// --------------------------------------------------
+
+@mixin transition($transition...) {
+ -webkit-transition: $transition;
+ -moz-transition: $transition;
+ transition: $transition;
+}
+@mixin transition-delay($transition-delay) {
+ -webkit-transition-delay: $transition-delay;
+ -moz-transition-delay: $transition-delay;
+ transition-delay: $transition-delay;
+}
+@mixin transition-duration($transition-duration) {
+ -webkit-transition-duration: $transition-duration;
+ -moz-transition-duration: $transition-duration;
+ transition-duration: $transition-duration;
+}
+@mixin transition-timing-function($transition-timing) {
+ -webkit-transition-timing-function: $transition-timing;
+ -moz-transition-timing-function: $transition-timing;
+ transition-timing-function: $transition-timing;
+ }
+ @mixin transition-property($property) {
+ -webkit-transition-property: $property;
+ -moz-transition-property: $property;
+ transition-property: $property;
+}
+@mixin transition-transform($properties...) {
+ // special case cuz of transform vendor prefixes
+ -webkit-transition: -webkit-transform $properties;
+ -moz-transition: -moz-transform $properties;
+ transition: transform $properties;
+}
+
+
+// Animation Mixins
+// --------------------------------------------------
+
+@mixin animation($animation) {
+ -webkit-animation: $animation;
+ -moz-animation: $animation;
+ animation: $animation;
+}
+@mixin animation-duration($duration) {
+ -webkit-animation-duration: $duration;
+ -moz-animation-duration: $duration;
+ animation-duration: $duration;
+}
+@mixin animation-direction($direction) {
+ -webkit-animation-direction: $direction;
+ -moz-animation-direction: $direction;
+ animation-direction: $direction;
+}
+@mixin animation-timing-function($animation-timing) {
+ -webkit-animation-timing-function: $animation-timing;
+ -moz-animation-timing-function: $animation-timing;
+ animation-timing-function: $animation-timing;
+}
+@mixin animation-fill-mode($fill-mode) {
+ -webkit-animation-fill-mode: $fill-mode;
+ -moz-animation-fill-mode: $fill-mode;
+ animation-fill-mode: $fill-mode;
+}
+@mixin animation-name($name) {
+ -webkit-animation-name: $name;
+ -moz-animation-name: $name;
+ animation-name: $name;
+}
+@mixin animation-iteration-count($count) {
+ -webkit-animation-iteration-count: $count;
+ -moz-animation-iteration-count: $count;
+ animation-iteration-count: $count;
+}
+
+
+// Transformation Mixins
+// --------------------------------------------------
+
+@mixin rotate($degrees) {
+ @include transform( rotate($degrees) );
+}
+@mixin scale($ratio) {
+ @include transform( scale($ratio) );
+}
+@mixin translate($x, $y) {
+ @include transform( translate($x, $y) );
+}
+@mixin skew($x, $y) {
+ @include transform( skew($x, $y) );
+ -webkit-backface-visibility: hidden;
+}
+@mixin translate3d($x, $y, $z) {
+ @include transform( translate3d($x, $y, $z) );
+}
+@mixin translateZ($z) {
+ @include transform( translateZ($z) );
+}
+@mixin transform($val) {
+ -webkit-transform: $val;
+ -moz-transform: $val;
+ transform: $val;
+}
+
+@mixin transform-origin($left, $top) {
+ -webkit-transform-origin: $left $top;
+ -moz-transform-origin: $left $top;
+ transform-origin: $left $top;
+}
+
+
+// Backface visibility
+// --------------------------------------------------
+// Prevent browsers from flickering when using CSS 3D transforms.
+// Default value is `visible`, but can be changed to `hidden
+
+@mixin backface-visibility($visibility){
+ -webkit-backface-visibility: $visibility;
+ backface-visibility: $visibility;
+}
+
+
+// Background clipping
+// --------------------------------------------------
+
+@mixin background-clip($clip) {
+ -webkit-background-clip: $clip;
+ -moz-background-clip: $clip;
+ background-clip: $clip;
+}
+
+
+// Background sizing
+// --------------------------------------------------
+
+@mixin background-size($size) {
+ -webkit-background-size: $size;
+ -moz-background-size: $size;
+ background-size: $size;
+}
+
+
+// Box sizing
+// --------------------------------------------------
+
+@mixin box-sizing($boxmodel) {
+ -webkit-box-sizing: $boxmodel;
+ -moz-box-sizing: $boxmodel;
+ box-sizing: $boxmodel;
+}
+
+
+// User select
+// --------------------------------------------------
+
+@mixin user-select($select) {
+ -webkit-user-select: $select;
+ -moz-user-select: $select;
+ -ms-user-select: $select;
+ user-select: $select;
+}
+
+
+// Content Columns
+// --------------------------------------------------
+
+@mixin content-columns($columnCount, $columnGap: $grid-gutter-width) {
+ -webkit-column-count: $columnCount;
+ -moz-column-count: $columnCount;
+ column-count: $columnCount;
+ -webkit-column-gap: $columnGap;
+ -moz-column-gap: $columnGap;
+ column-gap: $columnGap;
+}
+
+
+// Flexbox Mixins
+// --------------------------------------------------
+// http://philipwalton.github.io/solved-by-flexbox/
+// https://github.com/philipwalton/solved-by-flexbox
+
+@mixin display-flex {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -moz-box;
+ display: -moz-flex;
+ display: -ms-flexbox;
+ display: flex;
+}
+
+@mixin dislay-inline-flex {
+ display: -webkit-inline-box;
+ display: -webkit-inline-flex;
+ display: -moz-inline-flex;
+ display: -ms-inline-flexbox;
+ display: inline-flex;
+}
+
+@mixin flex-direction($value: row) {
+ @if $value == row-reverse {
+ -webkit-box-direction: reverse;
+ -webkit-box-orient: horizontal;
+ } @else if $value == column {
+ -webkit-box-direction: normal;
+ -webkit-box-orient: vertical;
+ } @else if $value == column-reverse {
+ -webkit-box-direction: reverse;
+ -webkit-box-orient: vertical;
+ } @else {
+ -webkit-box-direction: normal;
+ -webkit-box-orient: horizontal;
+ }
+ -webkit-flex-direction: $value;
+ -moz-flex-direction: $value;
+ -ms-flex-direction: $value;
+ flex-direction: $value;
+}
+
+@mixin flex-wrap($value: nowrap) {
+ // No Webkit Box fallback.
+ -webkit-flex-wrap: $value;
+ -moz-flex-wrap: $value;
+ @if $value == nowrap {
+ -ms-flex-wrap: none;
+ } @else {
+ -ms-flex-wrap: $value;
+ }
+ flex-wrap: $value;
+}
+
+@mixin flex($fg: 1, $fs: null, $fb: null) {
+ -webkit-box-flex: $fg;
+ -webkit-flex: $fg $fs $fb;
+ -moz-box-flex: $fg;
+ -moz-flex: $fg $fs $fb;
+ -ms-flex: $fg $fs $fb;
+ flex: $fg $fs $fb;
+}
+
+@mixin flex-flow($values: (row nowrap)) {
+ // No Webkit Box fallback.
+ -webkit-flex-flow: $values;
+ -moz-flex-flow: $values;
+ -ms-flex-flow: $values;
+ flex-flow: $values;
+}
+
+@mixin align-items($value: stretch) {
+ @if $value == flex-start {
+ -webkit-box-align: start;
+ -ms-flex-align: start;
+ } @else if $value == flex-end {
+ -webkit-box-align: end;
+ -ms-flex-align: end;
+ } @else {
+ -webkit-box-align: $value;
+ -ms-flex-align: $value;
+ }
+ -webkit-align-items: $value;
+ -moz-align-items: $value;
+ align-items: $value;
+}
+
+@mixin align-self($value: auto) {
+ -webkit-align-self: $value;
+ -moz-align-self: $value;
+ @if $value == flex-start {
+ -ms-flex-item-align: start;
+ } @else if $value == flex-end {
+ -ms-flex-item-align: end;
+ } @else {
+ -ms-flex-item-align: $value;
+ }
+ align-self: $value;
+}
+
+@mixin align-content($value: stretch) {
+ -webkit-align-content: $value;
+ -moz-align-content: $value;
+ @if $value == flex-start {
+ -ms-flex-line-pack: start;
+ } @else if $value == flex-end {
+ -ms-flex-line-pack: end;
+ } @else {
+ -ms-flex-line-pack: $value;
+ }
+ align-content: $value;
+}
+
+@mixin justify-content($value: stretch) {
+ @if $value == flex-start {
+ -webkit-box-pack: start;
+ -ms-flex-pack: start;
+ } @else if $value == flex-end {
+ -webkit-box-pack: end;
+ -ms-flex-pack: end;
+ } @else if $value == space-between {
+ -webkit-box-pack: justify;
+ -ms-flex-pack: justify;
+ } @else {
+ -webkit-box-pack: $value;
+ -ms-flex-pack: $value;
+ }
+ -webkit-justify-content: $value;
+ -moz-justify-content: $value;
+ justify-content: $value;
+}
+
+@mixin responsive-grid-break($selector, $max-width) {
+ @media (max-width: $max-width) {
+ #{$selector} {
+ -webkit-box-direction: normal;
+ -moz-box-direction: normal;
+ -webkit-box-orient: vertical;
+ -moz-box-orient: vertical;
+ -webkit-flex-direction: column;
+ -ms-flex-direction: column;
+ flex-direction: column;
+
+ .col, .col-10, .col-20, .col-25, .col-33, .col-34, .col-50, .col-66, .col-67, .col-75, .col-80, .col-90 {
+ @include flex(1);
+ margin-bottom: ($grid-padding-width * 3) / 2;
+ margin-left: 0;
+ max-width: 100%;
+ width: 100%;
+ }
+ }
+ }
+}
diff --git a/client/lib/ionic/scss/_modal.scss b/client/lib/ionic/scss/_modal.scss
new file mode 100644
index 0000000..3e701c9
--- /dev/null
+++ b/client/lib/ionic/scss/_modal.scss
@@ -0,0 +1,94 @@
+
+/**
+ * Modals
+ * --------------------------------------------------
+ * Modals are independent windows that slide in from off-screen.
+ */
+
+.modal-backdrop {
+ @include transition(background-color 300ms ease-in-out);
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: $z-index-modal;
+ width: 100%;
+ height: 100%;
+ background-color: $modal-backdrop-bg-inactive;
+
+ &.active {
+ background-color: $modal-backdrop-bg-active;
+ }
+}
+
+.modal {
+ display: block;
+ position: absolute;
+ top: 0;
+ z-index: $z-index-modal;
+ overflow: hidden;
+ min-height: 100%;
+ width: 100%;
+ background-color: $modal-bg-color;
+}
+
+@media (min-width: $modal-inset-mode-break-point) {
+ // inset mode is when the modal doesn't fill the entire
+ // display but instead is centered within a large display
+ .modal {
+ top: $modal-inset-mode-top;
+ right: $modal-inset-mode-right;
+ bottom: $modal-inset-mode-bottom;
+ left: $modal-inset-mode-left;
+ overflow: visible;
+ min-height: $modal-inset-mode-min-height;
+ width: (100% - $modal-inset-mode-left - $modal-inset-mode-right);
+ }
+
+ .modal.ng-leave-active {
+ bottom: 0;
+ }
+
+ // remove ios header padding from inset header
+ .platform-ios.platform-cordova .modal-wrapper .modal{
+ .bar-header:not(.bar-subheader) {
+ height: $bar-height;
+ > * {
+ margin-top: 0;
+ }
+ }
+ .tabs-top > .tabs,
+ .tabs.tabs-top {
+ top: $bar-height;
+ }
+ .has-header,
+ .bar-subheader {
+ top: $bar-height;
+ }
+ .has-subheader {
+ top: (2 * $bar-height);
+ }
+ .has-tabs-top {
+ top: $bar-height + $tabs-height;
+ }
+ .has-header.has-subheader.has-tabs-top {
+ top: 2 * $bar-height + $tabs-height;
+ }
+ }
+}
+
+// disable clicks on all but the modal
+.modal-open {
+ pointer-events: none;
+
+ .modal,
+ .modal-backdrop {
+ pointer-events: auto;
+ }
+ // prevent clicks on modal when loading overlay is active though
+ &.loading-active {
+ .modal,
+ .modal-backdrop {
+ pointer-events: none;
+ }
+ }
+}
diff --git a/client/lib/ionic/scss/_platform.scss b/client/lib/ionic/scss/_platform.scss
new file mode 100644
index 0000000..4a80492
--- /dev/null
+++ b/client/lib/ionic/scss/_platform.scss
@@ -0,0 +1,150 @@
+
+/**
+ * Platform
+ * --------------------------------------------------
+ * Platform specific tweaks
+ */
+
+
+/**
+ * Apply roboto font
+ */
+
+.roboto {
+ font-family: "Roboto", $font-family-base;
+
+ input {
+ font-family: "Roboto", $font-family-base;
+ }
+}
+/*
+.platform-android {
+
+
+ .bar {
+ padding: 0;
+
+ line-height: 40px;
+
+ .button {
+ line-height: 40px;
+ }
+
+ .button-icon:before {
+ font-size: 24px;
+ }
+ }
+
+ .back-button {
+ &.button-icon:before {
+ line-height: 40px;
+ }
+ margin-left: -3px;
+ padding: 0px 2px !important;
+ &.ion-android-arrow-back:before {
+ font-size: 12px;
+ }
+
+ &.back-button.active,
+ &.back-button.activated {
+ background-color: rgba(0,0,0,0.1);
+ }
+ }
+
+ .item-divider {
+ background: none;
+ border-top-width: 0;
+ border-bottom-width: 2px;
+ text-transform: uppercase;
+ margin-top: 10px;
+ font-size: 14px;
+ }
+ .item {
+ border-left-width: 0;
+ border-right-width: 0;
+ }
+
+ .item-divider ~ .item:not(.item-divider) {
+ border-bottom-width: 0;
+ }
+
+ .back-button:not(.ng-hide) + .left-buttons + .title {
+ // Don't allow normal titles in this mode
+ display: none;
+ }
+
+ .bar .title {
+ text-align: left;
+ font-weight: normal;
+ }
+
+ font-family: 'Roboto';
+
+ h1, h2, h3, h4, h5 {
+ font-family: 'Roboto', $font-family-base;
+ }
+
+ .tab-item {
+ font-family: 'Roboto', $font-family-base;
+ }
+
+
+ input, button, select, textarea {
+ font-family: 'Roboto', $font-family-base;
+ }
+ */
+//}
+
+.platform-ios.platform-cordova {
+ // iOS7/8 has a status bar which sits on top of the header.
+ // Bump down everything to make room for it. However, if
+ // if its in Cordova, and set to fullscreen, then disregard the bump.
+ &:not(.fullscreen) {
+ .bar-header:not(.bar-subheader) {
+ height: $bar-height + $ios-statusbar-height;
+
+ &.item-input-inset .item-input-wrapper {
+ margin-top: 19px !important;
+ }
+
+ > * {
+ margin-top: $ios-statusbar-height;
+ }
+ }
+ .tabs-top > .tabs,
+ .tabs.tabs-top {
+ top: $bar-height + $ios-statusbar-height;
+ }
+
+ .has-header,
+ .bar-subheader {
+ top: $bar-height + $ios-statusbar-height;
+ }
+ .has-subheader {
+ top: (2 * $bar-height) + $ios-statusbar-height;
+ }
+ .has-tabs-top {
+ top: $bar-height + $tabs-height + $ios-statusbar-height;
+ }
+ .has-header.has-subheader.has-tabs-top {
+ top: 2 * $bar-height + $tabs-height + $ios-statusbar-height;
+ }
+ }
+ &.status-bar-hide {
+ // Cordova doesn't adjust the body height correctly, this makes up for it
+ margin-bottom: 20px;
+ }
+}
+
+@media (orientation:landscape) {
+ .platform-ios.platform-browser.platform-ipad {
+ position: fixed; // required for iPad 7 Safari
+ }
+}
+
+.platform-c:not(.enable-transitions) * {
+ // disable transitions on grade-c devices (Android 2)
+ -webkit-transition: none !important;
+ transition: none !important;
+}
+
diff --git a/client/lib/ionic/scss/_popover.scss b/client/lib/ionic/scss/_popover.scss
new file mode 100644
index 0000000..c6eaf5c
--- /dev/null
+++ b/client/lib/ionic/scss/_popover.scss
@@ -0,0 +1,168 @@
+
+/**
+ * Popovers
+ * --------------------------------------------------
+ * Popovers are independent views which float over content
+ */
+
+.popover-backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: $z-index-popover;
+ width: 100%;
+ height: 100%;
+ background-color: $popover-backdrop-bg-inactive;
+
+ &.active {
+ background-color: $popover-backdrop-bg-active;
+ }
+}
+
+.popover {
+ position: absolute;
+ top: 25%;
+ left: 50%;
+ z-index: $z-index-popover;
+ display: block;
+ margin-top: 12px;
+ margin-left: -$popover-width / 2;
+ height: $popover-height;
+ width: $popover-width;
+ background-color: $popover-bg-color;
+ box-shadow: $popover-box-shadow;
+ opacity: 0;
+
+ .item:first-child {
+ border-top: 0;
+ }
+
+ .item:last-child {
+ border-bottom: 0;
+ }
+
+ &.popover-bottom {
+ margin-top: -12px;
+ }
+}
+
+
+// Set popover border-radius
+.popover,
+.popover .bar-header {
+ border-radius: $popover-border-radius;
+}
+.popover .scroll-content {
+ z-index: 1;
+ margin: 2px 0;
+}
+.popover .bar-header {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.popover .has-header {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+.popover-arrow {
+ display: none;
+}
+
+
+// iOS Popover
+.platform-ios {
+
+ .popover {
+ box-shadow: $popover-box-shadow-ios;
+ }
+
+ .popover,
+ .popover .bar-header {
+ border-radius: $popover-border-radius-ios;
+ }
+ .popover .scroll-content {
+ margin: 8px 0;
+ border-radius: $popover-border-radius-ios;
+ }
+ .popover .scroll-content.has-header {
+ margin-top: 0;
+ }
+ .popover-arrow {
+ position: absolute;
+ display: block;
+ top: -17px;
+ width: 30px;
+ height: 19px;
+ overflow: hidden;
+
+ &:after {
+ position: absolute;
+ top: 12px;
+ left: 5px;
+ width: 20px;
+ height: 20px;
+ background-color: $popover-bg-color;
+ border-radius: 3px;
+ content: '';
+ @include rotate(-45deg);
+ }
+ }
+ .popover-bottom .popover-arrow {
+ top: auto;
+ bottom: -10px;
+ &:after {
+ top: -6px;
+ }
+ }
+}
+
+
+// Android Popover
+.platform-android {
+
+ .popover {
+ margin-top: -32px;
+ background-color: $popover-bg-color-android;
+ box-shadow: $popover-box-shadow-android;
+
+ .item {
+ border-color: $popover-bg-color-android;
+ background-color: $popover-bg-color-android;
+ color: #4d4d4d;
+ }
+ &.popover-bottom {
+ margin-top: 32px;
+ }
+ }
+
+ .popover-backdrop,
+ .popover-backdrop.active {
+ background-color: transparent;
+ }
+}
+
+
+// disable clicks on all but the popover
+.popover-open {
+ pointer-events: none;
+
+ .popover,
+ .popover-backdrop {
+ pointer-events: auto;
+ }
+ // prevent clicks on popover when loading overlay is active though
+ &.loading-active {
+ .popover,
+ .popover-backdrop {
+ pointer-events: none;
+ }
+ }
+}
+
+
+// wider popover on larger viewports
+@media (min-width: $popover-large-break-point) {
+ .popover {
+ width: $popover-large-width;
+ }
+}
diff --git a/client/lib/ionic/scss/_popup.scss b/client/lib/ionic/scss/_popup.scss
new file mode 100644
index 0000000..68f5cab
--- /dev/null
+++ b/client/lib/ionic/scss/_popup.scss
@@ -0,0 +1,105 @@
+
+/**
+ * Popups
+ * --------------------------------------------------
+ */
+
+.popup-container {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ background: rgba(0,0,0,0);
+
+ @include display-flex();
+ @include justify-content(center);
+ @include align-items(center);
+
+ z-index: $z-index-popup;
+
+ // Start hidden
+ visibility: hidden;
+ &.popup-showing {
+ visibility: visible;
+ }
+
+ &.popup-hidden .popup {
+ @include animation-name(scaleOut);
+ @include animation-duration($popup-leave-animation-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
+
+ &.active .popup {
+ @include animation-name(superScaleIn);
+ @include animation-duration($popup-enter-animation-duration);
+ @include animation-timing-function(ease-in-out);
+ @include animation-fill-mode(both);
+ }
+
+ .popup {
+ width: $popup-width;
+ max-width: 100%;
+ max-height: 90%;
+
+ border-radius: $popup-border-radius;
+ background-color: $popup-background-color;
+
+ @include display-flex();
+ @include flex-direction(column);
+ }
+}
+
+.popup-head {
+ padding: 15px 10px;
+ border-bottom: 1px solid #eee;
+ text-align: center;
+}
+.popup-title {
+ margin: 0;
+ padding: 0;
+ font-size: 15px;
+}
+.popup-sub-title {
+ margin: 5px 0 0 0;
+ padding: 0;
+ font-weight: normal;
+ font-size: 11px;
+}
+.popup-body {
+ padding: 10px;
+ overflow: scroll;
+}
+
+.popup-buttons {
+ @include display-flex();
+ @include flex-direction(row);
+ padding: 10px;
+ min-height: $popup-button-min-height + 20;
+
+ .button {
+ @include flex(1);
+ display: block;
+ min-height: $popup-button-min-height;
+ border-radius: $popup-button-border-radius;
+ line-height: $popup-button-line-height;
+
+ margin-right: 5px;
+ &:last-child {
+ margin-right: 0px;
+ }
+ }
+}
+
+.popup-open {
+ pointer-events: none;
+
+ &.modal-open .modal {
+ pointer-events: none;
+ }
+
+ .popup-backdrop, .popup {
+ pointer-events: auto;
+ }
+}
diff --git a/client/lib/ionic/scss/_progress.scss b/client/lib/ionic/scss/_progress.scss
new file mode 100644
index 0000000..5defdcb
--- /dev/null
+++ b/client/lib/ionic/scss/_progress.scss
@@ -0,0 +1,11 @@
+
+/**
+ * Progress
+ * --------------------------------------------------
+ */
+
+progress {
+ display: block;
+ margin: $progress-margin;
+ width: $progress-width;
+}
diff --git a/client/lib/ionic/scss/_radio.scss b/client/lib/ionic/scss/_radio.scss
new file mode 100644
index 0000000..0f75fa1
--- /dev/null
+++ b/client/lib/ionic/scss/_radio.scss
@@ -0,0 +1,57 @@
+
+/**
+ * Radio Button Inputs
+ * --------------------------------------------------
+ */
+
+.item-radio {
+ padding: 0;
+
+ &:hover {
+ cursor: pointer;
+ }
+}
+
+.item-radio .item-content {
+ /* give some room to the right for the checkmark icon */
+ padding-right: $item-padding * 4;
+}
+
+.item-radio .radio-icon {
+ /* checkmark icon will be hidden by default */
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: $z-index-item-radio;
+ visibility: hidden;
+ padding: $item-padding - 2;
+ height: 100%;
+ font-size: 24px;
+}
+
+.item-radio input {
+ /* hide any radio button inputs elements (the ugly circles) */
+ position: absolute;
+ left: -9999px;
+
+ &:checked ~ .item-content {
+ /* style the item content when its checked */
+ background: #f7f7f7;
+ }
+
+ &:checked ~ .radio-icon {
+ /* show the checkmark icon when its checked */
+ visibility: visible;
+ }
+}
+
+// Hack for Android to correctly display the checked item
+// http://timpietrusky.com/advanced-checkbox-hack
+.platform-android.grade-b .item-radio,
+.platform-android.grade-c .item-radio {
+ -webkit-animation: androidCheckedbugfix infinite 1s;
+}
+@-webkit-keyframes androidCheckedbugfix {
+ from { padding: 0; }
+ to { padding: 0; }
+}
diff --git a/client/lib/ionic/scss/_range.scss b/client/lib/ionic/scss/_range.scss
new file mode 100644
index 0000000..92f1520
--- /dev/null
+++ b/client/lib/ionic/scss/_range.scss
@@ -0,0 +1,121 @@
+
+/**
+ * Range
+ * --------------------------------------------------
+ */
+
+input[type="range"] {
+ display: inline-block;
+ overflow: hidden;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ padding-right: 2px;
+ padding-left: 1px;
+ width: auto;
+ height: $range-slider-height + 15;
+ outline: none;
+ background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, $range-default-track-bg), color-stop(100%, $range-default-track-bg));
+ background: linear-gradient(to right, $range-default-track-bg 0%, $range-default-track-bg 100%);
+ background-position: center;
+ background-size: 99% $range-track-height;
+ background-repeat: no-repeat;
+ -webkit-appearance: none;
+
+ &::-webkit-slider-thumb {
+ position: relative;
+ width: $range-slider-width;
+ height: $range-slider-height;
+ border-radius: $range-slider-border-radius;
+ background-color: $toggle-handle-off-bg-color;
+ box-shadow: 0 0 2px rgba(0,0,0,.5), 1px 3px 5px rgba(0,0,0,0.25);
+ cursor: pointer;
+ -webkit-appearance: none;
+ }
+
+ &::-webkit-slider-thumb:before {
+ /* what creates the colorful line on the left side of the slider */
+ position: absolute;
+ top: ($range-slider-height / 2) - ($range-track-height / 2);
+ left: -2001px;
+ width: 2000px;
+ height: $range-track-height;
+ background: $dark;
+ content: ' ';
+ }
+
+ &::-webkit-slider-thumb:after {
+ /* create a larger (but hidden) hit area */
+ position: absolute;
+ top: -20px;
+ left: -20px;
+ padding: 30px;
+ content: ' ';
+ //background: red;
+ //opacity: .5;
+ }
+
+}
+
+.range {
+ @include display-flex();
+ @include align-items(center);
+ padding: 2px 11px;
+
+ &.range-light {
+ input { @include range-style($range-light-track-bg); }
+ }
+ &.range-stable {
+ input { @include range-style($range-stable-track-bg); }
+ }
+ &.range-positive {
+ input { @include range-style($range-positive-track-bg); }
+ }
+ &.range-calm {
+ input { @include range-style($range-calm-track-bg); }
+ }
+ &.range-balanced {
+ input { @include range-style($range-balanced-track-bg); }
+ }
+ &.range-assertive {
+ input { @include range-style($range-assertive-track-bg); }
+ }
+ &.range-energized {
+ input { @include range-style($range-energized-track-bg); }
+ }
+ &.range-royal {
+ input { @include range-style($range-royal-track-bg); }
+ }
+ &.range-dark {
+ input { @include range-style($range-dark-track-bg); }
+ }
+}
+
+.range .icon {
+ @include flex(0);
+ display: block;
+ min-width: $range-icon-size;
+ text-align: center;
+ font-size: $range-icon-size;
+}
+
+.range input {
+ @include flex(1);
+ display: block;
+ margin-right: 10px;
+ margin-left: 10px;
+}
+
+.range-label {
+ @include flex(0, 0, auto);
+ display: block;
+ white-space: nowrap;
+}
+
+.range-label:first-child {
+ padding-left: 5px;
+}
+.range input + .range-label {
+ padding-right: 5px;
+ padding-left: 0;
+}
+
diff --git a/client/lib/ionic/scss/_reset.scss b/client/lib/ionic/scss/_reset.scss
new file mode 100644
index 0000000..49e706a
--- /dev/null
+++ b/client/lib/ionic/scss/_reset.scss
@@ -0,0 +1,365 @@
+
+/**
+ * Resets
+ * --------------------------------------------------
+ * Adapted from normalize.css and some reset.css. We don't care even one
+ * bit about old IE, so we don't need any hacks for that in here.
+ *
+ * There are probably other things we could remove here, as well.
+ *
+ * normalize.css v2.1.2 | MIT License | git.io/normalize
+
+ * Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/)
+ * http://cssreset.com
+ */
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, i, u, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed, fieldset,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ vertical-align: baseline;
+ font: inherit;
+ font-size: 100%;
+}
+
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+
+/**
+ * Prevent modern browsers from displaying `audio` without controls.
+ * Remove excess height in iOS 5 devices.
+ */
+
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+
+/**
+ * Hide the `template` element in IE, Safari, and Firefox < 22.
+ */
+
+[hidden],
+template {
+ display: none;
+}
+
+script {
+ display: none !important;
+}
+
+/* ==========================================================================
+ Base
+ ========================================================================== */
+
+/**
+ * 1. Set default font family to sans-serif.
+ * 2. Prevent iOS text size adjust after orientation change, without disabling
+ * user zoom.
+ */
+
+html {
+ @include user-select(none);
+ font-family: sans-serif; /* 1 */
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%; /* 2 */
+ -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/**
+ * Remove default margin.
+ */
+
+body {
+ margin: 0;
+ line-height: 1;
+}
+
+
+/**
+ * Remove default outlines.
+ */
+a,
+button,
+:focus,
+a:focus,
+button:focus,
+a:active,
+a:hover {
+ outline: 0;
+}
+
+/* *
+ * Remove tap highlight color
+ */
+
+a {
+ -webkit-user-drag: none;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+
+ &[href]:hover {
+ cursor: pointer;
+ }
+}
+
+/* ==========================================================================
+ Typography
+ ========================================================================== */
+
+
+/**
+ * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
+ */
+
+b,
+strong {
+ font-weight: bold;
+}
+
+/**
+ * Address styling not present in Safari 5 and Chrome.
+ */
+
+dfn {
+ font-style: italic;
+}
+
+/**
+ * Address differences between Firefox and other browsers.
+ */
+
+hr {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ height: 0;
+}
+
+
+/**
+ * Correct font family set oddly in Safari 5 and Chrome.
+ */
+
+code,
+kbd,
+pre,
+samp {
+ font-size: 1em;
+ font-family: monospace, serif;
+}
+
+/**
+ * Improve readability of pre-formatted text in all browsers.
+ */
+
+pre {
+ white-space: pre-wrap;
+}
+
+/**
+ * Set consistent quote types.
+ */
+
+q {
+ quotes: "\201C" "\201D" "\2018" "\2019";
+}
+
+/**
+ * Address inconsistent and variable font size in all browsers.
+ */
+
+small {
+ font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` affecting `line-height` in all browsers.
+ */
+
+sub,
+sup {
+ position: relative;
+ vertical-align: baseline;
+ font-size: 75%;
+ line-height: 0;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+/**
+ * Define consistent border, margin, and padding.
+ */
+
+fieldset {
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+ border: 1px solid #c0c0c0;
+}
+
+/**
+ * 1. Correct `color` not being inherited in IE 8/9.
+ * 2. Remove padding so people aren't caught out if they zero out fieldsets.
+ */
+
+legend {
+ padding: 0; /* 2 */
+ border: 0; /* 1 */
+}
+
+/**
+ * 1. Correct font family not being inherited in all browsers.
+ * 2. Correct font size not being inherited in all browsers.
+ * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
+ * 4. Remove any default :focus styles
+ * 5. Make sure webkit font smoothing is being inherited
+ * 6. Remove default gradient in Android Firefox / FirefoxOS
+ */
+
+button,
+input,
+select,
+textarea {
+ margin: 0; /* 3 */
+ font-size: 100%; /* 2 */
+ font-family: inherit; /* 1 */
+ outline-offset: 0; /* 4 */
+ outline-style: none; /* 4 */
+ outline-width: 0; /* 4 */
+ -webkit-font-smoothing: inherit; /* 5 */
+ background-image: none; /* 6 */
+}
+
+/**
+ * Address Firefox 4+ setting `line-height` on `input` using `importnt` in
+ * the UA stylesheet.
+ */
+
+button,
+input {
+ line-height: normal;
+}
+
+/**
+ * Address inconsistent `text-transform` inheritance for `button` and `select`.
+ * All other form control elements do not inherit `text-transform` values.
+ * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
+ * Correct `select` style inheritance in Firefox 4+ and Opera.
+ */
+
+button,
+select {
+ text-transform: none;
+}
+
+/**
+ * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+ * and `video` controls.
+ * 2. Correct inability to style clickable `input` types in iOS.
+ * 3. Improve usability and consistency of cursor style between image-type
+ * `input` and others.
+ */
+
+button,
+html input[type="button"], /* 1 */
+input[type="reset"],
+input[type="submit"] {
+ cursor: pointer; /* 3 */
+ -webkit-appearance: button; /* 2 */
+}
+
+/**
+ * Re-set default cursor for disabled elements.
+ */
+
+button[disabled],
+html input[disabled] {
+ cursor: default;
+}
+
+/**
+ * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
+ * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
+ * (include `-moz` to future-proof).
+ */
+
+input[type="search"] {
+ -webkit-box-sizing: content-box; /* 2 */
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ -webkit-appearance: textfield; /* 1 */
+}
+
+/**
+ * Remove inner padding and search cancel button in Safari 5 and Chrome
+ * on OS X.
+ */
+
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+ * Remove inner padding and border in Firefox 4+.
+ */
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+
+/**
+ * 1. Remove default vertical scrollbar in IE 8/9.
+ * 2. Improve readability and alignment in all browsers.
+ */
+
+textarea {
+ overflow: auto; /* 1 */
+ vertical-align: top; /* 2 */
+}
+
+
+img {
+ -webkit-user-drag: none;
+}
+
+/* ==========================================================================
+ Tables
+ ========================================================================== */
+
+/**
+ * Remove most spacing between table cells.
+ */
+
+table {
+ border-spacing: 0;
+ border-collapse: collapse;
+}
diff --git a/client/lib/ionic/scss/_scaffolding.scss b/client/lib/ionic/scss/_scaffolding.scss
new file mode 100644
index 0000000..7182fe9
--- /dev/null
+++ b/client/lib/ionic/scss/_scaffolding.scss
@@ -0,0 +1,364 @@
+
+/**
+ * Scaffolding
+ * --------------------------------------------------
+ */
+
+*,
+*:before,
+*:after {
+ @include box-sizing(border-box);
+}
+
+html {
+ overflow: hidden;
+ -ms-touch-action: pan-y;
+ touch-action: pan-y;
+}
+
+body,
+.ionic-body {
+ @include touch-callout(none);
+ @include font-smoothing(antialiased);
+ @include text-size-adjust(none);
+ @include tap-highlight-transparent();
+ @include user-select(none);
+
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ overflow: hidden;
+
+ margin: 0;
+ padding: 0;
+
+ color: $base-color;
+ word-wrap: break-word;
+ font-size: $font-size-base;
+ font-family: $font-family-base;
+ line-height: $line-height-computed;
+ text-rendering: optimizeLegibility;
+ -webkit-backface-visibility: hidden;
+ -webkit-user-drag: none;
+}
+
+body.grade-b,
+body.grade-c {
+ // disable optimizeLegibility for low end devices
+ text-rendering: auto;
+}
+
+.content {
+ // used for content areas not using the content directive
+ position: relative;
+}
+
+.scroll-content {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ overflow: hidden;
+
+ // Hide the top border if any
+ margin-top: -1px;
+
+ // Prevents any distortion of lines
+ padding-top:1px;
+
+ width: auto;
+ height: auto;
+}
+
+.scroll-content-false,
+.menu .scroll-content.scroll-content-false{
+ z-index: $z-index-scroll-content-false;
+}
+
+.scroll-view {
+ position: relative;
+ display: block;
+ overflow: hidden;
+
+ // Hide the top border if any
+ margin-top: -1px;
+}
+
+/**
+ * Scroll is the scroll view component available for complex and custom
+ * scroll view functionality.
+ */
+.scroll {
+ @include user-select(none);
+ @include touch-callout(none);
+ @include text-size-adjust(none);
+ @include transform-origin(left, top);
+}
+
+// hide webkit scrollbars
+::-webkit-scrollbar {
+ display:none;
+}
+
+// Scroll bar styles
+.scroll-bar {
+ position: absolute;
+ z-index: $z-index-scroll-bar;
+}
+// hide the scroll-bar during animations
+.ng-animate .scroll-bar {
+ visibility: hidden;
+}
+.scroll-bar-h {
+ right: 2px;
+ bottom: 3px;
+ left: 2px;
+ height: 3px;
+
+ .scroll-bar-indicator {
+ height: 100%;
+ }
+}
+
+.scroll-bar-v {
+ top: 2px;
+ right: 3px;
+ bottom: 2px;
+ width: 3px;
+
+ .scroll-bar-indicator {
+ width: 100%;
+ }
+}
+.scroll-bar-indicator {
+ position: absolute;
+ border-radius: 4px;
+ background: rgba(0,0,0,0.3);
+ opacity: 1;
+ @include transition(opacity .3s linear);
+
+ &.scroll-bar-fade-out {
+ opacity: 0;
+ }
+}
+.grade-b .scroll-bar-indicator,
+.grade-c .scroll-bar-indicator {
+ // disable rgba background and border radius for low end devices
+ border-radius: 0;
+ background: #aaa;
+
+ &.scroll-bar-fade-out {
+ @include transition(none);
+ }
+}
+
+@keyframes refresh-spin {
+ 0% { transform: translate3d(0,0,0) rotate(0); }
+ 100% { transform: translate3d(0,0,0) rotate(180deg); }
+}
+
+@-webkit-keyframes refresh-spin {
+ 0% {-webkit-transform: translate3d(0,0,0) rotate(0); }
+ 100% {-webkit-transform: translate3d(0,0,0) rotate(180deg); }
+}
+
+@keyframes refresh-spin-back {
+ 0% { transform: translate3d(0,0,0) rotate(180deg); }
+ 100% { transform: translate3d(0,0,0) rotate(0); }
+}
+
+@-webkit-keyframes refresh-spin-back {
+ 0% {-webkit-transform: translate3d(0,0,0) rotate(180deg); }
+ 100% {-webkit-transform: translate3d(0,0,0) rotate(0); }
+}
+
+// Scroll refresher (for pull to refresh)
+.scroll-refresher {
+ position: absolute;
+ top: -60px;
+ right: 0;
+ left: 0;
+ overflow: hidden;
+ margin: auto;
+ height: 60px;
+
+ .ionic-refresher-content {
+ position: absolute;
+ bottom: 15px;
+ left: 0;
+ width: 100%;
+ color: $scroll-refresh-icon-color;
+ text-align: center;
+
+ font-size: 30px;
+
+ .text-refreshing,
+ .text-pulling {
+ font-size: 16px;
+ line-height: 16px;
+ }
+ &.ionic-refresher-with-text {
+ bottom: 10px;
+ }
+ }
+
+ .icon-refreshing,
+ .icon-pulling {
+ width: 100%;
+ -webkit-backface-visibility: hidden;
+ -webkit-transform-style: preserve-3d;
+ backface-visibility: hidden;
+ transform-style: preserve-3d;
+ }
+ .icon-pulling {
+ @include animation-name(refresh-spin-back);
+ @include animation-duration(200ms);
+ @include animation-timing-function(linear);
+ @include animation-fill-mode(none);
+ -webkit-transform: translate3d(0,0,0) rotate(0deg);
+ transform: translate3d(0,0,0) rotate(0deg);
+ }
+ .icon-refreshing,
+ .text-refreshing {
+ display: none;
+ }
+ .icon-refreshing {
+ @include animation-duration(1.5s);
+ }
+
+ &.active {
+ .icon-pulling:not(.pulling-rotation-disabled) {
+ @include animation-name(refresh-spin);
+ -webkit-transform: translate3d(0,0,0) rotate(-180deg);
+ transform: translate3d(0,0,0) rotate(-180deg);
+ }
+ &.refreshing {
+ @include transition(transform .2s);
+ @include transition(-webkit-transform .2s);
+ -webkit-transform: scale(1,1);
+ transform: scale(1,1);
+ .icon-pulling,
+ .text-pulling {
+ display: none;
+ }
+ .icon-refreshing,
+ .text-refreshing {
+ display: block;
+ }
+ &.refreshing-tail{
+ -webkit-transform: scale(0,0);
+ transform: scale(0,0);
+ }
+ }
+ }
+}
+
+ion-infinite-scroll {
+ height: 60px;
+ width: 100%;
+ opacity: 0;
+ display: block;
+
+ @include transition(opacity 0.25s);
+ @include display-flex();
+ @include flex-direction(row);
+ @include justify-content(center);
+ @include align-items(center);
+
+ .icon {
+ color: #666666;
+ font-size: 30px;
+ color: $scroll-refresh-icon-color;
+ }
+
+ &.active {
+ opacity: 1;
+ }
+}
+
+.overflow-scroll {
+ overflow-x: hidden;
+ overflow-y: scroll;
+ -webkit-overflow-scrolling: touch;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ position: absolute;
+
+ .scroll {
+ position: static;
+ height: 100%;
+ -webkit-transform: translate3d(0, 0, 0); // fix iOS bug where relative children of scroller disapear while scrolling. see: http://stackoverflow.com/questions/9807620/ipad-safari-scrolling-causes-html-elements-to-disappear-and-reappear-with-a-dela
+ }
+}
+
+
+// Pad top/bottom of content so it doesn't hide behind .bar-title and .bar-tab.
+// Note: For these to work, content must come after both bars in the markup
+/* If you change these, change platform.scss as well */
+.has-header {
+ top: $bar-height;
+}
+// Force no header
+.no-header {
+ top: 0;
+}
+
+.has-subheader {
+ top: $bar-height * 2;
+}
+.has-tabs-top {
+ top: $bar-height + $tabs-height;
+}
+.has-header.has-subheader.has-tabs-top {
+ top: 2 * $bar-height + $tabs-height;
+}
+
+.has-footer {
+ bottom: $bar-height;
+}
+.has-subfooter {
+ bottom: $bar-height * 2;
+}
+
+.has-tabs,
+.bar-footer.has-tabs {
+ bottom: $tabs-height;
+}
+
+.has-footer.has-tabs {
+ bottom: $tabs-height + $bar-height;
+}
+
+// A full screen section with a solid background
+.pane {
+ @include translate3d(0,0,0);
+ z-index: $z-index-pane;
+}
+.view {
+ z-index: $z-index-view;
+}
+.pane,
+.view {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: $base-background-color;
+ overflow: hidden;
+}
+
+ion-nav-view {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #000;
+}
diff --git a/client/lib/ionic/scss/_select.scss b/client/lib/ionic/scss/_select.scss
new file mode 100644
index 0000000..78b7e43
--- /dev/null
+++ b/client/lib/ionic/scss/_select.scss
@@ -0,0 +1,141 @@
+
+/**
+ * Select
+ * --------------------------------------------------
+ */
+
+.item-select {
+ position: relative;
+
+ select {
+ @include appearance(none);
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: ($item-padding - 2) ($item-padding * 3) ($item-padding) $item-padding;
+ max-width: 65%;
+
+ border: none;
+ background: $item-default-bg;
+ color: #333;
+
+ // hack to hide default dropdown arrow in FF
+ text-indent: .01px;
+ text-overflow: '';
+
+ white-space: nowrap;
+ font-size: $font-size-base;
+
+ cursor: pointer;
+ direction: rtl; // right align the select text
+ }
+
+ select::-ms-expand {
+ // hide default dropdown arrow in IE
+ display: none;
+ }
+
+ option {
+ direction: ltr;
+ }
+
+ &:after {
+ position: absolute;
+ top: 50%;
+ right: $item-padding;
+ margin-top: -3px;
+ width: 0;
+ height: 0;
+ border-top: 5px solid;
+ border-right: 5px solid rgba(0, 0, 0, 0);
+ border-left: 5px solid rgba(0, 0, 0, 0);
+ color: #999;
+ content: "";
+ pointer-events: none;
+ }
+ &.item-light {
+ select{
+ background:$item-light-bg;
+ color:$item-light-text;
+ }
+ }
+ &.item-stable {
+ select{
+ background:$item-stable-bg;
+ color:$item-stable-text;
+ }
+ &:after, .input-label{
+ color:darken($item-stable-border,30%);
+ }
+ }
+ &.item-positive {
+ select{
+ background:$item-positive-bg;
+ color:$item-positive-text;
+ }
+ &:after, .input-label{
+ color:$item-positive-text;
+ }
+ }
+ &.item-calm {
+ select{
+ background:$item-calm-bg;
+ color:$item-calm-text;
+ }
+ &:after, .input-label{
+ color:$item-calm-text;
+ }
+ }
+ &.item-assertive {
+ select{
+ background:$item-assertive-bg;
+ color:$item-assertive-text;
+ }
+ &:after, .input-label{
+ color:$item-assertive-text;
+ }
+ }
+ &.item-balanced {
+ select{
+ background:$item-balanced-bg;
+ color:$item-balanced-text;
+ }
+ &:after, .input-label{
+ color:$item-balanced-text;
+ }
+ }
+ &.item-energized {
+ select{
+ background:$item-energized-bg;
+ color:$item-energized-text;
+ }
+ &:after, .input-label{
+ color:$item-energized-text;
+ }
+ }
+ &.item-royal {
+ select{
+ background:$item-royal-bg;
+ color:$item-royal-text;
+ }
+ &:after, .input-label{
+ color:$item-royal-text;
+ }
+ }
+ &.item-dark {
+ select{
+ background:$item-dark-bg;
+ color:$item-dark-text;
+ }
+ &:after, .input-label{
+ color:$item-dark-text;
+ }
+ }
+}
+
+select {
+ &[multiple],
+ &[size] {
+ height: auto;
+ }
+}
diff --git a/client/lib/ionic/scss/_slide-box.scss b/client/lib/ionic/scss/_slide-box.scss
new file mode 100644
index 0000000..f6dfa11
--- /dev/null
+++ b/client/lib/ionic/scss/_slide-box.scss
@@ -0,0 +1,56 @@
+
+/**
+ * Slide Box
+ * --------------------------------------------------
+ */
+
+.slider {
+ position: relative;
+ visibility: hidden;
+ // Make sure items don't scroll over ever
+ overflow: hidden;
+}
+
+.slider-slides {
+ position: relative;
+ height: 100%;
+}
+
+.slider-slide {
+ position: relative;
+ display: block;
+ float: left;
+ width: 100%;
+ height: 100%;
+ vertical-align: top;
+}
+
+.slider-slide-image {
+ > img {
+ width: 100%;
+ }
+}
+
+.slider-pager {
+ position: absolute;
+ bottom: 20px;
+ z-index: $z-index-slider-pager;
+ width: 100%;
+ height: 15px;
+ text-align: center;
+
+ .slider-pager-page {
+ display: inline-block;
+ margin: 0px 3px;
+ width: 15px;
+ color: #000;
+ text-decoration: none;
+
+ opacity: 0.3;
+
+ &.active {
+ @include transition(opacity 0.4s ease-in);
+ opacity: 1;
+ }
+ }
+}
diff --git a/client/lib/ionic/scss/_tabs.scss b/client/lib/ionic/scss/_tabs.scss
new file mode 100644
index 0000000..8fe78f4
--- /dev/null
+++ b/client/lib/ionic/scss/_tabs.scss
@@ -0,0 +1,421 @@
+/**
+ * Tabs
+ * --------------------------------------------------
+ * A navigation bar with any number of tab items supported.
+ */
+
+.tabs {
+ @include display-flex();
+ @include flex-direction(horizontal);
+ @include justify-content(center);
+ @include translate3d(0,0,0);
+
+ @include tab-style($tabs-default-bg, $tabs-default-border, $tabs-default-text);
+ @include tab-badge-style($tabs-default-text, $tabs-default-bg);
+
+ position: absolute;
+ bottom: 0;
+
+ z-index: $z-index-tabs;
+
+ width: 100%;
+ height: $tabs-height;
+
+ border-style: solid;
+ border-top-width: 1px;
+
+ background-size: 0;
+ line-height: $tabs-height;
+
+ @media (min--moz-device-pixel-ratio: 1.5),
+ (-webkit-min-device-pixel-ratio: 1.5),
+ (min-device-pixel-ratio: 1.5),
+ (min-resolution: 144dpi),
+ (min-resolution: 1.5dppx) {
+ padding-top: 2px;
+ border-top: none !important;
+ border-bottom: none;
+ background-position: top;
+ background-size: 100% 1px;
+ background-repeat: no-repeat;
+ }
+
+}
+/* Allow parent element of tabs to define color, or just the tab itself */
+.tabs-light > .tabs,
+.tabs.tabs-light {
+ @include tab-style($tabs-light-bg, $tabs-light-border, $tabs-light-text);
+ @include tab-badge-style($tabs-light-text, $tabs-light-bg);
+}
+.tabs-stable > .tabs,
+.tabs.tabs-stable {
+ @include tab-style($tabs-stable-bg, $tabs-stable-border, $tabs-stable-text);
+ @include tab-badge-style($tabs-stable-text, $tabs-stable-bg);
+}
+.tabs-positive > .tabs,
+.tabs.tabs-positive {
+ @include tab-style($tabs-positive-bg, $tabs-positive-border, $tabs-positive-text);
+ @include tab-badge-style($tabs-positive-text, $tabs-positive-bg);
+}
+.tabs-calm > .tabs,
+.tabs.tabs-calm {
+ @include tab-style($tabs-calm-bg, $tabs-calm-border, $tabs-calm-text);
+ @include tab-badge-style($tabs-calm-text, $tabs-calm-bg);
+}
+.tabs-assertive > .tabs,
+.tabs.tabs-assertive {
+ @include tab-style($tabs-assertive-bg, $tabs-assertive-border, $tabs-assertive-text);
+ @include tab-badge-style($tabs-assertive-text, $tabs-assertive-bg);
+}
+.tabs-balanced > .tabs,
+.tabs.tabs-balanced {
+ @include tab-style($tabs-balanced-bg, $tabs-balanced-border, $tabs-balanced-text);
+ @include tab-badge-style($tabs-balanced-text, $tabs-balanced-bg);
+}
+.tabs-energized > .tabs,
+.tabs.tabs-energized {
+ @include tab-style($tabs-energized-bg, $tabs-energized-border, $tabs-energized-text);
+ @include tab-badge-style($tabs-energized-text, $tabs-energized-bg);
+}
+.tabs-royal > .tabs,
+.tabs.tabs-royal {
+ @include tab-style($tabs-royal-bg, $tabs-royal-border, $tabs-royal-text);
+ @include tab-badge-style($tabs-royal-text, $tabs-royal-bg);
+}
+.tabs-dark > .tabs,
+.tabs.tabs-dark {
+ @include tab-style($tabs-dark-bg, $tabs-dark-border, $tabs-dark-text);
+ @include tab-badge-style($tabs-dark-text, $tabs-dark-bg);
+}
+
+@mixin tabs-striped($style, $color, $background) {
+ &.#{$style} {
+ .tabs{
+ background-color: $background;
+ }
+ .tab-item {
+ color: rgba($color, $tabs-striped-off-opacity);
+ opacity: 1;
+ .badge{
+ opacity:$tabs-striped-off-opacity;
+ }
+ &.tab-item-active,
+ &.active,
+ &.activated {
+ margin-top: -$tabs-striped-border-width;
+ color: $color;
+ border-style: solid;
+ border-width: $tabs-striped-border-width 0 0 0;
+ border-color: $color;
+ .badge{
+ top:$tabs-striped-border-width;
+ opacity: 1;
+ }
+ }
+ }
+ }
+ &.tabs-top{
+ .tab-item {
+ &.tab-item-active,
+ &.active,
+ &.activated {
+ .badge {
+ top: 4%;
+ }
+ }
+ }
+ }
+}
+
+@mixin tabs-background($style, $color) {
+ &.#{$style} {
+ .tabs {
+ background-color: $color;
+ }
+ }
+}
+
+@mixin tabs-color($style, $color) {
+ &.#{$style} {
+ .tab-item {
+ color: rgba($color, $tabs-striped-off-opacity);
+ opacity: 1;
+ .badge{
+ opacity:$tabs-striped-off-opacity;
+ }
+ &.tab-item-active,
+ &.active,
+ &.activated {
+ margin-top: -$tabs-striped-border-width;
+ color: $color;
+ border: 0 solid $color;
+ border-top-width: $tabs-striped-border-width;
+ .badge{
+ top:$tabs-striped-border-width;
+ opacity: 1;
+ }
+ }
+ }
+ }
+}
+
+.tabs-striped {
+ .tabs {
+ background-color: white;
+ background-image: none;
+ border: none;
+ box-shadow: 0 2px 3px rgba(0, 0, 0, 0.15);
+ padding-top: $tabs-striped-border-width;
+ }
+ .tab-item {
+ // default android tab style
+ &.tab-item-active,
+ &.active,
+ &.activated {
+ margin-top: -$tabs-striped-border-width;
+ border-style: solid;
+ border-width: $tabs-striped-border-width 0 0 0;
+ border-color: $dark;
+ }
+ }
+ @include tabs-striped('tabs-light', $light, $dark);
+ @include tabs-striped('tabs-stable', $stable, $dark);
+ @include tabs-striped('tabs-positive', $positive, $light);
+ @include tabs-striped('tabs-calm', $calm, $light);
+ @include tabs-striped('tabs-assertive', $assertive, $light);
+ @include tabs-striped('tabs-balanced', $balanced, $light);
+ @include tabs-striped('tabs-energized', $energized, $light);
+ @include tabs-striped('tabs-royal', $royal, $light);
+ @include tabs-striped('tabs-dark', $dark, $light);
+
+
+ @include tabs-background('tabs-background-light', $light);
+ @include tabs-background('tabs-background-stable', $stable);
+ @include tabs-background('tabs-background-positive', $positive);
+ @include tabs-background('tabs-background-calm', $calm);
+ @include tabs-background('tabs-background-assertive', $assertive);
+ @include tabs-background('tabs-background-balanced', $balanced);
+ @include tabs-background('tabs-background-energized',$energized);
+ @include tabs-background('tabs-background-royal', $royal);
+ @include tabs-background('tabs-background-dark', $dark);
+
+ @include tabs-color('tabs-color-light', $light);
+ @include tabs-color('tabs-color-stable', $stable);
+ @include tabs-color('tabs-color-positive', $positive);
+ @include tabs-color('tabs-color-calm', $calm);
+ @include tabs-color('tabs-color-assertive', $assertive);
+ @include tabs-color('tabs-color-balanced', $balanced);
+ @include tabs-color('tabs-color-energized',$energized);
+ @include tabs-color('tabs-color-royal', $royal);
+ @include tabs-color('tabs-color-dark', $dark);
+}
+
+.tabs-top {
+ &.tabs-striped {
+ padding-bottom:0;
+ .tab-item{
+ background: transparent;
+ // animate the top bar, leave bottom for platform consistency
+ -webkit-transition: all .1s ease;
+ -moz-transition: all .1s ease;
+ -ms-transition: all .1s ease;
+ -o-transition: all .1s ease;
+ transition: all .1s ease;
+ &.tab-item-active,
+ &.active,
+ &.activated {
+ margin-top: 0;
+ margin-bottom: -$tabs-striped-border-width;
+ border-width: 0px 0px $tabs-striped-border-width 0px !important;
+ border-style: solid;
+ }
+ .badge{
+ -webkit-transition: all .2s ease;
+ -moz-transition: all .2s ease;
+ -ms-transition: all .2s ease;
+ -o-transition: all .2s ease;
+ transition: all .2s ease;
+ }
+ }
+ }
+}
+
+/* Allow parent element to have tabs-top */
+/* If you change this, change platform.scss as well */
+.tabs-top > .tabs,
+.tabs.tabs-top {
+ top: $bar-height;
+ padding-top: 0;
+ background-position: bottom;
+ .tab-item {
+ &.tab-item-active,
+ &.active,
+ &.activated {
+ .badge {
+ top: 4%;
+ }
+ }
+ }
+}
+.tabs-top ~ .bar-header {
+ border-bottom-width: 0;
+}
+
+.tab-item {
+ @include flex(1);
+ display: block;
+ overflow: hidden;
+
+ max-width: $tab-item-max-width;
+ height: 100%;
+
+ color: inherit;
+ text-align: center;
+ text-decoration: none;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+
+ font-weight: 400;
+ font-size: $tabs-text-font-size;
+ font-family: $font-family-light-sans-serif;
+
+ opacity: 0.7;
+
+ &:hover {
+ cursor: pointer;
+ }
+ &.tab-hidden{
+ display:none;
+ }
+}
+
+.tabs-item-hide > .tabs,
+.tabs.tabs-item-hide {
+ display: none;
+}
+
+.tabs-icon-top > .tabs .tab-item,
+.tabs-icon-top.tabs .tab-item,
+.tabs-icon-bottom > .tabs .tab-item,
+.tabs-icon-bottom.tabs .tab-item {
+ font-size: $tabs-text-font-size-side-icon;
+ line-height: $tabs-text-font-size;
+}
+
+.tab-item .icon {
+ display: block;
+ margin: 0 auto;
+ height: $tabs-icon-size;
+ font-size: $tabs-icon-size;
+}
+
+.tabs-icon-left.tabs .tab-item,
+.tabs-icon-left > .tabs .tab-item,
+.tabs-icon-right.tabs .tab-item,
+.tabs-icon-right > .tabs .tab-item {
+ font-size: $tabs-text-font-size-side-icon;
+
+ .icon {
+ display: inline-block;
+ vertical-align: top;
+ margin-top: -.1em;
+
+ &:before {
+ font-size: $tabs-icon-size - 8;
+ line-height: $tabs-height;
+ }
+ }
+}
+
+.tabs-icon-left > .tabs .tab-item .icon,
+.tabs-icon-left.tabs .tab-item .icon {
+ padding-right: 3px;
+}
+
+.tabs-icon-right > .tabs .tab-item .icon,
+.tabs-icon-right.tabs .tab-item .icon {
+ padding-left: 3px;
+}
+
+.tabs-icon-only > .tabs .icon,
+.tabs-icon-only.tabs .icon {
+ line-height: inherit;
+}
+
+
+.tab-item.has-badge {
+ position: relative;
+}
+
+.tab-item .badge {
+ position: absolute;
+ top: 4%;
+ right: 33%; // fallback
+ right: calc(50% - 26px);
+ padding: $tabs-badge-padding;
+ height: auto;
+ font-size: $tabs-badge-font-size;
+ line-height: $tabs-badge-font-size + 4;
+}
+
+
+/* Navigational tab */
+
+/* Active state for tab */
+.tab-item.tab-item-active,
+.tab-item.active,
+.tab-item.activated {
+ opacity: 1;
+
+ &.tab-item-light {
+ color: $light;
+ }
+ &.tab-item-stable {
+ color: $stable;
+ }
+ &.tab-item-positive {
+ color: $positive;
+ }
+ &.tab-item-calm {
+ color: $calm;
+ }
+ &.tab-item-assertive {
+ color: $assertive;
+ }
+ &.tab-item-balanced {
+ color: $balanced;
+ }
+ &.tab-item-energized {
+ color: $energized;
+ }
+ &.tab-item-royal {
+ color: $royal;
+ }
+ &.tab-item-dark {
+ color: $dark;
+ }
+}
+
+.item.tabs {
+ @include display-flex();
+ padding: 0;
+
+ .icon:before {
+ position: relative;
+ }
+}
+
+.tab-item.disabled,
+.tab-item[disabled] {
+ opacity: .4;
+ cursor: default;
+ pointer-events: none;
+}
+
+/** Platform styles **/
+
+.tab-item.tab-item-ios {
+}
+.tab-item.tab-item-android {
+ border-top: 2px solid inherit;
+}
diff --git a/client/lib/ionic/scss/_toggle.scss b/client/lib/ionic/scss/_toggle.scss
new file mode 100644
index 0000000..1cf58ed
--- /dev/null
+++ b/client/lib/ionic/scss/_toggle.scss
@@ -0,0 +1,138 @@
+
+/**
+ * Toggle
+ * --------------------------------------------------
+ */
+
+.item-toggle {
+ pointer-events: none;
+}
+
+.toggle {
+ // set the color defaults
+ @include toggle-style($toggle-on-default-border, $toggle-on-default-bg);
+
+ position: relative;
+ display: inline-block;
+ pointer-events: auto;
+ margin: -$toggle-hit-area-expansion;
+ padding: $toggle-hit-area-expansion;
+
+ &.dragging {
+ .handle {
+ background-color: $toggle-handle-dragging-bg-color !important;
+ }
+ }
+
+ &.toggle-light {
+ @include toggle-style($toggle-on-light-border, $toggle-on-light-bg);
+ }
+ &.toggle-stable {
+ @include toggle-style($toggle-on-stable-border, $toggle-on-stable-bg);
+ }
+ &.toggle-positive {
+ @include toggle-style($toggle-on-positive-border, $toggle-on-positive-bg);
+ }
+ &.toggle-calm {
+ @include toggle-style($toggle-on-calm-border, $toggle-on-calm-bg);
+ }
+ &.toggle-assertive {
+ @include toggle-style($toggle-on-assertive-border, $toggle-on-assertive-bg);
+ }
+ &.toggle-balanced {
+ @include toggle-style($toggle-on-balanced-border, $toggle-on-balanced-bg);
+ }
+ &.toggle-energized {
+ @include toggle-style($toggle-on-energized-border, $toggle-on-energized-bg);
+ }
+ &.toggle-royal {
+ @include toggle-style($toggle-on-royal-border, $toggle-on-royal-bg);
+ }
+ &.toggle-dark {
+ @include toggle-style($toggle-on-dark-border, $toggle-on-dark-bg);
+ }
+}
+
+.toggle input {
+ // hide the actual input checkbox
+ display: none;
+}
+
+/* the track appearance when the toggle is "off" */
+.toggle .track {
+ @include transition-timing-function(ease-in-out);
+ @include transition-duration($toggle-transition-duration);
+ @include transition-property((background-color, border));
+
+ display: inline-block;
+ box-sizing: border-box;
+ width: $toggle-width;
+ height: $toggle-height;
+ border: solid $toggle-border-width $toggle-off-border-color;
+ border-radius: $toggle-border-radius;
+ background-color: $toggle-off-bg-color;
+ content: ' ';
+ cursor: pointer;
+ pointer-events: none;
+}
+
+/* Fix to avoid background color bleeding */
+/* (occured on (at least) Android 4.2, Asus MeMO Pad HD7 ME173X) */
+.platform-android4_2 .toggle .track {
+ -webkit-background-clip: padding-box;
+}
+
+/* the handle (circle) thats inside the toggle's track area */
+/* also the handle's appearance when it is "off" */
+.toggle .handle {
+ @include transition($toggle-transition-duration ease-in-out);
+ position: absolute;
+ display: block;
+ width: $toggle-handle-width;
+ height: $toggle-handle-height;
+ border-radius: $toggle-handle-radius;
+ background-color: $toggle-handle-off-bg-color;
+ top: $toggle-border-width + $toggle-hit-area-expansion;
+ left: $toggle-border-width + $toggle-hit-area-expansion;
+
+ &:before {
+ // used to create a larger (but hidden) hit area to slide the handle
+ position: absolute;
+ top: -4px;
+ left: ( ($toggle-handle-width / 2) * -1) - 8;
+ padding: ($toggle-handle-height / 2) + 5 ($toggle-handle-width + 7);
+ content: " ";
+ }
+}
+
+.toggle input:checked + .track .handle {
+ // the handle when the toggle is "on"
+ @include translate3d($toggle-width - $toggle-handle-width - ($toggle-border-width * 2), 0, 0);
+ background-color: $toggle-handle-on-bg-color;
+}
+
+.item-toggle.active {
+ box-shadow: none;
+}
+
+.item-toggle,
+.item-toggle.item-complex .item-content {
+ // make sure list item content have enough padding on right to fit the toggle
+ padding-right: ($item-padding * 3) + $toggle-width;
+}
+
+.item-toggle.item-complex {
+ padding-right: 0;
+}
+
+.item-toggle .toggle {
+ // position the toggle to the right within a list item
+ position: absolute;
+ top: $item-padding / 2;
+ right: $item-padding;
+ z-index: $z-index-item-toggle;
+}
+
+.toggle input:disabled + .track {
+ opacity: .6;
+}
diff --git a/client/lib/ionic/scss/_type.scss b/client/lib/ionic/scss/_type.scss
new file mode 100644
index 0000000..fda6ed8
--- /dev/null
+++ b/client/lib/ionic/scss/_type.scss
@@ -0,0 +1,163 @@
+
+/**
+ * Typography
+ * --------------------------------------------------
+ */
+
+
+// Body text
+// -------------------------
+
+p {
+ margin: 0 0 ($line-height-computed / 2);
+}
+
+
+// Emphasis & misc
+// -------------------------
+
+small { font-size: 85%; }
+cite { font-style: normal; }
+
+
+// Alignment
+// -------------------------
+
+.text-left { text-align: left; }
+.text-right { text-align: right; }
+.text-center { text-align: center; }
+
+
+// Headings
+// -------------------------
+
+h1, h2, h3, h4, h5, h6,
+.h1, .h2, .h3, .h4, .h5, .h6 {
+ color: $base-color;
+ font-weight: $headings-font-weight;
+ font-family: $headings-font-family;
+ line-height: $headings-line-height;
+
+ small {
+ font-weight: normal;
+ line-height: 1;
+ }
+}
+
+h1, .h1,
+h2, .h2,
+h3, .h3 {
+ margin-top: $line-height-computed;
+ margin-bottom: ($line-height-computed / 2);
+
+ &:first-child {
+ margin-top: 0;
+ }
+
+ + h1, + .h1,
+ + h2, + .h2,
+ + h3, + .h3 {
+ margin-top: ($line-height-computed / 2);
+ }
+}
+
+h4, .h4,
+h5, .h5,
+h6, .h6 {
+ margin-top: ($line-height-computed / 2);
+ margin-bottom: ($line-height-computed / 2);
+}
+
+h1, .h1 { font-size: floor($font-size-base * 2.60); } // ~36px
+h2, .h2 { font-size: floor($font-size-base * 2.15); } // ~30px
+h3, .h3 { font-size: ceil($font-size-base * 1.70); } // ~24px
+h4, .h4 { font-size: ceil($font-size-base * 1.25); } // ~18px
+h5, .h5 { font-size: $font-size-base; }
+h6, .h6 { font-size: ceil($font-size-base * 0.85); } // ~12px
+
+h1 small, .h1 small { font-size: ceil($font-size-base * 1.70); } // ~24px
+h2 small, .h2 small { font-size: ceil($font-size-base * 1.25); } // ~18px
+h3 small, .h3 small,
+h4 small, .h4 small { font-size: $font-size-base; }
+
+
+// Description Lists
+// -------------------------
+
+dl {
+ margin-bottom: $line-height-computed;
+}
+dt,
+dd {
+ line-height: $line-height-base;
+}
+dt {
+ font-weight: bold;
+}
+
+
+// Blockquotes
+// -------------------------
+
+blockquote {
+ margin: 0 0 $line-height-computed;
+ padding: ($line-height-computed / 2) $line-height-computed;
+ border-left: 5px solid gray;
+
+ p {
+ font-weight: 300;
+ font-size: ($font-size-base * 1.25);
+ line-height: 1.25;
+ }
+
+ p:last-child {
+ margin-bottom: 0;
+ }
+
+ small {
+ display: block;
+ line-height: $line-height-base;
+ &:before {
+ content: '\2014 \00A0';// EM DASH, NBSP;
+ }
+ }
+}
+
+
+// Quotes
+// -------------------------
+
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+ content: "";
+}
+
+
+// Addresses
+// -------------------------
+
+address {
+ display: block;
+ margin-bottom: $line-height-computed;
+ font-style: normal;
+ line-height: $line-height-base;
+}
+
+
+// Links
+// -------------------------
+
+a.subdued {
+ padding-right: 10px;
+ color: #888;
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: none;
+ }
+ &:last-child {
+ padding-right: 0;
+ }
+}
diff --git a/client/lib/ionic/scss/_util.scss b/client/lib/ionic/scss/_util.scss
new file mode 100644
index 0000000..aa01c50
--- /dev/null
+++ b/client/lib/ionic/scss/_util.scss
@@ -0,0 +1,246 @@
+
+/**
+ * Utility Classes
+ * --------------------------------------------------
+ */
+
+.hide {
+ display: none;
+}
+.opacity-hide {
+ opacity: 0;
+}
+.grade-b .opacity-hide,
+.grade-c .opacity-hide {
+ opacity: 1;
+ display: none;
+}
+.show {
+ display: block;
+}
+.opacity-show {
+ opacity: 1;
+}
+.invisible {
+ visibility: hidden;
+}
+
+.keyboard-open .hide-on-keyboard-open {
+ display: none;
+}
+
+.keyboard-open .tabs.hide-on-keyboard-open + .pane .has-tabs,
+.keyboard-open .bar-footer.hide-on-keyboard-open + .pane .has-footer {
+ bottom: 0;
+}
+
+.inline {
+ display: inline-block;
+}
+
+.disable-pointer-events {
+ pointer-events: none;
+}
+
+.enable-pointer-events {
+ pointer-events: auto;
+}
+
+.disable-user-behavior {
+ // used to prevent the browser from doing its native behavior. this doesnt
+ // prevent the scrolling, but cancels the contextmenu, tap highlighting, etc
+
+ @include user-select(none);
+ @include touch-callout(none);
+ @include tap-highlight-transparent();
+
+ -webkit-user-drag: none;
+
+ -ms-touch-action: none;
+ -ms-content-zooming: none;
+}
+
+// Fill the screen to block clicks (a better pointer-events: none) for the body
+// to avoid full-page reflows and paints which can cause flickers
+.click-block {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: $z-index-click-block;
+ width: 100%;
+ height: 100%;
+ background: transparent;
+}
+
+.no-resize {
+ resize: none;
+}
+
+.block {
+ display: block;
+ clear: both;
+ &:after {
+ display: block;
+ visibility: hidden;
+ clear: both;
+ height: 0;
+ content: ".";
+ }
+}
+
+.full-image {
+ width: 100%;
+}
+
+.clearfix {
+ *zoom: 1;
+ &:before,
+ &:after {
+ display: table;
+ content: "";
+ // Fixes Opera/contenteditable bug:
+ // http://nicolasgallagher.com/micro-clearfix-hack/#comment-36952
+ line-height: 0;
+ }
+ &:after {
+ clear: both;
+ }
+}
+
+/**
+ * Content Padding
+ * --------------------------------------------------
+ */
+
+.padding {
+ padding: $content-padding;
+}
+
+.padding-top,
+.padding-vertical {
+ padding-top: $content-padding;
+}
+
+.padding-right,
+.padding-horizontal {
+ padding-right: $content-padding;
+}
+
+.padding-bottom,
+.padding-vertical {
+ padding-bottom: $content-padding;
+}
+
+.padding-left,
+.padding-horizontal {
+ padding-left: $content-padding;
+}
+
+
+/**
+ * Rounded
+ * --------------------------------------------------
+ */
+
+.rounded {
+ border-radius: $border-radius-base;
+}
+
+
+/**
+ * Utility Colors
+ * --------------------------------------------------
+ * Utility colors are added to help set a naming convention. You'll
+ * notice we purposely do not use words like "red" or "blue", but
+ * instead have colors which represent an emotion or generic theme.
+ */
+
+.light, a.light {
+ color: $light;
+}
+.light-bg {
+ background-color: $light;
+}
+.light-border {
+ border-color: $button-light-border;
+}
+
+.stable, a.stable {
+ color: $stable;
+}
+.stable-bg {
+ background-color: $stable;
+}
+.stable-border {
+ border-color: $button-stable-border;
+}
+
+.positive, a.positive {
+ color: $positive;
+}
+.positive-bg {
+ background-color: $positive;
+}
+.positive-border {
+ border-color: $button-positive-border;
+}
+
+.calm, a.calm {
+ color: $calm;
+}
+.calm-bg {
+ background-color: $calm;
+}
+.calm-border {
+ border-color: $button-calm-border;
+}
+
+.assertive, a.assertive {
+ color: $assertive;
+}
+.assertive-bg {
+ background-color: $assertive;
+}
+.assertive-border {
+ border-color: $button-assertive-border;
+}
+
+.balanced, a.balanced {
+ color: $balanced;
+}
+.balanced-bg {
+ background-color: $balanced;
+}
+.balanced-border {
+ border-color: $button-balanced-border;
+}
+
+.energized, a.energized {
+ color: $energized;
+}
+.energized-bg {
+ background-color: $energized;
+}
+.energized-border {
+ border-color: $button-energized-border;
+}
+
+.royal, a.royal {
+ color: $royal;
+}
+.royal-bg {
+ background-color: $royal;
+}
+.royal-border {
+ border-color: $button-royal-border;
+}
+
+.dark, a.dark {
+ color: $dark;
+}
+.dark-bg {
+ background-color: $dark;
+}
+.dark-border {
+ border-color: $button-dark-border;
+}
diff --git a/client/lib/ionic/scss/_variables.scss b/client/lib/ionic/scss/_variables.scss
new file mode 100644
index 0000000..84e15b4
--- /dev/null
+++ b/client/lib/ionic/scss/_variables.scss
@@ -0,0 +1,708 @@
+
+// Colors
+// -------------------------------
+
+$light: #fff !default;
+$stable: #f8f8f8 !default;
+$positive: #4a87ee !default;
+$calm: #43cee6 !default;
+$balanced: #66cc33 !default;
+$energized: #f0b840 !default;
+$assertive: #ef4e3a !default;
+$royal: #8a6de9 !default;
+$dark: #444 !default;
+
+
+// Base
+// -------------------------------
+
+$font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif !default;
+$font-family-light-sans-serif: "Helvetica Neue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif !default;
+$font-family-serif: Georgia, "Times New Roman", Times, serif !default;
+$font-family-monospace: Monaco, Menlo, Consolas, "Courier New", monospace !default;
+
+$font-family-base: $font-family-sans-serif !default;
+$font-size-base: 14px !default;
+$font-size-large: 18px !default;
+$font-size-small: 11px !default;
+
+$line-height-base: 1.428571429 !default; // 20/14
+$line-height-computed: floor($font-size-base * $line-height-base) !default; // ~20px
+$line-height-large: 1.33 !default;
+$line-height-small: 1.5 !default;
+
+$headings-font-family: $font-family-base !default;
+$headings-font-weight: 500 !default;
+$headings-line-height: 1.2 !default;
+
+$base-background-color: #fff !default;
+$base-color: #000 !default;
+
+$link-color: $positive !default;
+$link-hover-color: darken($link-color, 15%) !default;
+
+$content-padding: 10px !default;
+
+$padding-base-vertical: 6px !default;
+$padding-base-horizontal: 12px !default;
+
+$padding-large-vertical: 10px !default;
+$padding-large-horizontal: 16px !default;
+
+$padding-small-vertical: 5px !default;
+$padding-small-horizontal: 10px !default;
+
+$border-radius-base: 4px !default;
+$border-radius-large: 6px !default;
+$border-radius-small: 3px !default;
+
+
+// Content
+// -------------------------------
+
+$scroll-refresh-icon-color: #666666 !default;
+
+
+// Buttons
+// -------------------------------
+
+$button-color: #222 !default;
+$button-block-margin: 10px !default;
+$button-clear-padding: 6px !default;
+$button-border-radius: 2px !default;
+$button-border-width: 1px !default;
+
+$button-font-size: 16px !default;
+$button-height: 42px !default;
+$button-padding: 12px !default;
+$button-icon-size: 24px !default;
+
+$button-large-font-size: 20px !default;
+$button-large-height: 54px !default;
+$button-large-padding: 16px !default;
+$button-large-icon-size: 32px !default;
+
+$button-small-font-size: 12px !default;
+$button-small-height: 28px !default;
+$button-small-padding: 4px !default;
+$button-small-icon-size: 16px !default;
+
+$button-bar-button-font-size: 13px !default;
+$button-bar-button-height: 32px !default;
+$button-bar-button-padding: 8px !default;
+$button-bar-button-icon-size: 20px !default;
+
+$button-light-bg: $light !default;
+$button-light-text: #444 !default;
+$button-light-border: #ddd !default;
+$button-light-active-bg: #fafafa !default;
+$button-light-active-border: #ccc !default;
+
+$button-stable-bg: $stable !default;
+$button-stable-text: #444 !default;
+$button-stable-border: #b2b2b2 !default;
+$button-stable-active-bg: #e5e5e5 !default;
+$button-stable-active-border: #a2a2a2 !default;
+
+$button-positive-bg: $positive !default;
+$button-positive-text: #fff !default;
+$button-positive-border: darken($positive, 15%) !default;
+$button-positive-active-bg: darken($positive, 15%) !default;
+$button-positive-active-border: darken($positive, 15%) !default;
+
+$button-calm-bg: $calm !default;
+$button-calm-text: #fff !default;
+$button-calm-border: darken($calm, 15%) !default;
+$button-calm-active-bg: darken($calm, 15%) !default;
+$button-calm-active-border: darken($calm, 15%) !default;
+
+$button-assertive-bg: $assertive !default;
+$button-assertive-text: #fff !default;
+$button-assertive-border: darken($assertive, 15%) !default;
+$button-assertive-active-bg: darken($assertive, 15%) !default;
+$button-assertive-active-border: darken($assertive, 15%) !default;
+
+$button-balanced-bg: $balanced !default;
+$button-balanced-text: #fff !default;
+$button-balanced-border: darken($balanced, 15%) !default;
+$button-balanced-active-bg: darken($balanced, 15%) !default;
+$button-balanced-active-border: darken($balanced, 15%) !default;
+
+$button-energized-bg: $energized !default;
+$button-energized-text: #fff !default;
+$button-energized-border: darken($energized, 15%) !default;
+$button-energized-active-bg: darken($energized, 15%) !default;
+$button-energized-active-border: darken($energized, 15%) !default;
+
+$button-royal-bg: $royal !default;
+$button-royal-text: #fff !default;
+$button-royal-border: darken($royal, 15%) !default;
+$button-royal-active-bg: darken($royal, 15%) !default;
+$button-royal-active-border: darken($royal, 15%) !default;
+
+$button-dark-bg: $dark !default;
+$button-dark-text: #fff !default;
+$button-dark-border: #111 !default;
+$button-dark-active-bg: #262626 !default;
+$button-dark-active-border: #000 !default;
+
+$button-default-bg: $button-stable-bg !default;
+$button-default-text: $button-stable-text !default;
+$button-default-border: $button-stable-border !default;
+$button-default-active-bg: $button-stable-active-bg !default;
+$button-default-active-border: $button-stable-active-border !default;
+
+
+// Bars
+// -------------------------------
+
+$bar-height: 44px !default;
+$bar-title-font-size: 17px !default;
+$bar-padding-portrait: 5px !default;
+$bar-padding-landscape: 5px !default;
+$bar-transparency: 1 !default;
+
+$bar-light-bg: rgba($button-light-bg, $bar-transparency) !default;
+$bar-light-text: $button-light-text !default;
+$bar-light-border: $button-light-border !default;
+$bar-light-active-bg: $button-light-active-bg !default;
+$bar-light-active-border: $button-light-active-border !default;
+
+$bar-stable-bg: rgba($button-stable-bg, $bar-transparency) !default;
+$bar-stable-text: $button-stable-text !default;
+$bar-stable-border: $button-stable-border !default;
+$bar-stable-active-bg: $button-stable-active-bg !default;
+$bar-stable-active-border: $button-stable-active-border !default;
+
+$bar-positive-bg: rgba($button-positive-bg, $bar-transparency) !default;
+$bar-positive-text: $button-positive-text !default;
+$bar-positive-border: $button-positive-border !default;
+$bar-positive-active-bg: $button-positive-active-bg !default;
+$bar-positive-active-border: $button-positive-active-border !default;
+
+$bar-calm-bg: rgba($button-calm-bg, $bar-transparency) !default;
+$bar-calm-text: $button-calm-text !default;
+$bar-calm-border: $button-calm-border !default;
+$bar-calm-active-bg: $button-calm-active-bg !default;
+$bar-calm-active-border: $button-calm-active-border !default;
+
+$bar-assertive-bg: rgba($button-assertive-bg, $bar-transparency) !default;
+$bar-assertive-text: $button-assertive-text !default;
+$bar-assertive-border: $button-assertive-border !default;
+$bar-assertive-active-bg: $button-assertive-active-bg !default;
+$bar-assertive-active-border: $button-assertive-active-border !default;
+
+$bar-balanced-bg: rgba($button-balanced-bg, $bar-transparency) !default;
+$bar-balanced-text: $button-balanced-text !default;
+$bar-balanced-border: $button-balanced-border !default;
+$bar-balanced-active-bg: $button-balanced-active-bg !default;
+$bar-balanced-active-border: $button-balanced-active-border !default;
+
+$bar-energized-bg: rgba($button-energized-bg, $bar-transparency) !default;
+$bar-energized-text: $button-energized-text !default;
+$bar-energized-border: $button-energized-border !default;
+$bar-energized-active-bg: $button-energized-active-bg !default;
+$bar-energized-active-border: $button-energized-active-border !default;
+
+$bar-royal-bg: rgba($button-royal-bg, $bar-transparency) !default;
+$bar-royal-text: $button-royal-text !default;
+$bar-royal-border: $button-royal-border !default;
+$bar-royal-active-bg: $button-royal-active-bg !default;
+$bar-royal-active-border: $button-royal-active-border !default;
+
+$bar-dark-bg: rgba($button-dark-bg, $bar-transparency) !default;
+$bar-dark-text: $button-dark-text !default;
+$bar-dark-border: $button-dark-border !default;
+$bar-dark-active-bg: $button-dark-active-bg !default;
+$bar-dark-active-border: $button-dark-active-border !default;
+
+$bar-default-bg: $bar-light-bg !default;
+$bar-default-text: $bar-light-text !default;
+$bar-default-border: $bar-light-border !default;
+$bar-default-active-bg: $bar-light-active-bg !default;
+$bar-default-active-border: $bar-light-active-border !default;
+
+
+// Tabs
+// -------------------------------
+
+$tabs-height: 49px !default;
+$tabs-text-font-size: 14px !default;
+$tabs-text-font-size-side-icon: 12px !default;
+$tabs-icon-size: 32px !default;
+$tabs-badge-padding: 1px 6px !default;
+$tabs-badge-font-size: 12px !default;
+$tabs-text-font-size: 14px !default;
+
+$tabs-light-bg: $button-light-bg !default;
+$tabs-light-border: $button-light-border !default;
+$tabs-light-text: $button-light-text !default;
+
+$tabs-stable-bg: $button-stable-bg !default;
+$tabs-stable-border: $button-stable-border !default;
+$tabs-stable-text: $button-stable-text !default;
+
+$tabs-positive-bg: $button-positive-bg !default;
+$tabs-positive-border: $button-positive-border !default;
+$tabs-positive-text: $button-positive-text !default;
+
+$tabs-calm-bg: $button-calm-bg !default;
+$tabs-calm-border: $button-calm-border !default;
+$tabs-calm-text: $button-calm-text !default;
+
+$tabs-assertive-bg: $button-assertive-bg !default;
+$tabs-assertive-border: $button-assertive-border !default;
+$tabs-assertive-text: $button-assertive-text !default;
+
+$tabs-balanced-bg: $button-balanced-bg !default;
+$tabs-balanced-border: $button-balanced-border !default;
+$tabs-balanced-text: $button-balanced-text !default;
+
+$tabs-energized-bg: $button-energized-bg !default;
+$tabs-energized-border: $button-energized-border !default;
+$tabs-energized-text: $button-energized-text !default;
+
+$tabs-royal-bg: $button-royal-bg !default;
+$tabs-royal-border: $button-royal-border !default;
+$tabs-royal-text: $button-royal-text !default;
+
+$tabs-dark-bg: $button-dark-bg !default;
+$tabs-dark-border: $button-dark-border !default;
+$tabs-dark-text: $button-dark-text !default;
+
+$tabs-default-bg: $tabs-stable-bg !default;
+$tabs-default-border: $tabs-stable-border !default;
+$tabs-default-text: $tabs-stable-text !default;
+
+$tab-item-max-width: 150px !default;
+
+$tabs-striped-off-color: #000;
+$tabs-striped-off-opacity: 0.4;
+$tabs-striped-border-width: 2px;
+
+
+// Items
+// -------------------------------
+
+$item-font-size: 16px !default;
+$item-border-width: 1px !default;
+$item-padding: 16px !default;
+
+$item-button-font-size: 18px !default;
+$item-button-line-height: 32px !default;
+$item-icon-font-size: 32px !default;
+$item-icon-fill-font-size: 28px !default;
+
+$item-icon-accessory-color: #ccc !default;
+$item-icon-accessory-font-size: 16px !default;
+
+$item-avatar-width: 40px !default;
+$item-avatar-height: 40px !default;
+$item-avatar-border-radius: 4px !default;
+
+$item-thumbnail-width: 80px !default;
+$item-thumbnail-height: 80px !default;
+$item-thumbnail-margin: 10px !default;
+
+$item-divider-bg: #f5f5f5 !default;
+$item-divider-color: #222 !default;
+$item-divider-padding: 5px 15px !default;
+
+$item-light-bg: $button-light-bg !default;
+$item-light-border: $button-light-border !default;
+$item-light-text: $button-light-text !default;
+$item-light-active-bg: $button-light-active-bg !default;
+$item-light-active-border: $button-light-active-border !default;
+
+$item-stable-bg: $button-stable-bg !default;
+$item-stable-border: $button-stable-border !default;
+$item-stable-text: $button-stable-text !default;
+$item-stable-active-bg: $button-stable-active-bg !default;
+$item-stable-active-border: $button-stable-active-border !default;
+
+$item-positive-bg: $button-positive-bg !default;
+$item-positive-border: $button-positive-border !default;
+$item-positive-text: $button-positive-text !default;
+$item-positive-active-bg: $button-positive-active-bg !default;
+$item-positive-active-border: $button-positive-active-border !default;
+
+$item-calm-bg: $button-calm-bg !default;
+$item-calm-border: $button-calm-border !default;
+$item-calm-text: $button-calm-text !default;
+$item-calm-active-bg: $button-calm-active-bg !default;
+$item-calm-active-border: $button-calm-active-border !default;
+
+$item-assertive-bg: $button-assertive-bg !default;
+$item-assertive-border: $button-assertive-border !default;
+$item-assertive-text: $button-assertive-text !default;
+$item-assertive-active-bg: $button-assertive-active-bg !default;
+$item-assertive-active-border: $button-assertive-active-border !default;
+
+$item-balanced-bg: $button-balanced-bg !default;
+$item-balanced-border: $button-balanced-border !default;
+$item-balanced-text: $button-balanced-text !default;
+$item-balanced-active-bg: $button-balanced-active-bg !default;
+$item-balanced-active-border: $button-balanced-active-border !default;
+
+$item-energized-bg: $button-energized-bg !default;
+$item-energized-border: $button-energized-border !default;
+$item-energized-text: $button-energized-text !default;
+$item-energized-active-bg: $button-energized-active-bg !default;
+$item-energized-active-border: $button-energized-active-border !default;
+
+$item-royal-bg: $button-royal-bg !default;
+$item-royal-border: $button-royal-border !default;
+$item-royal-text: $button-royal-text !default;
+$item-royal-active-bg: $button-royal-active-bg !default;
+$item-royal-active-border: $button-royal-active-border !default;
+
+$item-dark-bg: $button-dark-bg !default;
+$item-dark-border: $button-dark-border !default;
+$item-dark-text: $button-dark-text !default;
+$item-dark-active-bg: $button-dark-active-bg !default;
+$item-dark-active-border: $button-dark-active-border !default;
+
+$item-default-bg: $item-light-bg !default;
+$item-default-border: $item-light-border !default;
+$item-default-text: $item-light-text !default;
+$item-default-active-bg: #D9D9D9 !default;
+$item-default-active-border: $item-light-active-border !default;
+
+
+// Item Editing
+// -------------------------------
+
+$item-edit-transition-duration: 250ms !default;
+$item-edit-transition-function: ease-in-out !default;
+
+$item-left-edit-left: 8px !default; // item's left side edit's "left" property
+
+$item-right-edit-open-width: 50px !default;
+$item-left-edit-open-width: 50px !default;
+
+$item-delete-icon-size: 24px !default;
+$item-delete-icon-color: $assertive !default;
+
+$item-reorder-icon-size: 32px !default;
+$item-reorder-icon-color: $dark !default;
+
+
+// Lists
+// -------------------------------
+
+$list-header-bg: transparent !default;
+$list-header-color: #222 !default;
+$list-header-padding: 5px 15px !default;
+$list-header-margin-top: 20px !default;
+
+
+// Cards
+// -------------------------------
+
+$card-header-bg: #F5F5F5 !default;
+$card-body-bg: #fff !default;
+$card-footer-bg: #F5F5F5 !default;
+
+$card-padding: 10px !default;
+$card-border-width: 1px !default;
+
+$card-border-color: #ccc !default;
+$card-border-radius: 2px !default;
+
+
+// Forms
+// -------------------------------
+
+$input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
+$input-height-large: (floor($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default;
+$input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default;
+
+$input-bg: $light !default;
+$input-bg-disabled: $stable !default;
+
+$input-color: #111 !default;
+$input-border: $item-default-border !default;
+$input-border-width: $item-border-width !default;
+$input-label-color: $dark !default;
+$input-color-placeholder: lighten($dark, 40%) !default;
+
+
+// Progress
+// -------------------------------
+
+$progress-width: 100% !default;
+$progress-margin: 15px auto !default;
+
+
+// Toggle
+// -------------------------------
+
+$toggle-width: 54px !default;
+$toggle-height: 32px !default;
+$toggle-border-width: 2px !default;
+$toggle-border-radius: 20px !default;
+
+$toggle-handle-width: $toggle-height - ($toggle-border-width * 2) !default;
+$toggle-handle-height: $toggle-handle-width !default;
+$toggle-handle-radius: $toggle-handle-width !default;
+$toggle-handle-dragging-bg-color: darken(#fff, 5%) !default;
+
+$toggle-off-bg-color: #E5E5E5 !default;
+$toggle-off-border-color: #E5E5E5 !default;
+
+$toggle-on-light-bg: $button-light-border !default;
+$toggle-on-light-border: $toggle-on-light-bg !default;
+$toggle-on-stable-bg: $button-stable-border !default;
+$toggle-on-stable-border: $toggle-on-stable-bg !default;
+$toggle-on-positive-bg: $positive !default;
+$toggle-on-positive-border: $toggle-on-positive-bg !default;
+$toggle-on-calm-bg: $calm !default;
+$toggle-on-calm-border: $toggle-on-calm-bg !default;
+$toggle-on-assertive-bg: $assertive !default;
+$toggle-on-assertive-border: $toggle-on-assertive-bg !default;
+$toggle-on-balanced-bg: $balanced !default;
+$toggle-on-balanced-border: $toggle-on-balanced-bg !default;
+$toggle-on-energized-bg: $energized !default;
+$toggle-on-energized-border: $toggle-on-energized-bg !default;
+$toggle-on-royal-bg: $royal !default;
+$toggle-on-royal-border: $toggle-on-royal-bg !default;
+$toggle-on-dark-bg: $dark !default;
+$toggle-on-dark-border: $toggle-on-dark-bg !default;
+$toggle-on-default-bg: $positive !default;
+$toggle-on-default-border: $toggle-on-default-bg !default;
+
+$toggle-handle-off-bg-color: $light !default;
+$toggle-handle-on-bg-color: $toggle-handle-off-bg-color !default;
+
+$toggle-transition-duration: .2s !default;
+
+$toggle-hit-area-expansion: 5px;
+
+
+// Checkbox
+// -------------------------------
+
+$checkbox-width: 28px !default;
+$checkbox-height: 28px !default;
+$checkbox-border-radius: $checkbox-width !default;
+$checkbox-border-width: 1px !default;
+
+$checkbox-off-bg-color: #fff !default;
+$checkbox-off-border-light: $button-light-border !default;
+$checkbox-on-bg-light: $button-light-border !default;
+$checkbox-off-border-stable: $button-stable-border !default;
+$checkbox-on-bg-stable: $button-stable-border !default;
+$checkbox-off-border-positive: $positive !default;
+$checkbox-on-bg-positive: $positive !default;
+$checkbox-off-border-calm: $calm !default;
+$checkbox-on-bg-calm: $calm !default;
+$checkbox-off-border-assertive: $assertive !default;
+$checkbox-on-bg-assertive: $assertive !default;
+$checkbox-off-border-balanced: $balanced !default;
+$checkbox-on-bg-balanced: $balanced !default;
+$checkbox-off-border-energized: $energized !default;
+$checkbox-on-bg-energized: $energized !default;
+$checkbox-off-border-royal: $royal !default;
+$checkbox-on-bg-royal: $royal !default;
+$checkbox-off-border-dark: $dark !default;
+$checkbox-on-bg-dark: $dark !default;
+$checkbox-off-border-default: $positive !default;
+$checkbox-on-bg-default: $positive !default;
+
+$checkbox-check-width: 3px !default;
+$checkbox-check-color: #fff !default;
+
+
+// Range
+// -------------------------------
+
+$range-track-height: 4px !default;
+$range-slider-width: 20px !default;
+$range-slider-height: 20px !default;
+$range-slider-border-radius: 10px !default;
+$range-icon-size: 24px !default;
+
+$range-light-track-bg: $button-light-border !default;
+$range-stable-track-bg: $button-stable-border !default;
+$range-positive-track-bg: $button-positive-bg !default;
+$range-calm-track-bg: $button-calm-bg !default;
+$range-balanced-track-bg: $button-balanced-bg !default;
+$range-assertive-track-bg: $button-assertive-bg !default;
+$range-energized-track-bg: $button-energized-bg !default;
+$range-royal-track-bg: $button-royal-bg !default;
+$range-dark-track-bg: $button-dark-bg !default;
+$range-default-track-bg: #ccc !default;
+
+
+// Menus
+// -------------------------------
+
+$menu-bg: #fff !default;
+$menu-width: 275px !default;
+$menu-animation-speed: 200ms !default;
+
+$menu-side-shadow: -1px 0px 2px rgba(0, 0, 0, 0.2), 1px 0px 2px rgba(0,0,0,0.2) !default;
+
+
+// Modals
+// -------------------------------
+
+$modal-bg-color: #fff !default;
+$modal-backdrop-bg-active: rgba(0,0,0,0.5) !default;
+$modal-backdrop-bg-inactive: rgba(0,0,0,0) !default;
+
+$modal-inset-mode-break-point: 680px !default; // @media min-width
+$modal-inset-mode-top: 20% !default;
+$modal-inset-mode-right: 20% !default;
+$modal-inset-mode-bottom: 20% !default;
+$modal-inset-mode-left: 20% !default;
+$modal-inset-mode-min-height: 240px !default;
+
+
+// Popovers
+// -------------------------------
+
+$popover-bg-color: $light !default;
+$popover-backdrop-bg-active: rgba(0,0,0,0.1) !default;
+$popover-backdrop-bg-inactive: rgba(0,0,0,0) !default;
+$popover-width: 220px !default;
+$popover-height: 280px !default;
+$popover-large-break-point: 680px !default;
+$popover-large-width: 360px !default;
+
+$popover-box-shadow: 0 1px 3px rgba(0,0,0,0.4) !default;
+$popover-border-radius: 2px !default;
+
+$popover-box-shadow-ios: 0 0 40px rgba(0,0,0,0.08) !default;
+$popover-border-radius-ios: 10px !default;
+
+$popover-bg-color-android: #fafafa !default;
+$popover-box-shadow-android: 0 2px 6px rgba(0,0,0,0.35) !default;
+
+
+// Grids
+// -------------------------------
+
+$grid-padding-width: 10px !default;
+$grid-responsive-sm-break: 567px !default; // smaller than landscape phone
+$grid-responsive-md-break: 767px !default; // smaller than portrait tablet
+$grid-responsive-lg-break: 1023px !default; // smaller than landscape tablet
+
+
+// Action Sheets
+// -------------------------------
+
+$sheet-bg-color: rgba(255, 255, 255, 0.6) !default;
+$sheet-opacity: 0.95 !default;
+
+$sheet-border-radius: 3px 3px 3px 3px !default;
+$sheet-border-radius-top: 3px 3px 0px 0px !default;
+$sheet-border-radius-bottom: 0px 0px 3px 3px !default;
+
+
+// Popups
+// -------------------------------
+
+$popup-width: 250px !default;
+$popup-enter-animation: superScaleIn !default;
+$popup-enter-animation-duration: 0.2s !default;
+$popup-leave-animation-duration: 0.1s !default;
+
+$popup-border-radius: 0px !default;
+$popup-background-color: rgba(255,255,255,0.9) !default;
+
+$popup-button-border-radius: 2px !default;
+$popup-button-line-height: 20px !default;
+$popup-button-min-height: 45px !default;
+
+
+// Loading
+// -------------------------------
+
+$loading-text-color: #fff !default;
+$loading-bg-color: rgba(0,0,0,0.7) !default;
+$loading-padding: 20px !default;
+$loading-border-radius: 5px !default;
+$loading-font-size: 15px !default;
+
+$loading-backdrop-fadein-duration:0.3s !default;
+$loading-backdrop-bg-color: rgba(0,0,0,0.7) !default;
+
+
+// Badges
+// -------------------------------
+
+$badge-font-size: 14px !default;
+$badge-line-height: 16px !default;
+$badge-font-weight: bold !default;
+$badge-border-radius: 10px !default;
+
+$badge-light-bg: $button-light-bg !default;
+$badge-light-text: $button-light-text !default;
+
+$badge-stable-bg: $button-stable-bg !default;
+$badge-stable-text: $button-stable-text !default;
+
+$badge-positive-bg: $button-positive-bg !default;
+$badge-positive-text: $button-positive-text !default;
+
+$badge-calm-bg: $button-calm-bg !default;
+$badge-calm-text: $button-calm-text !default;
+
+$badge-balanced-bg: $button-balanced-bg !default;
+$badge-balanced-text: $button-balanced-text !default;
+
+$badge-assertive-bg: $button-assertive-bg !default;
+$badge-assertive-text: $button-assertive-text !default;
+
+$badge-energized-bg: $button-energized-bg !default;
+$badge-energized-text: $button-energized-text !default;
+
+$badge-royal-bg: $button-royal-bg !default;
+$badge-royal-text: $button-royal-text !default;
+
+$badge-dark-bg: $button-dark-bg !default;
+$badge-dark-text: $button-dark-text !default;
+
+$badge-default-bg: transparent !default;
+$badge-default-text: #AAAAAA !default;
+
+
+// Z-Indexes
+// -------------------------------
+
+$z-index-bar-title: 0 !default;
+$z-index-item-drag: 0 !default;
+$z-index-item-edit: 0 !default;
+$z-index-menu: 0 !default;
+$z-index-badge: 1 !default;
+$z-index-bar-button: 1 !default;
+$z-index-item-options: 1 !default;
+$z-index-pane: 1 !default;
+$z-index-slider-pager: 1 !default;
+$z-index-view: 1 !default;
+$z-index-item: 2 !default;
+$z-index-item-checkbox: 3 !default;
+$z-index-item-radio: 3 !default;
+$z-index-item-reorder: 3 !default;
+$z-index-item-toggle: 3 !default;
+$z-index-tabs: 5 !default;
+$z-index-item-reordering: 9 !default;
+$z-index-bar: 10 !default;
+$z-index-menu-scroll-content: 10 !default;
+$z-index-modal: 10 !default;
+$z-index-popover: 10 !default;
+$z-index-action-sheet: 11 !default;
+$z-index-backdrop: 11 !default;
+$z-index-menu-bar-header: 11 !default;
+$z-index-scroll-content-false: 11 !default;
+$z-index-popup: 12 !default;
+$z-index-loading: 13 !default;
+$z-index-scroll-bar: 9999 !default;
+$z-index-click-block: 99999 !default;
+
+
+// Platform
+// -------------------------------
+
+$ios-statusbar-height: 20px !default;
diff --git a/client/lib/ionic/scss/ionic.scss b/client/lib/ionic/scss/ionic.scss
new file mode 100644
index 0000000..a659f0c
--- /dev/null
+++ b/client/lib/ionic/scss/ionic.scss
@@ -0,0 +1,48 @@
+@charset "UTF-8";
+
+@import
+ // Ionicons
+ "ionicons/ionicons.scss",
+
+ // Variables
+ "mixins",
+ "variables",
+
+ // Base
+ "reset",
+ "scaffolding",
+ "type",
+
+ // Components
+ "action-sheet",
+ "backdrop",
+ "bar",
+ "tabs",
+ "menu",
+ "modal",
+ "popover",
+ "popup",
+ "loading",
+ "items",
+ "list",
+ "badge",
+ "slide-box",
+
+ // Forms
+ "form",
+ "checkbox",
+ "toggle",
+ "radio",
+ "range",
+ "select",
+ "progress",
+
+ // Buttons
+ "button",
+ "button-bar",
+
+ // Util
+ "animations",
+ "grid",
+ "util",
+ "platform";
diff --git a/client/lib/ionic/scss/ionicons/_ionicons-animation.scss b/client/lib/ionic/scss/ionicons/_ionicons-animation.scss
new file mode 100644
index 0000000..f338d58
--- /dev/null
+++ b/client/lib/ionic/scss/ionicons/_ionicons-animation.scss
@@ -0,0 +1,76 @@
+// Animation Icons
+// --------------------------
+
+.#{$ionicons-prefix}spin {
+ -webkit-animation: spin 1s infinite linear;
+ -moz-animation: spin 1s infinite linear;
+ -o-animation: spin 1s infinite linear;
+ animation: spin 1s infinite linear;
+}
+
+@-moz-keyframes spin {
+ 0% { -moz-transform: rotate(0deg); }
+ 100% { -moz-transform: rotate(359deg); }
+}
+@-webkit-keyframes spin {
+ 0% { -webkit-transform: rotate(0deg); }
+ 100% { -webkit-transform: rotate(359deg); }
+}
+@-o-keyframes spin {
+ 0% { -o-transform: rotate(0deg); }
+ 100% { -o-transform: rotate(359deg); }
+}
+@-ms-keyframes spin {
+ 0% { -ms-transform: rotate(0deg); }
+ 100% { -ms-transform: rotate(359deg); }
+}
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(359deg); }
+}
+
+
+.#{$ionicons-prefix}loading-a,
+.#{$ionicons-prefix}loading-b,
+.#{$ionicons-prefix}loading-c,
+.#{$ionicons-prefix}loading-d,
+.#{$ionicons-prefix}looping,
+.#{$ionicons-prefix}refreshing,
+.#{$ionicons-prefix}ios7-reloading {
+ @extend .ion;
+ @extend .#{$ionicons-prefix}spin;
+}
+
+.#{$ionicons-prefix}loading-a {
+ -webkit-animation-timing-function: steps(8, start);
+ -moz-animation-timing-function: steps(8, start);
+ animation-timing-function: steps(8, start);
+}
+
+.#{$ionicons-prefix}loading-a:before {
+ @extend .#{$ionicons-prefix}load-a:before;
+}
+
+.#{$ionicons-prefix}loading-b:before {
+ @extend .#{$ionicons-prefix}load-b:before;
+}
+
+.#{$ionicons-prefix}loading-c:before {
+ @extend .#{$ionicons-prefix}load-c:before;
+}
+
+.#{$ionicons-prefix}loading-d:before {
+ @extend .#{$ionicons-prefix}load-d:before;
+}
+
+.#{$ionicons-prefix}looping:before {
+ @extend .#{$ionicons-prefix}loop:before;
+}
+
+.#{$ionicons-prefix}refreshing:before {
+ @extend .#{$ionicons-prefix}refresh:before;
+}
+
+.#{$ionicons-prefix}ios7-reloading:before {
+ @extend .#{$ionicons-prefix}ios7-reload:before;
+}
diff --git a/client/lib/ionic/scss/ionicons/_ionicons-font.scss b/client/lib/ionic/scss/ionicons/_ionicons-font.scss
new file mode 100644
index 0000000..76ec6eb
--- /dev/null
+++ b/client/lib/ionic/scss/ionicons/_ionicons-font.scss
@@ -0,0 +1,27 @@
+// Ionicons Font Path
+// --------------------------
+
+@font-face {
+ font-family: $ionicons-font-family;
+ src:url("#{$ionicons-font-path}/ionicons.eot?v=#{$ionicons-version}");
+ src:url("#{$ionicons-font-path}/ionicons.eot?v=#{$ionicons-version}#iefix") format("embedded-opentype"),
+ url("#{$ionicons-font-path}/ionicons.ttf?v=#{$ionicons-version}") format("truetype"),
+ url("#{$ionicons-font-path}/ionicons.woff?v=#{$ionicons-version}") format("woff"),
+ url("#{$ionicons-font-path}/ionicons.svg?v=#{$ionicons-version}#Ionicons") format("svg");
+ font-weight: normal;
+ font-style: normal;
+}
+
+.ion {
+ display: inline-block;
+ font-family: $ionicons-font-family;
+ speak: none;
+ font-style: normal;
+ font-weight: normal;
+ font-variant: normal;
+ text-transform: none;
+ text-rendering: auto;
+ line-height: 1;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
\ No newline at end of file
diff --git a/client/lib/ionic/scss/ionicons/_ionicons-icons.scss b/client/lib/ionic/scss/ionicons/_ionicons-icons.scss
new file mode 100644
index 0000000..a46b2a9
--- /dev/null
+++ b/client/lib/ionic/scss/ionicons/_ionicons-icons.scss
@@ -0,0 +1,1209 @@
+// Ionicons Icons
+// --------------------------
+
+.ionicons,
+.#{$ionicons-prefix}alert:before,
+.#{$ionicons-prefix}alert-circled:before,
+.#{$ionicons-prefix}android-add:before,
+.#{$ionicons-prefix}android-add-contact:before,
+.#{$ionicons-prefix}android-alarm:before,
+.#{$ionicons-prefix}android-archive:before,
+.#{$ionicons-prefix}android-arrow-back:before,
+.#{$ionicons-prefix}android-arrow-down-left:before,
+.#{$ionicons-prefix}android-arrow-down-right:before,
+.#{$ionicons-prefix}android-arrow-forward:before,
+.#{$ionicons-prefix}android-arrow-up-left:before,
+.#{$ionicons-prefix}android-arrow-up-right:before,
+.#{$ionicons-prefix}android-battery:before,
+.#{$ionicons-prefix}android-book:before,
+.#{$ionicons-prefix}android-calendar:before,
+.#{$ionicons-prefix}android-call:before,
+.#{$ionicons-prefix}android-camera:before,
+.#{$ionicons-prefix}android-chat:before,
+.#{$ionicons-prefix}android-checkmark:before,
+.#{$ionicons-prefix}android-clock:before,
+.#{$ionicons-prefix}android-close:before,
+.#{$ionicons-prefix}android-contact:before,
+.#{$ionicons-prefix}android-contacts:before,
+.#{$ionicons-prefix}android-data:before,
+.#{$ionicons-prefix}android-developer:before,
+.#{$ionicons-prefix}android-display:before,
+.#{$ionicons-prefix}android-download:before,
+.#{$ionicons-prefix}android-drawer:before,
+.#{$ionicons-prefix}android-dropdown:before,
+.#{$ionicons-prefix}android-earth:before,
+.#{$ionicons-prefix}android-folder:before,
+.#{$ionicons-prefix}android-forums:before,
+.#{$ionicons-prefix}android-friends:before,
+.#{$ionicons-prefix}android-hand:before,
+.#{$ionicons-prefix}android-image:before,
+.#{$ionicons-prefix}android-inbox:before,
+.#{$ionicons-prefix}android-information:before,
+.#{$ionicons-prefix}android-keypad:before,
+.#{$ionicons-prefix}android-lightbulb:before,
+.#{$ionicons-prefix}android-locate:before,
+.#{$ionicons-prefix}android-location:before,
+.#{$ionicons-prefix}android-mail:before,
+.#{$ionicons-prefix}android-microphone:before,
+.#{$ionicons-prefix}android-mixer:before,
+.#{$ionicons-prefix}android-more:before,
+.#{$ionicons-prefix}android-note:before,
+.#{$ionicons-prefix}android-playstore:before,
+.#{$ionicons-prefix}android-printer:before,
+.#{$ionicons-prefix}android-promotion:before,
+.#{$ionicons-prefix}android-reminder:before,
+.#{$ionicons-prefix}android-remove:before,
+.#{$ionicons-prefix}android-search:before,
+.#{$ionicons-prefix}android-send:before,
+.#{$ionicons-prefix}android-settings:before,
+.#{$ionicons-prefix}android-share:before,
+.#{$ionicons-prefix}android-social:before,
+.#{$ionicons-prefix}android-social-user:before,
+.#{$ionicons-prefix}android-sort:before,
+.#{$ionicons-prefix}android-stair-drawer:before,
+.#{$ionicons-prefix}android-star:before,
+.#{$ionicons-prefix}android-stopwatch:before,
+.#{$ionicons-prefix}android-storage:before,
+.#{$ionicons-prefix}android-system-back:before,
+.#{$ionicons-prefix}android-system-home:before,
+.#{$ionicons-prefix}android-system-windows:before,
+.#{$ionicons-prefix}android-timer:before,
+.#{$ionicons-prefix}android-trash:before,
+.#{$ionicons-prefix}android-user-menu:before,
+.#{$ionicons-prefix}android-volume:before,
+.#{$ionicons-prefix}android-wifi:before,
+.#{$ionicons-prefix}aperture:before,
+.#{$ionicons-prefix}archive:before,
+.#{$ionicons-prefix}arrow-down-a:before,
+.#{$ionicons-prefix}arrow-down-b:before,
+.#{$ionicons-prefix}arrow-down-c:before,
+.#{$ionicons-prefix}arrow-expand:before,
+.#{$ionicons-prefix}arrow-graph-down-left:before,
+.#{$ionicons-prefix}arrow-graph-down-right:before,
+.#{$ionicons-prefix}arrow-graph-up-left:before,
+.#{$ionicons-prefix}arrow-graph-up-right:before,
+.#{$ionicons-prefix}arrow-left-a:before,
+.#{$ionicons-prefix}arrow-left-b:before,
+.#{$ionicons-prefix}arrow-left-c:before,
+.#{$ionicons-prefix}arrow-move:before,
+.#{$ionicons-prefix}arrow-resize:before,
+.#{$ionicons-prefix}arrow-return-left:before,
+.#{$ionicons-prefix}arrow-return-right:before,
+.#{$ionicons-prefix}arrow-right-a:before,
+.#{$ionicons-prefix}arrow-right-b:before,
+.#{$ionicons-prefix}arrow-right-c:before,
+.#{$ionicons-prefix}arrow-shrink:before,
+.#{$ionicons-prefix}arrow-swap:before,
+.#{$ionicons-prefix}arrow-up-a:before,
+.#{$ionicons-prefix}arrow-up-b:before,
+.#{$ionicons-prefix}arrow-up-c:before,
+.#{$ionicons-prefix}asterisk:before,
+.#{$ionicons-prefix}at:before,
+.#{$ionicons-prefix}bag:before,
+.#{$ionicons-prefix}battery-charging:before,
+.#{$ionicons-prefix}battery-empty:before,
+.#{$ionicons-prefix}battery-full:before,
+.#{$ionicons-prefix}battery-half:before,
+.#{$ionicons-prefix}battery-low:before,
+.#{$ionicons-prefix}beaker:before,
+.#{$ionicons-prefix}beer:before,
+.#{$ionicons-prefix}bluetooth:before,
+.#{$ionicons-prefix}bonfire:before,
+.#{$ionicons-prefix}bookmark:before,
+.#{$ionicons-prefix}briefcase:before,
+.#{$ionicons-prefix}bug:before,
+.#{$ionicons-prefix}calculator:before,
+.#{$ionicons-prefix}calendar:before,
+.#{$ionicons-prefix}camera:before,
+.#{$ionicons-prefix}card:before,
+.#{$ionicons-prefix}cash:before,
+.#{$ionicons-prefix}chatbox:before,
+.#{$ionicons-prefix}chatbox-working:before,
+.#{$ionicons-prefix}chatboxes:before,
+.#{$ionicons-prefix}chatbubble:before,
+.#{$ionicons-prefix}chatbubble-working:before,
+.#{$ionicons-prefix}chatbubbles:before,
+.#{$ionicons-prefix}checkmark:before,
+.#{$ionicons-prefix}checkmark-circled:before,
+.#{$ionicons-prefix}checkmark-round:before,
+.#{$ionicons-prefix}chevron-down:before,
+.#{$ionicons-prefix}chevron-left:before,
+.#{$ionicons-prefix}chevron-right:before,
+.#{$ionicons-prefix}chevron-up:before,
+.#{$ionicons-prefix}clipboard:before,
+.#{$ionicons-prefix}clock:before,
+.#{$ionicons-prefix}close:before,
+.#{$ionicons-prefix}close-circled:before,
+.#{$ionicons-prefix}close-round:before,
+.#{$ionicons-prefix}closed-captioning:before,
+.#{$ionicons-prefix}cloud:before,
+.#{$ionicons-prefix}code:before,
+.#{$ionicons-prefix}code-download:before,
+.#{$ionicons-prefix}code-working:before,
+.#{$ionicons-prefix}coffee:before,
+.#{$ionicons-prefix}compass:before,
+.#{$ionicons-prefix}compose:before,
+.#{$ionicons-prefix}connection-bars:before,
+.#{$ionicons-prefix}contrast:before,
+.#{$ionicons-prefix}cube:before,
+.#{$ionicons-prefix}disc:before,
+.#{$ionicons-prefix}document:before,
+.#{$ionicons-prefix}document-text:before,
+.#{$ionicons-prefix}drag:before,
+.#{$ionicons-prefix}earth:before,
+.#{$ionicons-prefix}edit:before,
+.#{$ionicons-prefix}egg:before,
+.#{$ionicons-prefix}eject:before,
+.#{$ionicons-prefix}email:before,
+.#{$ionicons-prefix}eye:before,
+.#{$ionicons-prefix}eye-disabled:before,
+.#{$ionicons-prefix}female:before,
+.#{$ionicons-prefix}filing:before,
+.#{$ionicons-prefix}film-marker:before,
+.#{$ionicons-prefix}fireball:before,
+.#{$ionicons-prefix}flag:before,
+.#{$ionicons-prefix}flame:before,
+.#{$ionicons-prefix}flash:before,
+.#{$ionicons-prefix}flash-off:before,
+.#{$ionicons-prefix}flask:before,
+.#{$ionicons-prefix}folder:before,
+.#{$ionicons-prefix}fork:before,
+.#{$ionicons-prefix}fork-repo:before,
+.#{$ionicons-prefix}forward:before,
+.#{$ionicons-prefix}funnel:before,
+.#{$ionicons-prefix}game-controller-a:before,
+.#{$ionicons-prefix}game-controller-b:before,
+.#{$ionicons-prefix}gear-a:before,
+.#{$ionicons-prefix}gear-b:before,
+.#{$ionicons-prefix}grid:before,
+.#{$ionicons-prefix}hammer:before,
+.#{$ionicons-prefix}happy:before,
+.#{$ionicons-prefix}headphone:before,
+.#{$ionicons-prefix}heart:before,
+.#{$ionicons-prefix}heart-broken:before,
+.#{$ionicons-prefix}help:before,
+.#{$ionicons-prefix}help-buoy:before,
+.#{$ionicons-prefix}help-circled:before,
+.#{$ionicons-prefix}home:before,
+.#{$ionicons-prefix}icecream:before,
+.#{$ionicons-prefix}icon-social-google-plus:before,
+.#{$ionicons-prefix}icon-social-google-plus-outline:before,
+.#{$ionicons-prefix}image:before,
+.#{$ionicons-prefix}images:before,
+.#{$ionicons-prefix}information:before,
+.#{$ionicons-prefix}information-circled:before,
+.#{$ionicons-prefix}ionic:before,
+.#{$ionicons-prefix}ios7-alarm:before,
+.#{$ionicons-prefix}ios7-alarm-outline:before,
+.#{$ionicons-prefix}ios7-albums:before,
+.#{$ionicons-prefix}ios7-albums-outline:before,
+.#{$ionicons-prefix}ios7-americanfootball:before,
+.#{$ionicons-prefix}ios7-americanfootball-outline:before,
+.#{$ionicons-prefix}ios7-analytics:before,
+.#{$ionicons-prefix}ios7-analytics-outline:before,
+.#{$ionicons-prefix}ios7-arrow-back:before,
+.#{$ionicons-prefix}ios7-arrow-down:before,
+.#{$ionicons-prefix}ios7-arrow-forward:before,
+.#{$ionicons-prefix}ios7-arrow-left:before,
+.#{$ionicons-prefix}ios7-arrow-right:before,
+.#{$ionicons-prefix}ios7-arrow-thin-down:before,
+.#{$ionicons-prefix}ios7-arrow-thin-left:before,
+.#{$ionicons-prefix}ios7-arrow-thin-right:before,
+.#{$ionicons-prefix}ios7-arrow-thin-up:before,
+.#{$ionicons-prefix}ios7-arrow-up:before,
+.#{$ionicons-prefix}ios7-at:before,
+.#{$ionicons-prefix}ios7-at-outline:before,
+.#{$ionicons-prefix}ios7-barcode:before,
+.#{$ionicons-prefix}ios7-barcode-outline:before,
+.#{$ionicons-prefix}ios7-baseball:before,
+.#{$ionicons-prefix}ios7-baseball-outline:before,
+.#{$ionicons-prefix}ios7-basketball:before,
+.#{$ionicons-prefix}ios7-basketball-outline:before,
+.#{$ionicons-prefix}ios7-bell:before,
+.#{$ionicons-prefix}ios7-bell-outline:before,
+.#{$ionicons-prefix}ios7-bolt:before,
+.#{$ionicons-prefix}ios7-bolt-outline:before,
+.#{$ionicons-prefix}ios7-bookmarks:before,
+.#{$ionicons-prefix}ios7-bookmarks-outline:before,
+.#{$ionicons-prefix}ios7-box:before,
+.#{$ionicons-prefix}ios7-box-outline:before,
+.#{$ionicons-prefix}ios7-briefcase:before,
+.#{$ionicons-prefix}ios7-briefcase-outline:before,
+.#{$ionicons-prefix}ios7-browsers:before,
+.#{$ionicons-prefix}ios7-browsers-outline:before,
+.#{$ionicons-prefix}ios7-calculator:before,
+.#{$ionicons-prefix}ios7-calculator-outline:before,
+.#{$ionicons-prefix}ios7-calendar:before,
+.#{$ionicons-prefix}ios7-calendar-outline:before,
+.#{$ionicons-prefix}ios7-camera:before,
+.#{$ionicons-prefix}ios7-camera-outline:before,
+.#{$ionicons-prefix}ios7-cart:before,
+.#{$ionicons-prefix}ios7-cart-outline:before,
+.#{$ionicons-prefix}ios7-chatboxes:before,
+.#{$ionicons-prefix}ios7-chatboxes-outline:before,
+.#{$ionicons-prefix}ios7-chatbubble:before,
+.#{$ionicons-prefix}ios7-chatbubble-outline:before,
+.#{$ionicons-prefix}ios7-checkmark:before,
+.#{$ionicons-prefix}ios7-checkmark-empty:before,
+.#{$ionicons-prefix}ios7-checkmark-outline:before,
+.#{$ionicons-prefix}ios7-circle-filled:before,
+.#{$ionicons-prefix}ios7-circle-outline:before,
+.#{$ionicons-prefix}ios7-clock:before,
+.#{$ionicons-prefix}ios7-clock-outline:before,
+.#{$ionicons-prefix}ios7-close:before,
+.#{$ionicons-prefix}ios7-close-empty:before,
+.#{$ionicons-prefix}ios7-close-outline:before,
+.#{$ionicons-prefix}ios7-cloud:before,
+.#{$ionicons-prefix}ios7-cloud-download:before,
+.#{$ionicons-prefix}ios7-cloud-download-outline:before,
+.#{$ionicons-prefix}ios7-cloud-outline:before,
+.#{$ionicons-prefix}ios7-cloud-upload:before,
+.#{$ionicons-prefix}ios7-cloud-upload-outline:before,
+.#{$ionicons-prefix}ios7-cloudy:before,
+.#{$ionicons-prefix}ios7-cloudy-night:before,
+.#{$ionicons-prefix}ios7-cloudy-night-outline:before,
+.#{$ionicons-prefix}ios7-cloudy-outline:before,
+.#{$ionicons-prefix}ios7-cog:before,
+.#{$ionicons-prefix}ios7-cog-outline:before,
+.#{$ionicons-prefix}ios7-compose:before,
+.#{$ionicons-prefix}ios7-compose-outline:before,
+.#{$ionicons-prefix}ios7-contact:before,
+.#{$ionicons-prefix}ios7-contact-outline:before,
+.#{$ionicons-prefix}ios7-copy:before,
+.#{$ionicons-prefix}ios7-copy-outline:before,
+.#{$ionicons-prefix}ios7-download:before,
+.#{$ionicons-prefix}ios7-download-outline:before,
+.#{$ionicons-prefix}ios7-drag:before,
+.#{$ionicons-prefix}ios7-email:before,
+.#{$ionicons-prefix}ios7-email-outline:before,
+.#{$ionicons-prefix}ios7-expand:before,
+.#{$ionicons-prefix}ios7-eye:before,
+.#{$ionicons-prefix}ios7-eye-outline:before,
+.#{$ionicons-prefix}ios7-fastforward:before,
+.#{$ionicons-prefix}ios7-fastforward-outline:before,
+.#{$ionicons-prefix}ios7-filing:before,
+.#{$ionicons-prefix}ios7-filing-outline:before,
+.#{$ionicons-prefix}ios7-film:before,
+.#{$ionicons-prefix}ios7-film-outline:before,
+.#{$ionicons-prefix}ios7-flag:before,
+.#{$ionicons-prefix}ios7-flag-outline:before,
+.#{$ionicons-prefix}ios7-folder:before,
+.#{$ionicons-prefix}ios7-folder-outline:before,
+.#{$ionicons-prefix}ios7-football:before,
+.#{$ionicons-prefix}ios7-football-outline:before,
+.#{$ionicons-prefix}ios7-gear:before,
+.#{$ionicons-prefix}ios7-gear-outline:before,
+.#{$ionicons-prefix}ios7-glasses:before,
+.#{$ionicons-prefix}ios7-glasses-outline:before,
+.#{$ionicons-prefix}ios7-heart:before,
+.#{$ionicons-prefix}ios7-heart-outline:before,
+.#{$ionicons-prefix}ios7-help:before,
+.#{$ionicons-prefix}ios7-help-empty:before,
+.#{$ionicons-prefix}ios7-help-outline:before,
+.#{$ionicons-prefix}ios7-home:before,
+.#{$ionicons-prefix}ios7-home-outline:before,
+.#{$ionicons-prefix}ios7-infinite:before,
+.#{$ionicons-prefix}ios7-infinite-outline:before,
+.#{$ionicons-prefix}ios7-information:before,
+.#{$ionicons-prefix}ios7-information-empty:before,
+.#{$ionicons-prefix}ios7-information-outline:before,
+.#{$ionicons-prefix}ios7-ionic-outline:before,
+.#{$ionicons-prefix}ios7-keypad:before,
+.#{$ionicons-prefix}ios7-keypad-outline:before,
+.#{$ionicons-prefix}ios7-lightbulb:before,
+.#{$ionicons-prefix}ios7-lightbulb-outline:before,
+.#{$ionicons-prefix}ios7-location:before,
+.#{$ionicons-prefix}ios7-location-outline:before,
+.#{$ionicons-prefix}ios7-locked:before,
+.#{$ionicons-prefix}ios7-locked-outline:before,
+.#{$ionicons-prefix}ios7-loop:before,
+.#{$ionicons-prefix}ios7-loop-strong:before,
+.#{$ionicons-prefix}ios7-medkit:before,
+.#{$ionicons-prefix}ios7-medkit-outline:before,
+.#{$ionicons-prefix}ios7-mic:before,
+.#{$ionicons-prefix}ios7-mic-off:before,
+.#{$ionicons-prefix}ios7-mic-outline:before,
+.#{$ionicons-prefix}ios7-minus:before,
+.#{$ionicons-prefix}ios7-minus-empty:before,
+.#{$ionicons-prefix}ios7-minus-outline:before,
+.#{$ionicons-prefix}ios7-monitor:before,
+.#{$ionicons-prefix}ios7-monitor-outline:before,
+.#{$ionicons-prefix}ios7-moon:before,
+.#{$ionicons-prefix}ios7-moon-outline:before,
+.#{$ionicons-prefix}ios7-more:before,
+.#{$ionicons-prefix}ios7-more-outline:before,
+.#{$ionicons-prefix}ios7-musical-note:before,
+.#{$ionicons-prefix}ios7-musical-notes:before,
+.#{$ionicons-prefix}ios7-navigate:before,
+.#{$ionicons-prefix}ios7-navigate-outline:before,
+.#{$ionicons-prefix}ios7-paper:before,
+.#{$ionicons-prefix}ios7-paper-outline:before,
+.#{$ionicons-prefix}ios7-paperplane:before,
+.#{$ionicons-prefix}ios7-paperplane-outline:before,
+.#{$ionicons-prefix}ios7-partlysunny:before,
+.#{$ionicons-prefix}ios7-partlysunny-outline:before,
+.#{$ionicons-prefix}ios7-pause:before,
+.#{$ionicons-prefix}ios7-pause-outline:before,
+.#{$ionicons-prefix}ios7-paw:before,
+.#{$ionicons-prefix}ios7-paw-outline:before,
+.#{$ionicons-prefix}ios7-people:before,
+.#{$ionicons-prefix}ios7-people-outline:before,
+.#{$ionicons-prefix}ios7-person:before,
+.#{$ionicons-prefix}ios7-person-outline:before,
+.#{$ionicons-prefix}ios7-personadd:before,
+.#{$ionicons-prefix}ios7-personadd-outline:before,
+.#{$ionicons-prefix}ios7-photos:before,
+.#{$ionicons-prefix}ios7-photos-outline:before,
+.#{$ionicons-prefix}ios7-pie:before,
+.#{$ionicons-prefix}ios7-pie-outline:before,
+.#{$ionicons-prefix}ios7-play:before,
+.#{$ionicons-prefix}ios7-play-outline:before,
+.#{$ionicons-prefix}ios7-plus:before,
+.#{$ionicons-prefix}ios7-plus-empty:before,
+.#{$ionicons-prefix}ios7-plus-outline:before,
+.#{$ionicons-prefix}ios7-pricetag:before,
+.#{$ionicons-prefix}ios7-pricetag-outline:before,
+.#{$ionicons-prefix}ios7-pricetags:before,
+.#{$ionicons-prefix}ios7-pricetags-outline:before,
+.#{$ionicons-prefix}ios7-printer:before,
+.#{$ionicons-prefix}ios7-printer-outline:before,
+.#{$ionicons-prefix}ios7-pulse:before,
+.#{$ionicons-prefix}ios7-pulse-strong:before,
+.#{$ionicons-prefix}ios7-rainy:before,
+.#{$ionicons-prefix}ios7-rainy-outline:before,
+.#{$ionicons-prefix}ios7-recording:before,
+.#{$ionicons-prefix}ios7-recording-outline:before,
+.#{$ionicons-prefix}ios7-redo:before,
+.#{$ionicons-prefix}ios7-redo-outline:before,
+.#{$ionicons-prefix}ios7-refresh:before,
+.#{$ionicons-prefix}ios7-refresh-empty:before,
+.#{$ionicons-prefix}ios7-refresh-outline:before,
+.#{$ionicons-prefix}ios7-reload:before,
+.#{$ionicons-prefix}ios7-reverse-camera:before,
+.#{$ionicons-prefix}ios7-reverse-camera-outline:before,
+.#{$ionicons-prefix}ios7-rewind:before,
+.#{$ionicons-prefix}ios7-rewind-outline:before,
+.#{$ionicons-prefix}ios7-search:before,
+.#{$ionicons-prefix}ios7-search-strong:before,
+.#{$ionicons-prefix}ios7-settings:before,
+.#{$ionicons-prefix}ios7-settings-strong:before,
+.#{$ionicons-prefix}ios7-shrink:before,
+.#{$ionicons-prefix}ios7-skipbackward:before,
+.#{$ionicons-prefix}ios7-skipbackward-outline:before,
+.#{$ionicons-prefix}ios7-skipforward:before,
+.#{$ionicons-prefix}ios7-skipforward-outline:before,
+.#{$ionicons-prefix}ios7-snowy:before,
+.#{$ionicons-prefix}ios7-speedometer:before,
+.#{$ionicons-prefix}ios7-speedometer-outline:before,
+.#{$ionicons-prefix}ios7-star:before,
+.#{$ionicons-prefix}ios7-star-half:before,
+.#{$ionicons-prefix}ios7-star-outline:before,
+.#{$ionicons-prefix}ios7-stopwatch:before,
+.#{$ionicons-prefix}ios7-stopwatch-outline:before,
+.#{$ionicons-prefix}ios7-sunny:before,
+.#{$ionicons-prefix}ios7-sunny-outline:before,
+.#{$ionicons-prefix}ios7-telephone:before,
+.#{$ionicons-prefix}ios7-telephone-outline:before,
+.#{$ionicons-prefix}ios7-tennisball:before,
+.#{$ionicons-prefix}ios7-tennisball-outline:before,
+.#{$ionicons-prefix}ios7-thunderstorm:before,
+.#{$ionicons-prefix}ios7-thunderstorm-outline:before,
+.#{$ionicons-prefix}ios7-time:before,
+.#{$ionicons-prefix}ios7-time-outline:before,
+.#{$ionicons-prefix}ios7-timer:before,
+.#{$ionicons-prefix}ios7-timer-outline:before,
+.#{$ionicons-prefix}ios7-toggle:before,
+.#{$ionicons-prefix}ios7-toggle-outline:before,
+.#{$ionicons-prefix}ios7-trash:before,
+.#{$ionicons-prefix}ios7-trash-outline:before,
+.#{$ionicons-prefix}ios7-undo:before,
+.#{$ionicons-prefix}ios7-undo-outline:before,
+.#{$ionicons-prefix}ios7-unlocked:before,
+.#{$ionicons-prefix}ios7-unlocked-outline:before,
+.#{$ionicons-prefix}ios7-upload:before,
+.#{$ionicons-prefix}ios7-upload-outline:before,
+.#{$ionicons-prefix}ios7-videocam:before,
+.#{$ionicons-prefix}ios7-videocam-outline:before,
+.#{$ionicons-prefix}ios7-volume-high:before,
+.#{$ionicons-prefix}ios7-volume-low:before,
+.#{$ionicons-prefix}ios7-wineglass:before,
+.#{$ionicons-prefix}ios7-wineglass-outline:before,
+.#{$ionicons-prefix}ios7-world:before,
+.#{$ionicons-prefix}ios7-world-outline:before,
+.#{$ionicons-prefix}ipad:before,
+.#{$ionicons-prefix}iphone:before,
+.#{$ionicons-prefix}ipod:before,
+.#{$ionicons-prefix}jet:before,
+.#{$ionicons-prefix}key:before,
+.#{$ionicons-prefix}knife:before,
+.#{$ionicons-prefix}laptop:before,
+.#{$ionicons-prefix}leaf:before,
+.#{$ionicons-prefix}levels:before,
+.#{$ionicons-prefix}lightbulb:before,
+.#{$ionicons-prefix}link:before,
+.#{$ionicons-prefix}load-a:before,
+.#{$ionicons-prefix}load-b:before,
+.#{$ionicons-prefix}load-c:before,
+.#{$ionicons-prefix}load-d:before,
+.#{$ionicons-prefix}location:before,
+.#{$ionicons-prefix}locked:before,
+.#{$ionicons-prefix}log-in:before,
+.#{$ionicons-prefix}log-out:before,
+.#{$ionicons-prefix}loop:before,
+.#{$ionicons-prefix}magnet:before,
+.#{$ionicons-prefix}male:before,
+.#{$ionicons-prefix}man:before,
+.#{$ionicons-prefix}map:before,
+.#{$ionicons-prefix}medkit:before,
+.#{$ionicons-prefix}merge:before,
+.#{$ionicons-prefix}mic-a:before,
+.#{$ionicons-prefix}mic-b:before,
+.#{$ionicons-prefix}mic-c:before,
+.#{$ionicons-prefix}minus:before,
+.#{$ionicons-prefix}minus-circled:before,
+.#{$ionicons-prefix}minus-round:before,
+.#{$ionicons-prefix}model-s:before,
+.#{$ionicons-prefix}monitor:before,
+.#{$ionicons-prefix}more:before,
+.#{$ionicons-prefix}mouse:before,
+.#{$ionicons-prefix}music-note:before,
+.#{$ionicons-prefix}navicon:before,
+.#{$ionicons-prefix}navicon-round:before,
+.#{$ionicons-prefix}navigate:before,
+.#{$ionicons-prefix}network:before,
+.#{$ionicons-prefix}no-smoking:before,
+.#{$ionicons-prefix}nuclear:before,
+.#{$ionicons-prefix}outlet:before,
+.#{$ionicons-prefix}paper-airplane:before,
+.#{$ionicons-prefix}paperclip:before,
+.#{$ionicons-prefix}pause:before,
+.#{$ionicons-prefix}person:before,
+.#{$ionicons-prefix}person-add:before,
+.#{$ionicons-prefix}person-stalker:before,
+.#{$ionicons-prefix}pie-graph:before,
+.#{$ionicons-prefix}pin:before,
+.#{$ionicons-prefix}pinpoint:before,
+.#{$ionicons-prefix}pizza:before,
+.#{$ionicons-prefix}plane:before,
+.#{$ionicons-prefix}planet:before,
+.#{$ionicons-prefix}play:before,
+.#{$ionicons-prefix}playstation:before,
+.#{$ionicons-prefix}plus:before,
+.#{$ionicons-prefix}plus-circled:before,
+.#{$ionicons-prefix}plus-round:before,
+.#{$ionicons-prefix}podium:before,
+.#{$ionicons-prefix}pound:before,
+.#{$ionicons-prefix}power:before,
+.#{$ionicons-prefix}pricetag:before,
+.#{$ionicons-prefix}pricetags:before,
+.#{$ionicons-prefix}printer:before,
+.#{$ionicons-prefix}pull-request:before,
+.#{$ionicons-prefix}qr-scanner:before,
+.#{$ionicons-prefix}quote:before,
+.#{$ionicons-prefix}radio-waves:before,
+.#{$ionicons-prefix}record:before,
+.#{$ionicons-prefix}refresh:before,
+.#{$ionicons-prefix}reply:before,
+.#{$ionicons-prefix}reply-all:before,
+.#{$ionicons-prefix}ribbon-a:before,
+.#{$ionicons-prefix}ribbon-b:before,
+.#{$ionicons-prefix}sad:before,
+.#{$ionicons-prefix}scissors:before,
+.#{$ionicons-prefix}search:before,
+.#{$ionicons-prefix}settings:before,
+.#{$ionicons-prefix}share:before,
+.#{$ionicons-prefix}shuffle:before,
+.#{$ionicons-prefix}skip-backward:before,
+.#{$ionicons-prefix}skip-forward:before,
+.#{$ionicons-prefix}social-android:before,
+.#{$ionicons-prefix}social-android-outline:before,
+.#{$ionicons-prefix}social-apple:before,
+.#{$ionicons-prefix}social-apple-outline:before,
+.#{$ionicons-prefix}social-bitcoin:before,
+.#{$ionicons-prefix}social-bitcoin-outline:before,
+.#{$ionicons-prefix}social-buffer:before,
+.#{$ionicons-prefix}social-buffer-outline:before,
+.#{$ionicons-prefix}social-designernews:before,
+.#{$ionicons-prefix}social-designernews-outline:before,
+.#{$ionicons-prefix}social-dribbble:before,
+.#{$ionicons-prefix}social-dribbble-outline:before,
+.#{$ionicons-prefix}social-dropbox:before,
+.#{$ionicons-prefix}social-dropbox-outline:before,
+.#{$ionicons-prefix}social-facebook:before,
+.#{$ionicons-prefix}social-facebook-outline:before,
+.#{$ionicons-prefix}social-foursquare:before,
+.#{$ionicons-prefix}social-foursquare-outline:before,
+.#{$ionicons-prefix}social-freebsd-devil:before,
+.#{$ionicons-prefix}social-github:before,
+.#{$ionicons-prefix}social-github-outline:before,
+.#{$ionicons-prefix}social-google:before,
+.#{$ionicons-prefix}social-google-outline:before,
+.#{$ionicons-prefix}social-googleplus:before,
+.#{$ionicons-prefix}social-googleplus-outline:before,
+.#{$ionicons-prefix}social-hackernews:before,
+.#{$ionicons-prefix}social-hackernews-outline:before,
+.#{$ionicons-prefix}social-instagram:before,
+.#{$ionicons-prefix}social-instagram-outline:before,
+.#{$ionicons-prefix}social-linkedin:before,
+.#{$ionicons-prefix}social-linkedin-outline:before,
+.#{$ionicons-prefix}social-pinterest:before,
+.#{$ionicons-prefix}social-pinterest-outline:before,
+.#{$ionicons-prefix}social-reddit:before,
+.#{$ionicons-prefix}social-reddit-outline:before,
+.#{$ionicons-prefix}social-rss:before,
+.#{$ionicons-prefix}social-rss-outline:before,
+.#{$ionicons-prefix}social-skype:before,
+.#{$ionicons-prefix}social-skype-outline:before,
+.#{$ionicons-prefix}social-tumblr:before,
+.#{$ionicons-prefix}social-tumblr-outline:before,
+.#{$ionicons-prefix}social-tux:before,
+.#{$ionicons-prefix}social-twitter:before,
+.#{$ionicons-prefix}social-twitter-outline:before,
+.#{$ionicons-prefix}social-usd:before,
+.#{$ionicons-prefix}social-usd-outline:before,
+.#{$ionicons-prefix}social-vimeo:before,
+.#{$ionicons-prefix}social-vimeo-outline:before,
+.#{$ionicons-prefix}social-windows:before,
+.#{$ionicons-prefix}social-windows-outline:before,
+.#{$ionicons-prefix}social-wordpress:before,
+.#{$ionicons-prefix}social-wordpress-outline:before,
+.#{$ionicons-prefix}social-yahoo:before,
+.#{$ionicons-prefix}social-yahoo-outline:before,
+.#{$ionicons-prefix}social-youtube:before,
+.#{$ionicons-prefix}social-youtube-outline:before,
+.#{$ionicons-prefix}speakerphone:before,
+.#{$ionicons-prefix}speedometer:before,
+.#{$ionicons-prefix}spoon:before,
+.#{$ionicons-prefix}star:before,
+.#{$ionicons-prefix}stats-bars:before,
+.#{$ionicons-prefix}steam:before,
+.#{$ionicons-prefix}stop:before,
+.#{$ionicons-prefix}thermometer:before,
+.#{$ionicons-prefix}thumbsdown:before,
+.#{$ionicons-prefix}thumbsup:before,
+.#{$ionicons-prefix}toggle:before,
+.#{$ionicons-prefix}toggle-filled:before,
+.#{$ionicons-prefix}trash-a:before,
+.#{$ionicons-prefix}trash-b:before,
+.#{$ionicons-prefix}trophy:before,
+.#{$ionicons-prefix}umbrella:before,
+.#{$ionicons-prefix}university:before,
+.#{$ionicons-prefix}unlocked:before,
+.#{$ionicons-prefix}upload:before,
+.#{$ionicons-prefix}usb:before,
+.#{$ionicons-prefix}videocamera:before,
+.#{$ionicons-prefix}volume-high:before,
+.#{$ionicons-prefix}volume-low:before,
+.#{$ionicons-prefix}volume-medium:before,
+.#{$ionicons-prefix}volume-mute:before,
+.#{$ionicons-prefix}wand:before,
+.#{$ionicons-prefix}waterdrop:before,
+.#{$ionicons-prefix}wifi:before,
+.#{$ionicons-prefix}wineglass:before,
+.#{$ionicons-prefix}woman:before,
+.#{$ionicons-prefix}wrench:before,
+.#{$ionicons-prefix}xbox:before
+{
+ @extend .ion;
+}
+.#{$ionicons-prefix}alert:before { content: $ionicon-var-alert; }
+.#{$ionicons-prefix}alert-circled:before { content: $ionicon-var-alert-circled; }
+.#{$ionicons-prefix}android-add:before { content: $ionicon-var-android-add; }
+.#{$ionicons-prefix}android-add-contact:before { content: $ionicon-var-android-add-contact; }
+.#{$ionicons-prefix}android-alarm:before { content: $ionicon-var-android-alarm; }
+.#{$ionicons-prefix}android-archive:before { content: $ionicon-var-android-archive; }
+.#{$ionicons-prefix}android-arrow-back:before { content: $ionicon-var-android-arrow-back; }
+.#{$ionicons-prefix}android-arrow-down-left:before { content: $ionicon-var-android-arrow-down-left; }
+.#{$ionicons-prefix}android-arrow-down-right:before { content: $ionicon-var-android-arrow-down-right; }
+.#{$ionicons-prefix}android-arrow-forward:before { content: $ionicon-var-android-arrow-forward; }
+.#{$ionicons-prefix}android-arrow-up-left:before { content: $ionicon-var-android-arrow-up-left; }
+.#{$ionicons-prefix}android-arrow-up-right:before { content: $ionicon-var-android-arrow-up-right; }
+.#{$ionicons-prefix}android-battery:before { content: $ionicon-var-android-battery; }
+.#{$ionicons-prefix}android-book:before { content: $ionicon-var-android-book; }
+.#{$ionicons-prefix}android-calendar:before { content: $ionicon-var-android-calendar; }
+.#{$ionicons-prefix}android-call:before { content: $ionicon-var-android-call; }
+.#{$ionicons-prefix}android-camera:before { content: $ionicon-var-android-camera; }
+.#{$ionicons-prefix}android-chat:before { content: $ionicon-var-android-chat; }
+.#{$ionicons-prefix}android-checkmark:before { content: $ionicon-var-android-checkmark; }
+.#{$ionicons-prefix}android-clock:before { content: $ionicon-var-android-clock; }
+.#{$ionicons-prefix}android-close:before { content: $ionicon-var-android-close; }
+.#{$ionicons-prefix}android-contact:before { content: $ionicon-var-android-contact; }
+.#{$ionicons-prefix}android-contacts:before { content: $ionicon-var-android-contacts; }
+.#{$ionicons-prefix}android-data:before { content: $ionicon-var-android-data; }
+.#{$ionicons-prefix}android-developer:before { content: $ionicon-var-android-developer; }
+.#{$ionicons-prefix}android-display:before { content: $ionicon-var-android-display; }
+.#{$ionicons-prefix}android-download:before { content: $ionicon-var-android-download; }
+.#{$ionicons-prefix}android-drawer:before { content: $ionicon-var-android-drawer; }
+.#{$ionicons-prefix}android-dropdown:before { content: $ionicon-var-android-dropdown; }
+.#{$ionicons-prefix}android-earth:before { content: $ionicon-var-android-earth; }
+.#{$ionicons-prefix}android-folder:before { content: $ionicon-var-android-folder; }
+.#{$ionicons-prefix}android-forums:before { content: $ionicon-var-android-forums; }
+.#{$ionicons-prefix}android-friends:before { content: $ionicon-var-android-friends; }
+.#{$ionicons-prefix}android-hand:before { content: $ionicon-var-android-hand; }
+.#{$ionicons-prefix}android-image:before { content: $ionicon-var-android-image; }
+.#{$ionicons-prefix}android-inbox:before { content: $ionicon-var-android-inbox; }
+.#{$ionicons-prefix}android-information:before { content: $ionicon-var-android-information; }
+.#{$ionicons-prefix}android-keypad:before { content: $ionicon-var-android-keypad; }
+.#{$ionicons-prefix}android-lightbulb:before { content: $ionicon-var-android-lightbulb; }
+.#{$ionicons-prefix}android-locate:before { content: $ionicon-var-android-locate; }
+.#{$ionicons-prefix}android-location:before { content: $ionicon-var-android-location; }
+.#{$ionicons-prefix}android-mail:before { content: $ionicon-var-android-mail; }
+.#{$ionicons-prefix}android-microphone:before { content: $ionicon-var-android-microphone; }
+.#{$ionicons-prefix}android-mixer:before { content: $ionicon-var-android-mixer; }
+.#{$ionicons-prefix}android-more:before { content: $ionicon-var-android-more; }
+.#{$ionicons-prefix}android-note:before { content: $ionicon-var-android-note; }
+.#{$ionicons-prefix}android-playstore:before { content: $ionicon-var-android-playstore; }
+.#{$ionicons-prefix}android-printer:before { content: $ionicon-var-android-printer; }
+.#{$ionicons-prefix}android-promotion:before { content: $ionicon-var-android-promotion; }
+.#{$ionicons-prefix}android-reminder:before { content: $ionicon-var-android-reminder; }
+.#{$ionicons-prefix}android-remove:before { content: $ionicon-var-android-remove; }
+.#{$ionicons-prefix}android-search:before { content: $ionicon-var-android-search; }
+.#{$ionicons-prefix}android-send:before { content: $ionicon-var-android-send; }
+.#{$ionicons-prefix}android-settings:before { content: $ionicon-var-android-settings; }
+.#{$ionicons-prefix}android-share:before { content: $ionicon-var-android-share; }
+.#{$ionicons-prefix}android-social:before { content: $ionicon-var-android-social; }
+.#{$ionicons-prefix}android-social-user:before { content: $ionicon-var-android-social-user; }
+.#{$ionicons-prefix}android-sort:before { content: $ionicon-var-android-sort; }
+.#{$ionicons-prefix}android-stair-drawer:before { content: $ionicon-var-android-stair-drawer; }
+.#{$ionicons-prefix}android-star:before { content: $ionicon-var-android-star; }
+.#{$ionicons-prefix}android-stopwatch:before { content: $ionicon-var-android-stopwatch; }
+.#{$ionicons-prefix}android-storage:before { content: $ionicon-var-android-storage; }
+.#{$ionicons-prefix}android-system-back:before { content: $ionicon-var-android-system-back; }
+.#{$ionicons-prefix}android-system-home:before { content: $ionicon-var-android-system-home; }
+.#{$ionicons-prefix}android-system-windows:before { content: $ionicon-var-android-system-windows; }
+.#{$ionicons-prefix}android-timer:before { content: $ionicon-var-android-timer; }
+.#{$ionicons-prefix}android-trash:before { content: $ionicon-var-android-trash; }
+.#{$ionicons-prefix}android-user-menu:before { content: $ionicon-var-android-user-menu; }
+.#{$ionicons-prefix}android-volume:before { content: $ionicon-var-android-volume; }
+.#{$ionicons-prefix}android-wifi:before { content: $ionicon-var-android-wifi; }
+.#{$ionicons-prefix}aperture:before { content: $ionicon-var-aperture; }
+.#{$ionicons-prefix}archive:before { content: $ionicon-var-archive; }
+.#{$ionicons-prefix}arrow-down-a:before { content: $ionicon-var-arrow-down-a; }
+.#{$ionicons-prefix}arrow-down-b:before { content: $ionicon-var-arrow-down-b; }
+.#{$ionicons-prefix}arrow-down-c:before { content: $ionicon-var-arrow-down-c; }
+.#{$ionicons-prefix}arrow-expand:before { content: $ionicon-var-arrow-expand; }
+.#{$ionicons-prefix}arrow-graph-down-left:before { content: $ionicon-var-arrow-graph-down-left; }
+.#{$ionicons-prefix}arrow-graph-down-right:before { content: $ionicon-var-arrow-graph-down-right; }
+.#{$ionicons-prefix}arrow-graph-up-left:before { content: $ionicon-var-arrow-graph-up-left; }
+.#{$ionicons-prefix}arrow-graph-up-right:before { content: $ionicon-var-arrow-graph-up-right; }
+.#{$ionicons-prefix}arrow-left-a:before { content: $ionicon-var-arrow-left-a; }
+.#{$ionicons-prefix}arrow-left-b:before { content: $ionicon-var-arrow-left-b; }
+.#{$ionicons-prefix}arrow-left-c:before { content: $ionicon-var-arrow-left-c; }
+.#{$ionicons-prefix}arrow-move:before { content: $ionicon-var-arrow-move; }
+.#{$ionicons-prefix}arrow-resize:before { content: $ionicon-var-arrow-resize; }
+.#{$ionicons-prefix}arrow-return-left:before { content: $ionicon-var-arrow-return-left; }
+.#{$ionicons-prefix}arrow-return-right:before { content: $ionicon-var-arrow-return-right; }
+.#{$ionicons-prefix}arrow-right-a:before { content: $ionicon-var-arrow-right-a; }
+.#{$ionicons-prefix}arrow-right-b:before { content: $ionicon-var-arrow-right-b; }
+.#{$ionicons-prefix}arrow-right-c:before { content: $ionicon-var-arrow-right-c; }
+.#{$ionicons-prefix}arrow-shrink:before { content: $ionicon-var-arrow-shrink; }
+.#{$ionicons-prefix}arrow-swap:before { content: $ionicon-var-arrow-swap; }
+.#{$ionicons-prefix}arrow-up-a:before { content: $ionicon-var-arrow-up-a; }
+.#{$ionicons-prefix}arrow-up-b:before { content: $ionicon-var-arrow-up-b; }
+.#{$ionicons-prefix}arrow-up-c:before { content: $ionicon-var-arrow-up-c; }
+.#{$ionicons-prefix}asterisk:before { content: $ionicon-var-asterisk; }
+.#{$ionicons-prefix}at:before { content: $ionicon-var-at; }
+.#{$ionicons-prefix}bag:before { content: $ionicon-var-bag; }
+.#{$ionicons-prefix}battery-charging:before { content: $ionicon-var-battery-charging; }
+.#{$ionicons-prefix}battery-empty:before { content: $ionicon-var-battery-empty; }
+.#{$ionicons-prefix}battery-full:before { content: $ionicon-var-battery-full; }
+.#{$ionicons-prefix}battery-half:before { content: $ionicon-var-battery-half; }
+.#{$ionicons-prefix}battery-low:before { content: $ionicon-var-battery-low; }
+.#{$ionicons-prefix}beaker:before { content: $ionicon-var-beaker; }
+.#{$ionicons-prefix}beer:before { content: $ionicon-var-beer; }
+.#{$ionicons-prefix}bluetooth:before { content: $ionicon-var-bluetooth; }
+.#{$ionicons-prefix}bonfire:before { content: $ionicon-var-bonfire; }
+.#{$ionicons-prefix}bookmark:before { content: $ionicon-var-bookmark; }
+.#{$ionicons-prefix}briefcase:before { content: $ionicon-var-briefcase; }
+.#{$ionicons-prefix}bug:before { content: $ionicon-var-bug; }
+.#{$ionicons-prefix}calculator:before { content: $ionicon-var-calculator; }
+.#{$ionicons-prefix}calendar:before { content: $ionicon-var-calendar; }
+.#{$ionicons-prefix}camera:before { content: $ionicon-var-camera; }
+.#{$ionicons-prefix}card:before { content: $ionicon-var-card; }
+.#{$ionicons-prefix}cash:before { content: $ionicon-var-cash; }
+.#{$ionicons-prefix}chatbox:before { content: $ionicon-var-chatbox; }
+.#{$ionicons-prefix}chatbox-working:before { content: $ionicon-var-chatbox-working; }
+.#{$ionicons-prefix}chatboxes:before { content: $ionicon-var-chatboxes; }
+.#{$ionicons-prefix}chatbubble:before { content: $ionicon-var-chatbubble; }
+.#{$ionicons-prefix}chatbubble-working:before { content: $ionicon-var-chatbubble-working; }
+.#{$ionicons-prefix}chatbubbles:before { content: $ionicon-var-chatbubbles; }
+.#{$ionicons-prefix}checkmark:before { content: $ionicon-var-checkmark; }
+.#{$ionicons-prefix}checkmark-circled:before { content: $ionicon-var-checkmark-circled; }
+.#{$ionicons-prefix}checkmark-round:before { content: $ionicon-var-checkmark-round; }
+.#{$ionicons-prefix}chevron-down:before { content: $ionicon-var-chevron-down; }
+.#{$ionicons-prefix}chevron-left:before { content: $ionicon-var-chevron-left; }
+.#{$ionicons-prefix}chevron-right:before { content: $ionicon-var-chevron-right; }
+.#{$ionicons-prefix}chevron-up:before { content: $ionicon-var-chevron-up; }
+.#{$ionicons-prefix}clipboard:before { content: $ionicon-var-clipboard; }
+.#{$ionicons-prefix}clock:before { content: $ionicon-var-clock; }
+.#{$ionicons-prefix}close:before { content: $ionicon-var-close; }
+.#{$ionicons-prefix}close-circled:before { content: $ionicon-var-close-circled; }
+.#{$ionicons-prefix}close-round:before { content: $ionicon-var-close-round; }
+.#{$ionicons-prefix}closed-captioning:before { content: $ionicon-var-closed-captioning; }
+.#{$ionicons-prefix}cloud:before { content: $ionicon-var-cloud; }
+.#{$ionicons-prefix}code:before { content: $ionicon-var-code; }
+.#{$ionicons-prefix}code-download:before { content: $ionicon-var-code-download; }
+.#{$ionicons-prefix}code-working:before { content: $ionicon-var-code-working; }
+.#{$ionicons-prefix}coffee:before { content: $ionicon-var-coffee; }
+.#{$ionicons-prefix}compass:before { content: $ionicon-var-compass; }
+.#{$ionicons-prefix}compose:before { content: $ionicon-var-compose; }
+.#{$ionicons-prefix}connection-bars:before { content: $ionicon-var-connection-bars; }
+.#{$ionicons-prefix}contrast:before { content: $ionicon-var-contrast; }
+.#{$ionicons-prefix}cube:before { content: $ionicon-var-cube; }
+.#{$ionicons-prefix}disc:before { content: $ionicon-var-disc; }
+.#{$ionicons-prefix}document:before { content: $ionicon-var-document; }
+.#{$ionicons-prefix}document-text:before { content: $ionicon-var-document-text; }
+.#{$ionicons-prefix}drag:before { content: $ionicon-var-drag; }
+.#{$ionicons-prefix}earth:before { content: $ionicon-var-earth; }
+.#{$ionicons-prefix}edit:before { content: $ionicon-var-edit; }
+.#{$ionicons-prefix}egg:before { content: $ionicon-var-egg; }
+.#{$ionicons-prefix}eject:before { content: $ionicon-var-eject; }
+.#{$ionicons-prefix}email:before { content: $ionicon-var-email; }
+.#{$ionicons-prefix}eye:before { content: $ionicon-var-eye; }
+.#{$ionicons-prefix}eye-disabled:before { content: $ionicon-var-eye-disabled; }
+.#{$ionicons-prefix}female:before { content: $ionicon-var-female; }
+.#{$ionicons-prefix}filing:before { content: $ionicon-var-filing; }
+.#{$ionicons-prefix}film-marker:before { content: $ionicon-var-film-marker; }
+.#{$ionicons-prefix}fireball:before { content: $ionicon-var-fireball; }
+.#{$ionicons-prefix}flag:before { content: $ionicon-var-flag; }
+.#{$ionicons-prefix}flame:before { content: $ionicon-var-flame; }
+.#{$ionicons-prefix}flash:before { content: $ionicon-var-flash; }
+.#{$ionicons-prefix}flash-off:before { content: $ionicon-var-flash-off; }
+.#{$ionicons-prefix}flask:before { content: $ionicon-var-flask; }
+.#{$ionicons-prefix}folder:before { content: $ionicon-var-folder; }
+.#{$ionicons-prefix}fork:before { content: $ionicon-var-fork; }
+.#{$ionicons-prefix}fork-repo:before { content: $ionicon-var-fork-repo; }
+.#{$ionicons-prefix}forward:before { content: $ionicon-var-forward; }
+.#{$ionicons-prefix}funnel:before { content: $ionicon-var-funnel; }
+.#{$ionicons-prefix}game-controller-a:before { content: $ionicon-var-game-controller-a; }
+.#{$ionicons-prefix}game-controller-b:before { content: $ionicon-var-game-controller-b; }
+.#{$ionicons-prefix}gear-a:before { content: $ionicon-var-gear-a; }
+.#{$ionicons-prefix}gear-b:before { content: $ionicon-var-gear-b; }
+.#{$ionicons-prefix}grid:before { content: $ionicon-var-grid; }
+.#{$ionicons-prefix}hammer:before { content: $ionicon-var-hammer; }
+.#{$ionicons-prefix}happy:before { content: $ionicon-var-happy; }
+.#{$ionicons-prefix}headphone:before { content: $ionicon-var-headphone; }
+.#{$ionicons-prefix}heart:before { content: $ionicon-var-heart; }
+.#{$ionicons-prefix}heart-broken:before { content: $ionicon-var-heart-broken; }
+.#{$ionicons-prefix}help:before { content: $ionicon-var-help; }
+.#{$ionicons-prefix}help-buoy:before { content: $ionicon-var-help-buoy; }
+.#{$ionicons-prefix}help-circled:before { content: $ionicon-var-help-circled; }
+.#{$ionicons-prefix}home:before { content: $ionicon-var-home; }
+.#{$ionicons-prefix}icecream:before { content: $ionicon-var-icecream; }
+.#{$ionicons-prefix}icon-social-google-plus:before { content: $ionicon-var-icon-social-google-plus; }
+.#{$ionicons-prefix}icon-social-google-plus-outline:before { content: $ionicon-var-icon-social-google-plus-outline; }
+.#{$ionicons-prefix}image:before { content: $ionicon-var-image; }
+.#{$ionicons-prefix}images:before { content: $ionicon-var-images; }
+.#{$ionicons-prefix}information:before { content: $ionicon-var-information; }
+.#{$ionicons-prefix}information-circled:before { content: $ionicon-var-information-circled; }
+.#{$ionicons-prefix}ionic:before { content: $ionicon-var-ionic; }
+.#{$ionicons-prefix}ios7-alarm:before { content: $ionicon-var-ios7-alarm; }
+.#{$ionicons-prefix}ios7-alarm-outline:before { content: $ionicon-var-ios7-alarm-outline; }
+.#{$ionicons-prefix}ios7-albums:before { content: $ionicon-var-ios7-albums; }
+.#{$ionicons-prefix}ios7-albums-outline:before { content: $ionicon-var-ios7-albums-outline; }
+.#{$ionicons-prefix}ios7-americanfootball:before { content: $ionicon-var-ios7-americanfootball; }
+.#{$ionicons-prefix}ios7-americanfootball-outline:before { content: $ionicon-var-ios7-americanfootball-outline; }
+.#{$ionicons-prefix}ios7-analytics:before { content: $ionicon-var-ios7-analytics; }
+.#{$ionicons-prefix}ios7-analytics-outline:before { content: $ionicon-var-ios7-analytics-outline; }
+.#{$ionicons-prefix}ios7-arrow-back:before { content: $ionicon-var-ios7-arrow-back; }
+.#{$ionicons-prefix}ios7-arrow-down:before { content: $ionicon-var-ios7-arrow-down; }
+.#{$ionicons-prefix}ios7-arrow-forward:before { content: $ionicon-var-ios7-arrow-forward; }
+.#{$ionicons-prefix}ios7-arrow-left:before { content: $ionicon-var-ios7-arrow-left; }
+.#{$ionicons-prefix}ios7-arrow-right:before { content: $ionicon-var-ios7-arrow-right; }
+.#{$ionicons-prefix}ios7-arrow-thin-down:before { content: $ionicon-var-ios7-arrow-thin-down; }
+.#{$ionicons-prefix}ios7-arrow-thin-left:before { content: $ionicon-var-ios7-arrow-thin-left; }
+.#{$ionicons-prefix}ios7-arrow-thin-right:before { content: $ionicon-var-ios7-arrow-thin-right; }
+.#{$ionicons-prefix}ios7-arrow-thin-up:before { content: $ionicon-var-ios7-arrow-thin-up; }
+.#{$ionicons-prefix}ios7-arrow-up:before { content: $ionicon-var-ios7-arrow-up; }
+.#{$ionicons-prefix}ios7-at:before { content: $ionicon-var-ios7-at; }
+.#{$ionicons-prefix}ios7-at-outline:before { content: $ionicon-var-ios7-at-outline; }
+.#{$ionicons-prefix}ios7-barcode:before { content: $ionicon-var-ios7-barcode; }
+.#{$ionicons-prefix}ios7-barcode-outline:before { content: $ionicon-var-ios7-barcode-outline; }
+.#{$ionicons-prefix}ios7-baseball:before { content: $ionicon-var-ios7-baseball; }
+.#{$ionicons-prefix}ios7-baseball-outline:before { content: $ionicon-var-ios7-baseball-outline; }
+.#{$ionicons-prefix}ios7-basketball:before { content: $ionicon-var-ios7-basketball; }
+.#{$ionicons-prefix}ios7-basketball-outline:before { content: $ionicon-var-ios7-basketball-outline; }
+.#{$ionicons-prefix}ios7-bell:before { content: $ionicon-var-ios7-bell; }
+.#{$ionicons-prefix}ios7-bell-outline:before { content: $ionicon-var-ios7-bell-outline; }
+.#{$ionicons-prefix}ios7-bolt:before { content: $ionicon-var-ios7-bolt; }
+.#{$ionicons-prefix}ios7-bolt-outline:before { content: $ionicon-var-ios7-bolt-outline; }
+.#{$ionicons-prefix}ios7-bookmarks:before { content: $ionicon-var-ios7-bookmarks; }
+.#{$ionicons-prefix}ios7-bookmarks-outline:before { content: $ionicon-var-ios7-bookmarks-outline; }
+.#{$ionicons-prefix}ios7-box:before { content: $ionicon-var-ios7-box; }
+.#{$ionicons-prefix}ios7-box-outline:before { content: $ionicon-var-ios7-box-outline; }
+.#{$ionicons-prefix}ios7-briefcase:before { content: $ionicon-var-ios7-briefcase; }
+.#{$ionicons-prefix}ios7-briefcase-outline:before { content: $ionicon-var-ios7-briefcase-outline; }
+.#{$ionicons-prefix}ios7-browsers:before { content: $ionicon-var-ios7-browsers; }
+.#{$ionicons-prefix}ios7-browsers-outline:before { content: $ionicon-var-ios7-browsers-outline; }
+.#{$ionicons-prefix}ios7-calculator:before { content: $ionicon-var-ios7-calculator; }
+.#{$ionicons-prefix}ios7-calculator-outline:before { content: $ionicon-var-ios7-calculator-outline; }
+.#{$ionicons-prefix}ios7-calendar:before { content: $ionicon-var-ios7-calendar; }
+.#{$ionicons-prefix}ios7-calendar-outline:before { content: $ionicon-var-ios7-calendar-outline; }
+.#{$ionicons-prefix}ios7-camera:before { content: $ionicon-var-ios7-camera; }
+.#{$ionicons-prefix}ios7-camera-outline:before { content: $ionicon-var-ios7-camera-outline; }
+.#{$ionicons-prefix}ios7-cart:before { content: $ionicon-var-ios7-cart; }
+.#{$ionicons-prefix}ios7-cart-outline:before { content: $ionicon-var-ios7-cart-outline; }
+.#{$ionicons-prefix}ios7-chatboxes:before { content: $ionicon-var-ios7-chatboxes; }
+.#{$ionicons-prefix}ios7-chatboxes-outline:before { content: $ionicon-var-ios7-chatboxes-outline; }
+.#{$ionicons-prefix}ios7-chatbubble:before { content: $ionicon-var-ios7-chatbubble; }
+.#{$ionicons-prefix}ios7-chatbubble-outline:before { content: $ionicon-var-ios7-chatbubble-outline; }
+.#{$ionicons-prefix}ios7-checkmark:before { content: $ionicon-var-ios7-checkmark; }
+.#{$ionicons-prefix}ios7-checkmark-empty:before { content: $ionicon-var-ios7-checkmark-empty; }
+.#{$ionicons-prefix}ios7-checkmark-outline:before { content: $ionicon-var-ios7-checkmark-outline; }
+.#{$ionicons-prefix}ios7-circle-filled:before { content: $ionicon-var-ios7-circle-filled; }
+.#{$ionicons-prefix}ios7-circle-outline:before { content: $ionicon-var-ios7-circle-outline; }
+.#{$ionicons-prefix}ios7-clock:before { content: $ionicon-var-ios7-clock; }
+.#{$ionicons-prefix}ios7-clock-outline:before { content: $ionicon-var-ios7-clock-outline; }
+.#{$ionicons-prefix}ios7-close:before { content: $ionicon-var-ios7-close; }
+.#{$ionicons-prefix}ios7-close-empty:before { content: $ionicon-var-ios7-close-empty; }
+.#{$ionicons-prefix}ios7-close-outline:before { content: $ionicon-var-ios7-close-outline; }
+.#{$ionicons-prefix}ios7-cloud:before { content: $ionicon-var-ios7-cloud; }
+.#{$ionicons-prefix}ios7-cloud-download:before { content: $ionicon-var-ios7-cloud-download; }
+.#{$ionicons-prefix}ios7-cloud-download-outline:before { content: $ionicon-var-ios7-cloud-download-outline; }
+.#{$ionicons-prefix}ios7-cloud-outline:before { content: $ionicon-var-ios7-cloud-outline; }
+.#{$ionicons-prefix}ios7-cloud-upload:before { content: $ionicon-var-ios7-cloud-upload; }
+.#{$ionicons-prefix}ios7-cloud-upload-outline:before { content: $ionicon-var-ios7-cloud-upload-outline; }
+.#{$ionicons-prefix}ios7-cloudy:before { content: $ionicon-var-ios7-cloudy; }
+.#{$ionicons-prefix}ios7-cloudy-night:before { content: $ionicon-var-ios7-cloudy-night; }
+.#{$ionicons-prefix}ios7-cloudy-night-outline:before { content: $ionicon-var-ios7-cloudy-night-outline; }
+.#{$ionicons-prefix}ios7-cloudy-outline:before { content: $ionicon-var-ios7-cloudy-outline; }
+.#{$ionicons-prefix}ios7-cog:before { content: $ionicon-var-ios7-cog; }
+.#{$ionicons-prefix}ios7-cog-outline:before { content: $ionicon-var-ios7-cog-outline; }
+.#{$ionicons-prefix}ios7-compose:before { content: $ionicon-var-ios7-compose; }
+.#{$ionicons-prefix}ios7-compose-outline:before { content: $ionicon-var-ios7-compose-outline; }
+.#{$ionicons-prefix}ios7-contact:before { content: $ionicon-var-ios7-contact; }
+.#{$ionicons-prefix}ios7-contact-outline:before { content: $ionicon-var-ios7-contact-outline; }
+.#{$ionicons-prefix}ios7-copy:before { content: $ionicon-var-ios7-copy; }
+.#{$ionicons-prefix}ios7-copy-outline:before { content: $ionicon-var-ios7-copy-outline; }
+.#{$ionicons-prefix}ios7-download:before { content: $ionicon-var-ios7-download; }
+.#{$ionicons-prefix}ios7-download-outline:before { content: $ionicon-var-ios7-download-outline; }
+.#{$ionicons-prefix}ios7-drag:before { content: $ionicon-var-ios7-drag; }
+.#{$ionicons-prefix}ios7-email:before { content: $ionicon-var-ios7-email; }
+.#{$ionicons-prefix}ios7-email-outline:before { content: $ionicon-var-ios7-email-outline; }
+.#{$ionicons-prefix}ios7-expand:before { content: $ionicon-var-ios7-expand; }
+.#{$ionicons-prefix}ios7-eye:before { content: $ionicon-var-ios7-eye; }
+.#{$ionicons-prefix}ios7-eye-outline:before { content: $ionicon-var-ios7-eye-outline; }
+.#{$ionicons-prefix}ios7-fastforward:before { content: $ionicon-var-ios7-fastforward; }
+.#{$ionicons-prefix}ios7-fastforward-outline:before { content: $ionicon-var-ios7-fastforward-outline; }
+.#{$ionicons-prefix}ios7-filing:before { content: $ionicon-var-ios7-filing; }
+.#{$ionicons-prefix}ios7-filing-outline:before { content: $ionicon-var-ios7-filing-outline; }
+.#{$ionicons-prefix}ios7-film:before { content: $ionicon-var-ios7-film; }
+.#{$ionicons-prefix}ios7-film-outline:before { content: $ionicon-var-ios7-film-outline; }
+.#{$ionicons-prefix}ios7-flag:before { content: $ionicon-var-ios7-flag; }
+.#{$ionicons-prefix}ios7-flag-outline:before { content: $ionicon-var-ios7-flag-outline; }
+.#{$ionicons-prefix}ios7-folder:before { content: $ionicon-var-ios7-folder; }
+.#{$ionicons-prefix}ios7-folder-outline:before { content: $ionicon-var-ios7-folder-outline; }
+.#{$ionicons-prefix}ios7-football:before { content: $ionicon-var-ios7-football; }
+.#{$ionicons-prefix}ios7-football-outline:before { content: $ionicon-var-ios7-football-outline; }
+.#{$ionicons-prefix}ios7-gear:before { content: $ionicon-var-ios7-gear; }
+.#{$ionicons-prefix}ios7-gear-outline:before { content: $ionicon-var-ios7-gear-outline; }
+.#{$ionicons-prefix}ios7-glasses:before { content: $ionicon-var-ios7-glasses; }
+.#{$ionicons-prefix}ios7-glasses-outline:before { content: $ionicon-var-ios7-glasses-outline; }
+.#{$ionicons-prefix}ios7-heart:before { content: $ionicon-var-ios7-heart; }
+.#{$ionicons-prefix}ios7-heart-outline:before { content: $ionicon-var-ios7-heart-outline; }
+.#{$ionicons-prefix}ios7-help:before { content: $ionicon-var-ios7-help; }
+.#{$ionicons-prefix}ios7-help-empty:before { content: $ionicon-var-ios7-help-empty; }
+.#{$ionicons-prefix}ios7-help-outline:before { content: $ionicon-var-ios7-help-outline; }
+.#{$ionicons-prefix}ios7-home:before { content: $ionicon-var-ios7-home; }
+.#{$ionicons-prefix}ios7-home-outline:before { content: $ionicon-var-ios7-home-outline; }
+.#{$ionicons-prefix}ios7-infinite:before { content: $ionicon-var-ios7-infinite; }
+.#{$ionicons-prefix}ios7-infinite-outline:before { content: $ionicon-var-ios7-infinite-outline; }
+.#{$ionicons-prefix}ios7-information:before { content: $ionicon-var-ios7-information; }
+.#{$ionicons-prefix}ios7-information-empty:before { content: $ionicon-var-ios7-information-empty; }
+.#{$ionicons-prefix}ios7-information-outline:before { content: $ionicon-var-ios7-information-outline; }
+.#{$ionicons-prefix}ios7-ionic-outline:before { content: $ionicon-var-ios7-ionic-outline; }
+.#{$ionicons-prefix}ios7-keypad:before { content: $ionicon-var-ios7-keypad; }
+.#{$ionicons-prefix}ios7-keypad-outline:before { content: $ionicon-var-ios7-keypad-outline; }
+.#{$ionicons-prefix}ios7-lightbulb:before { content: $ionicon-var-ios7-lightbulb; }
+.#{$ionicons-prefix}ios7-lightbulb-outline:before { content: $ionicon-var-ios7-lightbulb-outline; }
+.#{$ionicons-prefix}ios7-location:before { content: $ionicon-var-ios7-location; }
+.#{$ionicons-prefix}ios7-location-outline:before { content: $ionicon-var-ios7-location-outline; }
+.#{$ionicons-prefix}ios7-locked:before { content: $ionicon-var-ios7-locked; }
+.#{$ionicons-prefix}ios7-locked-outline:before { content: $ionicon-var-ios7-locked-outline; }
+.#{$ionicons-prefix}ios7-loop:before { content: $ionicon-var-ios7-loop; }
+.#{$ionicons-prefix}ios7-loop-strong:before { content: $ionicon-var-ios7-loop-strong; }
+.#{$ionicons-prefix}ios7-medkit:before { content: $ionicon-var-ios7-medkit; }
+.#{$ionicons-prefix}ios7-medkit-outline:before { content: $ionicon-var-ios7-medkit-outline; }
+.#{$ionicons-prefix}ios7-mic:before { content: $ionicon-var-ios7-mic; }
+.#{$ionicons-prefix}ios7-mic-off:before { content: $ionicon-var-ios7-mic-off; }
+.#{$ionicons-prefix}ios7-mic-outline:before { content: $ionicon-var-ios7-mic-outline; }
+.#{$ionicons-prefix}ios7-minus:before { content: $ionicon-var-ios7-minus; }
+.#{$ionicons-prefix}ios7-minus-empty:before { content: $ionicon-var-ios7-minus-empty; }
+.#{$ionicons-prefix}ios7-minus-outline:before { content: $ionicon-var-ios7-minus-outline; }
+.#{$ionicons-prefix}ios7-monitor:before { content: $ionicon-var-ios7-monitor; }
+.#{$ionicons-prefix}ios7-monitor-outline:before { content: $ionicon-var-ios7-monitor-outline; }
+.#{$ionicons-prefix}ios7-moon:before { content: $ionicon-var-ios7-moon; }
+.#{$ionicons-prefix}ios7-moon-outline:before { content: $ionicon-var-ios7-moon-outline; }
+.#{$ionicons-prefix}ios7-more:before { content: $ionicon-var-ios7-more; }
+.#{$ionicons-prefix}ios7-more-outline:before { content: $ionicon-var-ios7-more-outline; }
+.#{$ionicons-prefix}ios7-musical-note:before { content: $ionicon-var-ios7-musical-note; }
+.#{$ionicons-prefix}ios7-musical-notes:before { content: $ionicon-var-ios7-musical-notes; }
+.#{$ionicons-prefix}ios7-navigate:before { content: $ionicon-var-ios7-navigate; }
+.#{$ionicons-prefix}ios7-navigate-outline:before { content: $ionicon-var-ios7-navigate-outline; }
+.#{$ionicons-prefix}ios7-paper:before { content: $ionicon-var-ios7-paper; }
+.#{$ionicons-prefix}ios7-paper-outline:before { content: $ionicon-var-ios7-paper-outline; }
+.#{$ionicons-prefix}ios7-paperplane:before { content: $ionicon-var-ios7-paperplane; }
+.#{$ionicons-prefix}ios7-paperplane-outline:before { content: $ionicon-var-ios7-paperplane-outline; }
+.#{$ionicons-prefix}ios7-partlysunny:before { content: $ionicon-var-ios7-partlysunny; }
+.#{$ionicons-prefix}ios7-partlysunny-outline:before { content: $ionicon-var-ios7-partlysunny-outline; }
+.#{$ionicons-prefix}ios7-pause:before { content: $ionicon-var-ios7-pause; }
+.#{$ionicons-prefix}ios7-pause-outline:before { content: $ionicon-var-ios7-pause-outline; }
+.#{$ionicons-prefix}ios7-paw:before { content: $ionicon-var-ios7-paw; }
+.#{$ionicons-prefix}ios7-paw-outline:before { content: $ionicon-var-ios7-paw-outline; }
+.#{$ionicons-prefix}ios7-people:before { content: $ionicon-var-ios7-people; }
+.#{$ionicons-prefix}ios7-people-outline:before { content: $ionicon-var-ios7-people-outline; }
+.#{$ionicons-prefix}ios7-person:before { content: $ionicon-var-ios7-person; }
+.#{$ionicons-prefix}ios7-person-outline:before { content: $ionicon-var-ios7-person-outline; }
+.#{$ionicons-prefix}ios7-personadd:before { content: $ionicon-var-ios7-personadd; }
+.#{$ionicons-prefix}ios7-personadd-outline:before { content: $ionicon-var-ios7-personadd-outline; }
+.#{$ionicons-prefix}ios7-photos:before { content: $ionicon-var-ios7-photos; }
+.#{$ionicons-prefix}ios7-photos-outline:before { content: $ionicon-var-ios7-photos-outline; }
+.#{$ionicons-prefix}ios7-pie:before { content: $ionicon-var-ios7-pie; }
+.#{$ionicons-prefix}ios7-pie-outline:before { content: $ionicon-var-ios7-pie-outline; }
+.#{$ionicons-prefix}ios7-play:before { content: $ionicon-var-ios7-play; }
+.#{$ionicons-prefix}ios7-play-outline:before { content: $ionicon-var-ios7-play-outline; }
+.#{$ionicons-prefix}ios7-plus:before { content: $ionicon-var-ios7-plus; }
+.#{$ionicons-prefix}ios7-plus-empty:before { content: $ionicon-var-ios7-plus-empty; }
+.#{$ionicons-prefix}ios7-plus-outline:before { content: $ionicon-var-ios7-plus-outline; }
+.#{$ionicons-prefix}ios7-pricetag:before { content: $ionicon-var-ios7-pricetag; }
+.#{$ionicons-prefix}ios7-pricetag-outline:before { content: $ionicon-var-ios7-pricetag-outline; }
+.#{$ionicons-prefix}ios7-pricetags:before { content: $ionicon-var-ios7-pricetags; }
+.#{$ionicons-prefix}ios7-pricetags-outline:before { content: $ionicon-var-ios7-pricetags-outline; }
+.#{$ionicons-prefix}ios7-printer:before { content: $ionicon-var-ios7-printer; }
+.#{$ionicons-prefix}ios7-printer-outline:before { content: $ionicon-var-ios7-printer-outline; }
+.#{$ionicons-prefix}ios7-pulse:before { content: $ionicon-var-ios7-pulse; }
+.#{$ionicons-prefix}ios7-pulse-strong:before { content: $ionicon-var-ios7-pulse-strong; }
+.#{$ionicons-prefix}ios7-rainy:before { content: $ionicon-var-ios7-rainy; }
+.#{$ionicons-prefix}ios7-rainy-outline:before { content: $ionicon-var-ios7-rainy-outline; }
+.#{$ionicons-prefix}ios7-recording:before { content: $ionicon-var-ios7-recording; }
+.#{$ionicons-prefix}ios7-recording-outline:before { content: $ionicon-var-ios7-recording-outline; }
+.#{$ionicons-prefix}ios7-redo:before { content: $ionicon-var-ios7-redo; }
+.#{$ionicons-prefix}ios7-redo-outline:before { content: $ionicon-var-ios7-redo-outline; }
+.#{$ionicons-prefix}ios7-refresh:before { content: $ionicon-var-ios7-refresh; }
+.#{$ionicons-prefix}ios7-refresh-empty:before { content: $ionicon-var-ios7-refresh-empty; }
+.#{$ionicons-prefix}ios7-refresh-outline:before { content: $ionicon-var-ios7-refresh-outline; }
+.#{$ionicons-prefix}ios7-reload:before { content: $ionicon-var-ios7-reload; }
+.#{$ionicons-prefix}ios7-reverse-camera:before { content: $ionicon-var-ios7-reverse-camera; }
+.#{$ionicons-prefix}ios7-reverse-camera-outline:before { content: $ionicon-var-ios7-reverse-camera-outline; }
+.#{$ionicons-prefix}ios7-rewind:before { content: $ionicon-var-ios7-rewind; }
+.#{$ionicons-prefix}ios7-rewind-outline:before { content: $ionicon-var-ios7-rewind-outline; }
+.#{$ionicons-prefix}ios7-search:before { content: $ionicon-var-ios7-search; }
+.#{$ionicons-prefix}ios7-search-strong:before { content: $ionicon-var-ios7-search-strong; }
+.#{$ionicons-prefix}ios7-settings:before { content: $ionicon-var-ios7-settings; }
+.#{$ionicons-prefix}ios7-settings-strong:before { content: $ionicon-var-ios7-settings-strong; }
+.#{$ionicons-prefix}ios7-shrink:before { content: $ionicon-var-ios7-shrink; }
+.#{$ionicons-prefix}ios7-skipbackward:before { content: $ionicon-var-ios7-skipbackward; }
+.#{$ionicons-prefix}ios7-skipbackward-outline:before { content: $ionicon-var-ios7-skipbackward-outline; }
+.#{$ionicons-prefix}ios7-skipforward:before { content: $ionicon-var-ios7-skipforward; }
+.#{$ionicons-prefix}ios7-skipforward-outline:before { content: $ionicon-var-ios7-skipforward-outline; }
+.#{$ionicons-prefix}ios7-snowy:before { content: $ionicon-var-ios7-snowy; }
+.#{$ionicons-prefix}ios7-speedometer:before { content: $ionicon-var-ios7-speedometer; }
+.#{$ionicons-prefix}ios7-speedometer-outline:before { content: $ionicon-var-ios7-speedometer-outline; }
+.#{$ionicons-prefix}ios7-star:before { content: $ionicon-var-ios7-star; }
+.#{$ionicons-prefix}ios7-star-half:before { content: $ionicon-var-ios7-star-half; }
+.#{$ionicons-prefix}ios7-star-outline:before { content: $ionicon-var-ios7-star-outline; }
+.#{$ionicons-prefix}ios7-stopwatch:before { content: $ionicon-var-ios7-stopwatch; }
+.#{$ionicons-prefix}ios7-stopwatch-outline:before { content: $ionicon-var-ios7-stopwatch-outline; }
+.#{$ionicons-prefix}ios7-sunny:before { content: $ionicon-var-ios7-sunny; }
+.#{$ionicons-prefix}ios7-sunny-outline:before { content: $ionicon-var-ios7-sunny-outline; }
+.#{$ionicons-prefix}ios7-telephone:before { content: $ionicon-var-ios7-telephone; }
+.#{$ionicons-prefix}ios7-telephone-outline:before { content: $ionicon-var-ios7-telephone-outline; }
+.#{$ionicons-prefix}ios7-tennisball:before { content: $ionicon-var-ios7-tennisball; }
+.#{$ionicons-prefix}ios7-tennisball-outline:before { content: $ionicon-var-ios7-tennisball-outline; }
+.#{$ionicons-prefix}ios7-thunderstorm:before { content: $ionicon-var-ios7-thunderstorm; }
+.#{$ionicons-prefix}ios7-thunderstorm-outline:before { content: $ionicon-var-ios7-thunderstorm-outline; }
+.#{$ionicons-prefix}ios7-time:before { content: $ionicon-var-ios7-time; }
+.#{$ionicons-prefix}ios7-time-outline:before { content: $ionicon-var-ios7-time-outline; }
+.#{$ionicons-prefix}ios7-timer:before { content: $ionicon-var-ios7-timer; }
+.#{$ionicons-prefix}ios7-timer-outline:before { content: $ionicon-var-ios7-timer-outline; }
+.#{$ionicons-prefix}ios7-toggle:before { content: $ionicon-var-ios7-toggle; }
+.#{$ionicons-prefix}ios7-toggle-outline:before { content: $ionicon-var-ios7-toggle-outline; }
+.#{$ionicons-prefix}ios7-trash:before { content: $ionicon-var-ios7-trash; }
+.#{$ionicons-prefix}ios7-trash-outline:before { content: $ionicon-var-ios7-trash-outline; }
+.#{$ionicons-prefix}ios7-undo:before { content: $ionicon-var-ios7-undo; }
+.#{$ionicons-prefix}ios7-undo-outline:before { content: $ionicon-var-ios7-undo-outline; }
+.#{$ionicons-prefix}ios7-unlocked:before { content: $ionicon-var-ios7-unlocked; }
+.#{$ionicons-prefix}ios7-unlocked-outline:before { content: $ionicon-var-ios7-unlocked-outline; }
+.#{$ionicons-prefix}ios7-upload:before { content: $ionicon-var-ios7-upload; }
+.#{$ionicons-prefix}ios7-upload-outline:before { content: $ionicon-var-ios7-upload-outline; }
+.#{$ionicons-prefix}ios7-videocam:before { content: $ionicon-var-ios7-videocam; }
+.#{$ionicons-prefix}ios7-videocam-outline:before { content: $ionicon-var-ios7-videocam-outline; }
+.#{$ionicons-prefix}ios7-volume-high:before { content: $ionicon-var-ios7-volume-high; }
+.#{$ionicons-prefix}ios7-volume-low:before { content: $ionicon-var-ios7-volume-low; }
+.#{$ionicons-prefix}ios7-wineglass:before { content: $ionicon-var-ios7-wineglass; }
+.#{$ionicons-prefix}ios7-wineglass-outline:before { content: $ionicon-var-ios7-wineglass-outline; }
+.#{$ionicons-prefix}ios7-world:before { content: $ionicon-var-ios7-world; }
+.#{$ionicons-prefix}ios7-world-outline:before { content: $ionicon-var-ios7-world-outline; }
+.#{$ionicons-prefix}ipad:before { content: $ionicon-var-ipad; }
+.#{$ionicons-prefix}iphone:before { content: $ionicon-var-iphone; }
+.#{$ionicons-prefix}ipod:before { content: $ionicon-var-ipod; }
+.#{$ionicons-prefix}jet:before { content: $ionicon-var-jet; }
+.#{$ionicons-prefix}key:before { content: $ionicon-var-key; }
+.#{$ionicons-prefix}knife:before { content: $ionicon-var-knife; }
+.#{$ionicons-prefix}laptop:before { content: $ionicon-var-laptop; }
+.#{$ionicons-prefix}leaf:before { content: $ionicon-var-leaf; }
+.#{$ionicons-prefix}levels:before { content: $ionicon-var-levels; }
+.#{$ionicons-prefix}lightbulb:before { content: $ionicon-var-lightbulb; }
+.#{$ionicons-prefix}link:before { content: $ionicon-var-link; }
+.#{$ionicons-prefix}load-a:before { content: $ionicon-var-load-a; }
+.#{$ionicons-prefix}load-b:before { content: $ionicon-var-load-b; }
+.#{$ionicons-prefix}load-c:before { content: $ionicon-var-load-c; }
+.#{$ionicons-prefix}load-d:before { content: $ionicon-var-load-d; }
+.#{$ionicons-prefix}location:before { content: $ionicon-var-location; }
+.#{$ionicons-prefix}locked:before { content: $ionicon-var-locked; }
+.#{$ionicons-prefix}log-in:before { content: $ionicon-var-log-in; }
+.#{$ionicons-prefix}log-out:before { content: $ionicon-var-log-out; }
+.#{$ionicons-prefix}loop:before { content: $ionicon-var-loop; }
+.#{$ionicons-prefix}magnet:before { content: $ionicon-var-magnet; }
+.#{$ionicons-prefix}male:before { content: $ionicon-var-male; }
+.#{$ionicons-prefix}man:before { content: $ionicon-var-man; }
+.#{$ionicons-prefix}map:before { content: $ionicon-var-map; }
+.#{$ionicons-prefix}medkit:before { content: $ionicon-var-medkit; }
+.#{$ionicons-prefix}merge:before { content: $ionicon-var-merge; }
+.#{$ionicons-prefix}mic-a:before { content: $ionicon-var-mic-a; }
+.#{$ionicons-prefix}mic-b:before { content: $ionicon-var-mic-b; }
+.#{$ionicons-prefix}mic-c:before { content: $ionicon-var-mic-c; }
+.#{$ionicons-prefix}minus:before { content: $ionicon-var-minus; }
+.#{$ionicons-prefix}minus-circled:before { content: $ionicon-var-minus-circled; }
+.#{$ionicons-prefix}minus-round:before { content: $ionicon-var-minus-round; }
+.#{$ionicons-prefix}model-s:before { content: $ionicon-var-model-s; }
+.#{$ionicons-prefix}monitor:before { content: $ionicon-var-monitor; }
+.#{$ionicons-prefix}more:before { content: $ionicon-var-more; }
+.#{$ionicons-prefix}mouse:before { content: $ionicon-var-mouse; }
+.#{$ionicons-prefix}music-note:before { content: $ionicon-var-music-note; }
+.#{$ionicons-prefix}navicon:before { content: $ionicon-var-navicon; }
+.#{$ionicons-prefix}navicon-round:before { content: $ionicon-var-navicon-round; }
+.#{$ionicons-prefix}navigate:before { content: $ionicon-var-navigate; }
+.#{$ionicons-prefix}network:before { content: $ionicon-var-network; }
+.#{$ionicons-prefix}no-smoking:before { content: $ionicon-var-no-smoking; }
+.#{$ionicons-prefix}nuclear:before { content: $ionicon-var-nuclear; }
+.#{$ionicons-prefix}outlet:before { content: $ionicon-var-outlet; }
+.#{$ionicons-prefix}paper-airplane:before { content: $ionicon-var-paper-airplane; }
+.#{$ionicons-prefix}paperclip:before { content: $ionicon-var-paperclip; }
+.#{$ionicons-prefix}pause:before { content: $ionicon-var-pause; }
+.#{$ionicons-prefix}person:before { content: $ionicon-var-person; }
+.#{$ionicons-prefix}person-add:before { content: $ionicon-var-person-add; }
+.#{$ionicons-prefix}person-stalker:before { content: $ionicon-var-person-stalker; }
+.#{$ionicons-prefix}pie-graph:before { content: $ionicon-var-pie-graph; }
+.#{$ionicons-prefix}pin:before { content: $ionicon-var-pin; }
+.#{$ionicons-prefix}pinpoint:before { content: $ionicon-var-pinpoint; }
+.#{$ionicons-prefix}pizza:before { content: $ionicon-var-pizza; }
+.#{$ionicons-prefix}plane:before { content: $ionicon-var-plane; }
+.#{$ionicons-prefix}planet:before { content: $ionicon-var-planet; }
+.#{$ionicons-prefix}play:before { content: $ionicon-var-play; }
+.#{$ionicons-prefix}playstation:before { content: $ionicon-var-playstation; }
+.#{$ionicons-prefix}plus:before { content: $ionicon-var-plus; }
+.#{$ionicons-prefix}plus-circled:before { content: $ionicon-var-plus-circled; }
+.#{$ionicons-prefix}plus-round:before { content: $ionicon-var-plus-round; }
+.#{$ionicons-prefix}podium:before { content: $ionicon-var-podium; }
+.#{$ionicons-prefix}pound:before { content: $ionicon-var-pound; }
+.#{$ionicons-prefix}power:before { content: $ionicon-var-power; }
+.#{$ionicons-prefix}pricetag:before { content: $ionicon-var-pricetag; }
+.#{$ionicons-prefix}pricetags:before { content: $ionicon-var-pricetags; }
+.#{$ionicons-prefix}printer:before { content: $ionicon-var-printer; }
+.#{$ionicons-prefix}pull-request:before { content: $ionicon-var-pull-request; }
+.#{$ionicons-prefix}qr-scanner:before { content: $ionicon-var-qr-scanner; }
+.#{$ionicons-prefix}quote:before { content: $ionicon-var-quote; }
+.#{$ionicons-prefix}radio-waves:before { content: $ionicon-var-radio-waves; }
+.#{$ionicons-prefix}record:before { content: $ionicon-var-record; }
+.#{$ionicons-prefix}refresh:before { content: $ionicon-var-refresh; }
+.#{$ionicons-prefix}reply:before { content: $ionicon-var-reply; }
+.#{$ionicons-prefix}reply-all:before { content: $ionicon-var-reply-all; }
+.#{$ionicons-prefix}ribbon-a:before { content: $ionicon-var-ribbon-a; }
+.#{$ionicons-prefix}ribbon-b:before { content: $ionicon-var-ribbon-b; }
+.#{$ionicons-prefix}sad:before { content: $ionicon-var-sad; }
+.#{$ionicons-prefix}scissors:before { content: $ionicon-var-scissors; }
+.#{$ionicons-prefix}search:before { content: $ionicon-var-search; }
+.#{$ionicons-prefix}settings:before { content: $ionicon-var-settings; }
+.#{$ionicons-prefix}share:before { content: $ionicon-var-share; }
+.#{$ionicons-prefix}shuffle:before { content: $ionicon-var-shuffle; }
+.#{$ionicons-prefix}skip-backward:before { content: $ionicon-var-skip-backward; }
+.#{$ionicons-prefix}skip-forward:before { content: $ionicon-var-skip-forward; }
+.#{$ionicons-prefix}social-android:before { content: $ionicon-var-social-android; }
+.#{$ionicons-prefix}social-android-outline:before { content: $ionicon-var-social-android-outline; }
+.#{$ionicons-prefix}social-apple:before { content: $ionicon-var-social-apple; }
+.#{$ionicons-prefix}social-apple-outline:before { content: $ionicon-var-social-apple-outline; }
+.#{$ionicons-prefix}social-bitcoin:before { content: $ionicon-var-social-bitcoin; }
+.#{$ionicons-prefix}social-bitcoin-outline:before { content: $ionicon-var-social-bitcoin-outline; }
+.#{$ionicons-prefix}social-buffer:before { content: $ionicon-var-social-buffer; }
+.#{$ionicons-prefix}social-buffer-outline:before { content: $ionicon-var-social-buffer-outline; }
+.#{$ionicons-prefix}social-designernews:before { content: $ionicon-var-social-designernews; }
+.#{$ionicons-prefix}social-designernews-outline:before { content: $ionicon-var-social-designernews-outline; }
+.#{$ionicons-prefix}social-dribbble:before { content: $ionicon-var-social-dribbble; }
+.#{$ionicons-prefix}social-dribbble-outline:before { content: $ionicon-var-social-dribbble-outline; }
+.#{$ionicons-prefix}social-dropbox:before { content: $ionicon-var-social-dropbox; }
+.#{$ionicons-prefix}social-dropbox-outline:before { content: $ionicon-var-social-dropbox-outline; }
+.#{$ionicons-prefix}social-facebook:before { content: $ionicon-var-social-facebook; }
+.#{$ionicons-prefix}social-facebook-outline:before { content: $ionicon-var-social-facebook-outline; }
+.#{$ionicons-prefix}social-foursquare:before { content: $ionicon-var-social-foursquare; }
+.#{$ionicons-prefix}social-foursquare-outline:before { content: $ionicon-var-social-foursquare-outline; }
+.#{$ionicons-prefix}social-freebsd-devil:before { content: $ionicon-var-social-freebsd-devil; }
+.#{$ionicons-prefix}social-github:before { content: $ionicon-var-social-github; }
+.#{$ionicons-prefix}social-github-outline:before { content: $ionicon-var-social-github-outline; }
+.#{$ionicons-prefix}social-google:before { content: $ionicon-var-social-google; }
+.#{$ionicons-prefix}social-google-outline:before { content: $ionicon-var-social-google-outline; }
+.#{$ionicons-prefix}social-googleplus:before { content: $ionicon-var-social-googleplus; }
+.#{$ionicons-prefix}social-googleplus-outline:before { content: $ionicon-var-social-googleplus-outline; }
+.#{$ionicons-prefix}social-hackernews:before { content: $ionicon-var-social-hackernews; }
+.#{$ionicons-prefix}social-hackernews-outline:before { content: $ionicon-var-social-hackernews-outline; }
+.#{$ionicons-prefix}social-instagram:before { content: $ionicon-var-social-instagram; }
+.#{$ionicons-prefix}social-instagram-outline:before { content: $ionicon-var-social-instagram-outline; }
+.#{$ionicons-prefix}social-linkedin:before { content: $ionicon-var-social-linkedin; }
+.#{$ionicons-prefix}social-linkedin-outline:before { content: $ionicon-var-social-linkedin-outline; }
+.#{$ionicons-prefix}social-pinterest:before { content: $ionicon-var-social-pinterest; }
+.#{$ionicons-prefix}social-pinterest-outline:before { content: $ionicon-var-social-pinterest-outline; }
+.#{$ionicons-prefix}social-reddit:before { content: $ionicon-var-social-reddit; }
+.#{$ionicons-prefix}social-reddit-outline:before { content: $ionicon-var-social-reddit-outline; }
+.#{$ionicons-prefix}social-rss:before { content: $ionicon-var-social-rss; }
+.#{$ionicons-prefix}social-rss-outline:before { content: $ionicon-var-social-rss-outline; }
+.#{$ionicons-prefix}social-skype:before { content: $ionicon-var-social-skype; }
+.#{$ionicons-prefix}social-skype-outline:before { content: $ionicon-var-social-skype-outline; }
+.#{$ionicons-prefix}social-tumblr:before { content: $ionicon-var-social-tumblr; }
+.#{$ionicons-prefix}social-tumblr-outline:before { content: $ionicon-var-social-tumblr-outline; }
+.#{$ionicons-prefix}social-tux:before { content: $ionicon-var-social-tux; }
+.#{$ionicons-prefix}social-twitter:before { content: $ionicon-var-social-twitter; }
+.#{$ionicons-prefix}social-twitter-outline:before { content: $ionicon-var-social-twitter-outline; }
+.#{$ionicons-prefix}social-usd:before { content: $ionicon-var-social-usd; }
+.#{$ionicons-prefix}social-usd-outline:before { content: $ionicon-var-social-usd-outline; }
+.#{$ionicons-prefix}social-vimeo:before { content: $ionicon-var-social-vimeo; }
+.#{$ionicons-prefix}social-vimeo-outline:before { content: $ionicon-var-social-vimeo-outline; }
+.#{$ionicons-prefix}social-windows:before { content: $ionicon-var-social-windows; }
+.#{$ionicons-prefix}social-windows-outline:before { content: $ionicon-var-social-windows-outline; }
+.#{$ionicons-prefix}social-wordpress:before { content: $ionicon-var-social-wordpress; }
+.#{$ionicons-prefix}social-wordpress-outline:before { content: $ionicon-var-social-wordpress-outline; }
+.#{$ionicons-prefix}social-yahoo:before { content: $ionicon-var-social-yahoo; }
+.#{$ionicons-prefix}social-yahoo-outline:before { content: $ionicon-var-social-yahoo-outline; }
+.#{$ionicons-prefix}social-youtube:before { content: $ionicon-var-social-youtube; }
+.#{$ionicons-prefix}social-youtube-outline:before { content: $ionicon-var-social-youtube-outline; }
+.#{$ionicons-prefix}speakerphone:before { content: $ionicon-var-speakerphone; }
+.#{$ionicons-prefix}speedometer:before { content: $ionicon-var-speedometer; }
+.#{$ionicons-prefix}spoon:before { content: $ionicon-var-spoon; }
+.#{$ionicons-prefix}star:before { content: $ionicon-var-star; }
+.#{$ionicons-prefix}stats-bars:before { content: $ionicon-var-stats-bars; }
+.#{$ionicons-prefix}steam:before { content: $ionicon-var-steam; }
+.#{$ionicons-prefix}stop:before { content: $ionicon-var-stop; }
+.#{$ionicons-prefix}thermometer:before { content: $ionicon-var-thermometer; }
+.#{$ionicons-prefix}thumbsdown:before { content: $ionicon-var-thumbsdown; }
+.#{$ionicons-prefix}thumbsup:before { content: $ionicon-var-thumbsup; }
+.#{$ionicons-prefix}toggle:before { content: $ionicon-var-toggle; }
+.#{$ionicons-prefix}toggle-filled:before { content: $ionicon-var-toggle-filled; }
+.#{$ionicons-prefix}trash-a:before { content: $ionicon-var-trash-a; }
+.#{$ionicons-prefix}trash-b:before { content: $ionicon-var-trash-b; }
+.#{$ionicons-prefix}trophy:before { content: $ionicon-var-trophy; }
+.#{$ionicons-prefix}umbrella:before { content: $ionicon-var-umbrella; }
+.#{$ionicons-prefix}university:before { content: $ionicon-var-university; }
+.#{$ionicons-prefix}unlocked:before { content: $ionicon-var-unlocked; }
+.#{$ionicons-prefix}upload:before { content: $ionicon-var-upload; }
+.#{$ionicons-prefix}usb:before { content: $ionicon-var-usb; }
+.#{$ionicons-prefix}videocamera:before { content: $ionicon-var-videocamera; }
+.#{$ionicons-prefix}volume-high:before { content: $ionicon-var-volume-high; }
+.#{$ionicons-prefix}volume-low:before { content: $ionicon-var-volume-low; }
+.#{$ionicons-prefix}volume-medium:before { content: $ionicon-var-volume-medium; }
+.#{$ionicons-prefix}volume-mute:before { content: $ionicon-var-volume-mute; }
+.#{$ionicons-prefix}wand:before { content: $ionicon-var-wand; }
+.#{$ionicons-prefix}waterdrop:before { content: $ionicon-var-waterdrop; }
+.#{$ionicons-prefix}wifi:before { content: $ionicon-var-wifi; }
+.#{$ionicons-prefix}wineglass:before { content: $ionicon-var-wineglass; }
+.#{$ionicons-prefix}woman:before { content: $ionicon-var-woman; }
+.#{$ionicons-prefix}wrench:before { content: $ionicon-var-wrench; }
+.#{$ionicons-prefix}xbox:before { content: $ionicon-var-xbox; }
\ No newline at end of file
diff --git a/client/lib/ionic/scss/ionicons/_ionicons-variables.scss b/client/lib/ionic/scss/ionicons/_ionicons-variables.scss
new file mode 100644
index 0000000..2f5425b
--- /dev/null
+++ b/client/lib/ionic/scss/ionicons/_ionicons-variables.scss
@@ -0,0 +1,609 @@
+// Ionicons Variables
+// --------------------------
+
+$ionicons-font-path: "../fonts" !default;
+$ionicons-font-family: "Ionicons" !default;
+$ionicons-version: "1.5.2" !default;
+$ionicons-prefix: ion- !default;
+
+$ionicon-var-alert: "\f101";
+$ionicon-var-alert-circled: "\f100";
+$ionicon-var-android-add: "\f2c7";
+$ionicon-var-android-add-contact: "\f2c6";
+$ionicon-var-android-alarm: "\f2c8";
+$ionicon-var-android-archive: "\f2c9";
+$ionicon-var-android-arrow-back: "\f2ca";
+$ionicon-var-android-arrow-down-left: "\f2cb";
+$ionicon-var-android-arrow-down-right: "\f2cc";
+$ionicon-var-android-arrow-forward: "\f30f";
+$ionicon-var-android-arrow-up-left: "\f2cd";
+$ionicon-var-android-arrow-up-right: "\f2ce";
+$ionicon-var-android-battery: "\f2cf";
+$ionicon-var-android-book: "\f2d0";
+$ionicon-var-android-calendar: "\f2d1";
+$ionicon-var-android-call: "\f2d2";
+$ionicon-var-android-camera: "\f2d3";
+$ionicon-var-android-chat: "\f2d4";
+$ionicon-var-android-checkmark: "\f2d5";
+$ionicon-var-android-clock: "\f2d6";
+$ionicon-var-android-close: "\f2d7";
+$ionicon-var-android-contact: "\f2d8";
+$ionicon-var-android-contacts: "\f2d9";
+$ionicon-var-android-data: "\f2da";
+$ionicon-var-android-developer: "\f2db";
+$ionicon-var-android-display: "\f2dc";
+$ionicon-var-android-download: "\f2dd";
+$ionicon-var-android-drawer: "\f310";
+$ionicon-var-android-dropdown: "\f2de";
+$ionicon-var-android-earth: "\f2df";
+$ionicon-var-android-folder: "\f2e0";
+$ionicon-var-android-forums: "\f2e1";
+$ionicon-var-android-friends: "\f2e2";
+$ionicon-var-android-hand: "\f2e3";
+$ionicon-var-android-image: "\f2e4";
+$ionicon-var-android-inbox: "\f2e5";
+$ionicon-var-android-information: "\f2e6";
+$ionicon-var-android-keypad: "\f2e7";
+$ionicon-var-android-lightbulb: "\f2e8";
+$ionicon-var-android-locate: "\f2e9";
+$ionicon-var-android-location: "\f2ea";
+$ionicon-var-android-mail: "\f2eb";
+$ionicon-var-android-microphone: "\f2ec";
+$ionicon-var-android-mixer: "\f2ed";
+$ionicon-var-android-more: "\f2ee";
+$ionicon-var-android-note: "\f2ef";
+$ionicon-var-android-playstore: "\f2f0";
+$ionicon-var-android-printer: "\f2f1";
+$ionicon-var-android-promotion: "\f2f2";
+$ionicon-var-android-reminder: "\f2f3";
+$ionicon-var-android-remove: "\f2f4";
+$ionicon-var-android-search: "\f2f5";
+$ionicon-var-android-send: "\f2f6";
+$ionicon-var-android-settings: "\f2f7";
+$ionicon-var-android-share: "\f2f8";
+$ionicon-var-android-social: "\f2fa";
+$ionicon-var-android-social-user: "\f2f9";
+$ionicon-var-android-sort: "\f2fb";
+$ionicon-var-android-stair-drawer: "\f311";
+$ionicon-var-android-star: "\f2fc";
+$ionicon-var-android-stopwatch: "\f2fd";
+$ionicon-var-android-storage: "\f2fe";
+$ionicon-var-android-system-back: "\f2ff";
+$ionicon-var-android-system-home: "\f300";
+$ionicon-var-android-system-windows: "\f301";
+$ionicon-var-android-timer: "\f302";
+$ionicon-var-android-trash: "\f303";
+$ionicon-var-android-user-menu: "\f312";
+$ionicon-var-android-volume: "\f304";
+$ionicon-var-android-wifi: "\f305";
+$ionicon-var-aperture: "\f313";
+$ionicon-var-archive: "\f102";
+$ionicon-var-arrow-down-a: "\f103";
+$ionicon-var-arrow-down-b: "\f104";
+$ionicon-var-arrow-down-c: "\f105";
+$ionicon-var-arrow-expand: "\f25e";
+$ionicon-var-arrow-graph-down-left: "\f25f";
+$ionicon-var-arrow-graph-down-right: "\f260";
+$ionicon-var-arrow-graph-up-left: "\f261";
+$ionicon-var-arrow-graph-up-right: "\f262";
+$ionicon-var-arrow-left-a: "\f106";
+$ionicon-var-arrow-left-b: "\f107";
+$ionicon-var-arrow-left-c: "\f108";
+$ionicon-var-arrow-move: "\f263";
+$ionicon-var-arrow-resize: "\f264";
+$ionicon-var-arrow-return-left: "\f265";
+$ionicon-var-arrow-return-right: "\f266";
+$ionicon-var-arrow-right-a: "\f109";
+$ionicon-var-arrow-right-b: "\f10a";
+$ionicon-var-arrow-right-c: "\f10b";
+$ionicon-var-arrow-shrink: "\f267";
+$ionicon-var-arrow-swap: "\f268";
+$ionicon-var-arrow-up-a: "\f10c";
+$ionicon-var-arrow-up-b: "\f10d";
+$ionicon-var-arrow-up-c: "\f10e";
+$ionicon-var-asterisk: "\f314";
+$ionicon-var-at: "\f10f";
+$ionicon-var-bag: "\f110";
+$ionicon-var-battery-charging: "\f111";
+$ionicon-var-battery-empty: "\f112";
+$ionicon-var-battery-full: "\f113";
+$ionicon-var-battery-half: "\f114";
+$ionicon-var-battery-low: "\f115";
+$ionicon-var-beaker: "\f269";
+$ionicon-var-beer: "\f26a";
+$ionicon-var-bluetooth: "\f116";
+$ionicon-var-bonfire: "\f315";
+$ionicon-var-bookmark: "\f26b";
+$ionicon-var-briefcase: "\f26c";
+$ionicon-var-bug: "\f2be";
+$ionicon-var-calculator: "\f26d";
+$ionicon-var-calendar: "\f117";
+$ionicon-var-camera: "\f118";
+$ionicon-var-card: "\f119";
+$ionicon-var-cash: "\f316";
+$ionicon-var-chatbox: "\f11b";
+$ionicon-var-chatbox-working: "\f11a";
+$ionicon-var-chatboxes: "\f11c";
+$ionicon-var-chatbubble: "\f11e";
+$ionicon-var-chatbubble-working: "\f11d";
+$ionicon-var-chatbubbles: "\f11f";
+$ionicon-var-checkmark: "\f122";
+$ionicon-var-checkmark-circled: "\f120";
+$ionicon-var-checkmark-round: "\f121";
+$ionicon-var-chevron-down: "\f123";
+$ionicon-var-chevron-left: "\f124";
+$ionicon-var-chevron-right: "\f125";
+$ionicon-var-chevron-up: "\f126";
+$ionicon-var-clipboard: "\f127";
+$ionicon-var-clock: "\f26e";
+$ionicon-var-close: "\f12a";
+$ionicon-var-close-circled: "\f128";
+$ionicon-var-close-round: "\f129";
+$ionicon-var-closed-captioning: "\f317";
+$ionicon-var-cloud: "\f12b";
+$ionicon-var-code: "\f271";
+$ionicon-var-code-download: "\f26f";
+$ionicon-var-code-working: "\f270";
+$ionicon-var-coffee: "\f272";
+$ionicon-var-compass: "\f273";
+$ionicon-var-compose: "\f12c";
+$ionicon-var-connection-bars: "\f274";
+$ionicon-var-contrast: "\f275";
+$ionicon-var-cube: "\f318";
+$ionicon-var-disc: "\f12d";
+$ionicon-var-document: "\f12f";
+$ionicon-var-document-text: "\f12e";
+$ionicon-var-drag: "\f130";
+$ionicon-var-earth: "\f276";
+$ionicon-var-edit: "\f2bf";
+$ionicon-var-egg: "\f277";
+$ionicon-var-eject: "\f131";
+$ionicon-var-email: "\f132";
+$ionicon-var-eye: "\f133";
+$ionicon-var-eye-disabled: "\f306";
+$ionicon-var-female: "\f278";
+$ionicon-var-filing: "\f134";
+$ionicon-var-film-marker: "\f135";
+$ionicon-var-fireball: "\f319";
+$ionicon-var-flag: "\f279";
+$ionicon-var-flame: "\f31a";
+$ionicon-var-flash: "\f137";
+$ionicon-var-flash-off: "\f136";
+$ionicon-var-flask: "\f138";
+$ionicon-var-folder: "\f139";
+$ionicon-var-fork: "\f27a";
+$ionicon-var-fork-repo: "\f2c0";
+$ionicon-var-forward: "\f13a";
+$ionicon-var-funnel: "\f31b";
+$ionicon-var-game-controller-a: "\f13b";
+$ionicon-var-game-controller-b: "\f13c";
+$ionicon-var-gear-a: "\f13d";
+$ionicon-var-gear-b: "\f13e";
+$ionicon-var-grid: "\f13f";
+$ionicon-var-hammer: "\f27b";
+$ionicon-var-happy: "\f31c";
+$ionicon-var-headphone: "\f140";
+$ionicon-var-heart: "\f141";
+$ionicon-var-heart-broken: "\f31d";
+$ionicon-var-help: "\f143";
+$ionicon-var-help-buoy: "\f27c";
+$ionicon-var-help-circled: "\f142";
+$ionicon-var-home: "\f144";
+$ionicon-var-icecream: "\f27d";
+$ionicon-var-icon-social-google-plus: "\f146";
+$ionicon-var-icon-social-google-plus-outline: "\f145";
+$ionicon-var-image: "\f147";
+$ionicon-var-images: "\f148";
+$ionicon-var-information: "\f14a";
+$ionicon-var-information-circled: "\f149";
+$ionicon-var-ionic: "\f14b";
+$ionicon-var-ios7-alarm: "\f14d";
+$ionicon-var-ios7-alarm-outline: "\f14c";
+$ionicon-var-ios7-albums: "\f14f";
+$ionicon-var-ios7-albums-outline: "\f14e";
+$ionicon-var-ios7-americanfootball: "\f31f";
+$ionicon-var-ios7-americanfootball-outline: "\f31e";
+$ionicon-var-ios7-analytics: "\f321";
+$ionicon-var-ios7-analytics-outline: "\f320";
+$ionicon-var-ios7-arrow-back: "\f150";
+$ionicon-var-ios7-arrow-down: "\f151";
+$ionicon-var-ios7-arrow-forward: "\f152";
+$ionicon-var-ios7-arrow-left: "\f153";
+$ionicon-var-ios7-arrow-right: "\f154";
+$ionicon-var-ios7-arrow-thin-down: "\f27e";
+$ionicon-var-ios7-arrow-thin-left: "\f27f";
+$ionicon-var-ios7-arrow-thin-right: "\f280";
+$ionicon-var-ios7-arrow-thin-up: "\f281";
+$ionicon-var-ios7-arrow-up: "\f155";
+$ionicon-var-ios7-at: "\f157";
+$ionicon-var-ios7-at-outline: "\f156";
+$ionicon-var-ios7-barcode: "\f323";
+$ionicon-var-ios7-barcode-outline: "\f322";
+$ionicon-var-ios7-baseball: "\f325";
+$ionicon-var-ios7-baseball-outline: "\f324";
+$ionicon-var-ios7-basketball: "\f327";
+$ionicon-var-ios7-basketball-outline: "\f326";
+$ionicon-var-ios7-bell: "\f159";
+$ionicon-var-ios7-bell-outline: "\f158";
+$ionicon-var-ios7-bolt: "\f15b";
+$ionicon-var-ios7-bolt-outline: "\f15a";
+$ionicon-var-ios7-bookmarks: "\f15d";
+$ionicon-var-ios7-bookmarks-outline: "\f15c";
+$ionicon-var-ios7-box: "\f15f";
+$ionicon-var-ios7-box-outline: "\f15e";
+$ionicon-var-ios7-briefcase: "\f283";
+$ionicon-var-ios7-briefcase-outline: "\f282";
+$ionicon-var-ios7-browsers: "\f161";
+$ionicon-var-ios7-browsers-outline: "\f160";
+$ionicon-var-ios7-calculator: "\f285";
+$ionicon-var-ios7-calculator-outline: "\f284";
+$ionicon-var-ios7-calendar: "\f163";
+$ionicon-var-ios7-calendar-outline: "\f162";
+$ionicon-var-ios7-camera: "\f165";
+$ionicon-var-ios7-camera-outline: "\f164";
+$ionicon-var-ios7-cart: "\f167";
+$ionicon-var-ios7-cart-outline: "\f166";
+$ionicon-var-ios7-chatboxes: "\f169";
+$ionicon-var-ios7-chatboxes-outline: "\f168";
+$ionicon-var-ios7-chatbubble: "\f16b";
+$ionicon-var-ios7-chatbubble-outline: "\f16a";
+$ionicon-var-ios7-checkmark: "\f16e";
+$ionicon-var-ios7-checkmark-empty: "\f16c";
+$ionicon-var-ios7-checkmark-outline: "\f16d";
+$ionicon-var-ios7-circle-filled: "\f16f";
+$ionicon-var-ios7-circle-outline: "\f170";
+$ionicon-var-ios7-clock: "\f172";
+$ionicon-var-ios7-clock-outline: "\f171";
+$ionicon-var-ios7-close: "\f2bc";
+$ionicon-var-ios7-close-empty: "\f2bd";
+$ionicon-var-ios7-close-outline: "\f2bb";
+$ionicon-var-ios7-cloud: "\f178";
+$ionicon-var-ios7-cloud-download: "\f174";
+$ionicon-var-ios7-cloud-download-outline: "\f173";
+$ionicon-var-ios7-cloud-outline: "\f175";
+$ionicon-var-ios7-cloud-upload: "\f177";
+$ionicon-var-ios7-cloud-upload-outline: "\f176";
+$ionicon-var-ios7-cloudy: "\f17a";
+$ionicon-var-ios7-cloudy-night: "\f308";
+$ionicon-var-ios7-cloudy-night-outline: "\f307";
+$ionicon-var-ios7-cloudy-outline: "\f179";
+$ionicon-var-ios7-cog: "\f17c";
+$ionicon-var-ios7-cog-outline: "\f17b";
+$ionicon-var-ios7-compose: "\f17e";
+$ionicon-var-ios7-compose-outline: "\f17d";
+$ionicon-var-ios7-contact: "\f180";
+$ionicon-var-ios7-contact-outline: "\f17f";
+$ionicon-var-ios7-copy: "\f182";
+$ionicon-var-ios7-copy-outline: "\f181";
+$ionicon-var-ios7-download: "\f184";
+$ionicon-var-ios7-download-outline: "\f183";
+$ionicon-var-ios7-drag: "\f185";
+$ionicon-var-ios7-email: "\f187";
+$ionicon-var-ios7-email-outline: "\f186";
+$ionicon-var-ios7-expand: "\f30d";
+$ionicon-var-ios7-eye: "\f189";
+$ionicon-var-ios7-eye-outline: "\f188";
+$ionicon-var-ios7-fastforward: "\f18b";
+$ionicon-var-ios7-fastforward-outline: "\f18a";
+$ionicon-var-ios7-filing: "\f18d";
+$ionicon-var-ios7-filing-outline: "\f18c";
+$ionicon-var-ios7-film: "\f18f";
+$ionicon-var-ios7-film-outline: "\f18e";
+$ionicon-var-ios7-flag: "\f191";
+$ionicon-var-ios7-flag-outline: "\f190";
+$ionicon-var-ios7-folder: "\f193";
+$ionicon-var-ios7-folder-outline: "\f192";
+$ionicon-var-ios7-football: "\f329";
+$ionicon-var-ios7-football-outline: "\f328";
+$ionicon-var-ios7-gear: "\f195";
+$ionicon-var-ios7-gear-outline: "\f194";
+$ionicon-var-ios7-glasses: "\f197";
+$ionicon-var-ios7-glasses-outline: "\f196";
+$ionicon-var-ios7-heart: "\f199";
+$ionicon-var-ios7-heart-outline: "\f198";
+$ionicon-var-ios7-help: "\f19c";
+$ionicon-var-ios7-help-empty: "\f19a";
+$ionicon-var-ios7-help-outline: "\f19b";
+$ionicon-var-ios7-home: "\f32b";
+$ionicon-var-ios7-home-outline: "\f32a";
+$ionicon-var-ios7-infinite: "\f19e";
+$ionicon-var-ios7-infinite-outline: "\f19d";
+$ionicon-var-ios7-information: "\f1a1";
+$ionicon-var-ios7-information-empty: "\f19f";
+$ionicon-var-ios7-information-outline: "\f1a0";
+$ionicon-var-ios7-ionic-outline: "\f1a2";
+$ionicon-var-ios7-keypad: "\f1a4";
+$ionicon-var-ios7-keypad-outline: "\f1a3";
+$ionicon-var-ios7-lightbulb: "\f287";
+$ionicon-var-ios7-lightbulb-outline: "\f286";
+$ionicon-var-ios7-location: "\f1a6";
+$ionicon-var-ios7-location-outline: "\f1a5";
+$ionicon-var-ios7-locked: "\f1a8";
+$ionicon-var-ios7-locked-outline: "\f1a7";
+$ionicon-var-ios7-loop: "\f32d";
+$ionicon-var-ios7-loop-strong: "\f32c";
+$ionicon-var-ios7-medkit: "\f289";
+$ionicon-var-ios7-medkit-outline: "\f288";
+$ionicon-var-ios7-mic: "\f1ab";
+$ionicon-var-ios7-mic-off: "\f1a9";
+$ionicon-var-ios7-mic-outline: "\f1aa";
+$ionicon-var-ios7-minus: "\f1ae";
+$ionicon-var-ios7-minus-empty: "\f1ac";
+$ionicon-var-ios7-minus-outline: "\f1ad";
+$ionicon-var-ios7-monitor: "\f1b0";
+$ionicon-var-ios7-monitor-outline: "\f1af";
+$ionicon-var-ios7-moon: "\f1b2";
+$ionicon-var-ios7-moon-outline: "\f1b1";
+$ionicon-var-ios7-more: "\f1b4";
+$ionicon-var-ios7-more-outline: "\f1b3";
+$ionicon-var-ios7-musical-note: "\f1b5";
+$ionicon-var-ios7-musical-notes: "\f1b6";
+$ionicon-var-ios7-navigate: "\f1b8";
+$ionicon-var-ios7-navigate-outline: "\f1b7";
+$ionicon-var-ios7-paper: "\f32f";
+$ionicon-var-ios7-paper-outline: "\f32e";
+$ionicon-var-ios7-paperplane: "\f1ba";
+$ionicon-var-ios7-paperplane-outline: "\f1b9";
+$ionicon-var-ios7-partlysunny: "\f1bc";
+$ionicon-var-ios7-partlysunny-outline: "\f1bb";
+$ionicon-var-ios7-pause: "\f1be";
+$ionicon-var-ios7-pause-outline: "\f1bd";
+$ionicon-var-ios7-paw: "\f331";
+$ionicon-var-ios7-paw-outline: "\f330";
+$ionicon-var-ios7-people: "\f1c0";
+$ionicon-var-ios7-people-outline: "\f1bf";
+$ionicon-var-ios7-person: "\f1c2";
+$ionicon-var-ios7-person-outline: "\f1c1";
+$ionicon-var-ios7-personadd: "\f1c4";
+$ionicon-var-ios7-personadd-outline: "\f1c3";
+$ionicon-var-ios7-photos: "\f1c6";
+$ionicon-var-ios7-photos-outline: "\f1c5";
+$ionicon-var-ios7-pie: "\f28b";
+$ionicon-var-ios7-pie-outline: "\f28a";
+$ionicon-var-ios7-play: "\f1c8";
+$ionicon-var-ios7-play-outline: "\f1c7";
+$ionicon-var-ios7-plus: "\f1cb";
+$ionicon-var-ios7-plus-empty: "\f1c9";
+$ionicon-var-ios7-plus-outline: "\f1ca";
+$ionicon-var-ios7-pricetag: "\f28d";
+$ionicon-var-ios7-pricetag-outline: "\f28c";
+$ionicon-var-ios7-pricetags: "\f333";
+$ionicon-var-ios7-pricetags-outline: "\f332";
+$ionicon-var-ios7-printer: "\f1cd";
+$ionicon-var-ios7-printer-outline: "\f1cc";
+$ionicon-var-ios7-pulse: "\f335";
+$ionicon-var-ios7-pulse-strong: "\f334";
+$ionicon-var-ios7-rainy: "\f1cf";
+$ionicon-var-ios7-rainy-outline: "\f1ce";
+$ionicon-var-ios7-recording: "\f1d1";
+$ionicon-var-ios7-recording-outline: "\f1d0";
+$ionicon-var-ios7-redo: "\f1d3";
+$ionicon-var-ios7-redo-outline: "\f1d2";
+$ionicon-var-ios7-refresh: "\f1d6";
+$ionicon-var-ios7-refresh-empty: "\f1d4";
+$ionicon-var-ios7-refresh-outline: "\f1d5";
+$ionicon-var-ios7-reload: "\f28e";
+$ionicon-var-ios7-reverse-camera: "\f337";
+$ionicon-var-ios7-reverse-camera-outline: "\f336";
+$ionicon-var-ios7-rewind: "\f1d8";
+$ionicon-var-ios7-rewind-outline: "\f1d7";
+$ionicon-var-ios7-search: "\f1da";
+$ionicon-var-ios7-search-strong: "\f1d9";
+$ionicon-var-ios7-settings: "\f339";
+$ionicon-var-ios7-settings-strong: "\f338";
+$ionicon-var-ios7-shrink: "\f30e";
+$ionicon-var-ios7-skipbackward: "\f1dc";
+$ionicon-var-ios7-skipbackward-outline: "\f1db";
+$ionicon-var-ios7-skipforward: "\f1de";
+$ionicon-var-ios7-skipforward-outline: "\f1dd";
+$ionicon-var-ios7-snowy: "\f309";
+$ionicon-var-ios7-speedometer: "\f290";
+$ionicon-var-ios7-speedometer-outline: "\f28f";
+$ionicon-var-ios7-star: "\f1e0";
+$ionicon-var-ios7-star-half: "\f33a";
+$ionicon-var-ios7-star-outline: "\f1df";
+$ionicon-var-ios7-stopwatch: "\f1e2";
+$ionicon-var-ios7-stopwatch-outline: "\f1e1";
+$ionicon-var-ios7-sunny: "\f1e4";
+$ionicon-var-ios7-sunny-outline: "\f1e3";
+$ionicon-var-ios7-telephone: "\f1e6";
+$ionicon-var-ios7-telephone-outline: "\f1e5";
+$ionicon-var-ios7-tennisball: "\f33c";
+$ionicon-var-ios7-tennisball-outline: "\f33b";
+$ionicon-var-ios7-thunderstorm: "\f1e8";
+$ionicon-var-ios7-thunderstorm-outline: "\f1e7";
+$ionicon-var-ios7-time: "\f292";
+$ionicon-var-ios7-time-outline: "\f291";
+$ionicon-var-ios7-timer: "\f1ea";
+$ionicon-var-ios7-timer-outline: "\f1e9";
+$ionicon-var-ios7-toggle: "\f33e";
+$ionicon-var-ios7-toggle-outline: "\f33d";
+$ionicon-var-ios7-trash: "\f1ec";
+$ionicon-var-ios7-trash-outline: "\f1eb";
+$ionicon-var-ios7-undo: "\f1ee";
+$ionicon-var-ios7-undo-outline: "\f1ed";
+$ionicon-var-ios7-unlocked: "\f1f0";
+$ionicon-var-ios7-unlocked-outline: "\f1ef";
+$ionicon-var-ios7-upload: "\f1f2";
+$ionicon-var-ios7-upload-outline: "\f1f1";
+$ionicon-var-ios7-videocam: "\f1f4";
+$ionicon-var-ios7-videocam-outline: "\f1f3";
+$ionicon-var-ios7-volume-high: "\f1f5";
+$ionicon-var-ios7-volume-low: "\f1f6";
+$ionicon-var-ios7-wineglass: "\f294";
+$ionicon-var-ios7-wineglass-outline: "\f293";
+$ionicon-var-ios7-world: "\f1f8";
+$ionicon-var-ios7-world-outline: "\f1f7";
+$ionicon-var-ipad: "\f1f9";
+$ionicon-var-iphone: "\f1fa";
+$ionicon-var-ipod: "\f1fb";
+$ionicon-var-jet: "\f295";
+$ionicon-var-key: "\f296";
+$ionicon-var-knife: "\f297";
+$ionicon-var-laptop: "\f1fc";
+$ionicon-var-leaf: "\f1fd";
+$ionicon-var-levels: "\f298";
+$ionicon-var-lightbulb: "\f299";
+$ionicon-var-link: "\f1fe";
+$ionicon-var-load-a: "\f29a";
+$ionicon-var-load-b: "\f29b";
+$ionicon-var-load-c: "\f29c";
+$ionicon-var-load-d: "\f29d";
+$ionicon-var-location: "\f1ff";
+$ionicon-var-locked: "\f200";
+$ionicon-var-log-in: "\f29e";
+$ionicon-var-log-out: "\f29f";
+$ionicon-var-loop: "\f201";
+$ionicon-var-magnet: "\f2a0";
+$ionicon-var-male: "\f2a1";
+$ionicon-var-man: "\f202";
+$ionicon-var-map: "\f203";
+$ionicon-var-medkit: "\f2a2";
+$ionicon-var-merge: "\f33f";
+$ionicon-var-mic-a: "\f204";
+$ionicon-var-mic-b: "\f205";
+$ionicon-var-mic-c: "\f206";
+$ionicon-var-minus: "\f209";
+$ionicon-var-minus-circled: "\f207";
+$ionicon-var-minus-round: "\f208";
+$ionicon-var-model-s: "\f2c1";
+$ionicon-var-monitor: "\f20a";
+$ionicon-var-more: "\f20b";
+$ionicon-var-mouse: "\f340";
+$ionicon-var-music-note: "\f20c";
+$ionicon-var-navicon: "\f20e";
+$ionicon-var-navicon-round: "\f20d";
+$ionicon-var-navigate: "\f2a3";
+$ionicon-var-network: "\f341";
+$ionicon-var-no-smoking: "\f2c2";
+$ionicon-var-nuclear: "\f2a4";
+$ionicon-var-outlet: "\f342";
+$ionicon-var-paper-airplane: "\f2c3";
+$ionicon-var-paperclip: "\f20f";
+$ionicon-var-pause: "\f210";
+$ionicon-var-person: "\f213";
+$ionicon-var-person-add: "\f211";
+$ionicon-var-person-stalker: "\f212";
+$ionicon-var-pie-graph: "\f2a5";
+$ionicon-var-pin: "\f2a6";
+$ionicon-var-pinpoint: "\f2a7";
+$ionicon-var-pizza: "\f2a8";
+$ionicon-var-plane: "\f214";
+$ionicon-var-planet: "\f343";
+$ionicon-var-play: "\f215";
+$ionicon-var-playstation: "\f30a";
+$ionicon-var-plus: "\f218";
+$ionicon-var-plus-circled: "\f216";
+$ionicon-var-plus-round: "\f217";
+$ionicon-var-podium: "\f344";
+$ionicon-var-pound: "\f219";
+$ionicon-var-power: "\f2a9";
+$ionicon-var-pricetag: "\f2aa";
+$ionicon-var-pricetags: "\f2ab";
+$ionicon-var-printer: "\f21a";
+$ionicon-var-pull-request: "\f345";
+$ionicon-var-qr-scanner: "\f346";
+$ionicon-var-quote: "\f347";
+$ionicon-var-radio-waves: "\f2ac";
+$ionicon-var-record: "\f21b";
+$ionicon-var-refresh: "\f21c";
+$ionicon-var-reply: "\f21e";
+$ionicon-var-reply-all: "\f21d";
+$ionicon-var-ribbon-a: "\f348";
+$ionicon-var-ribbon-b: "\f349";
+$ionicon-var-sad: "\f34a";
+$ionicon-var-scissors: "\f34b";
+$ionicon-var-search: "\f21f";
+$ionicon-var-settings: "\f2ad";
+$ionicon-var-share: "\f220";
+$ionicon-var-shuffle: "\f221";
+$ionicon-var-skip-backward: "\f222";
+$ionicon-var-skip-forward: "\f223";
+$ionicon-var-social-android: "\f225";
+$ionicon-var-social-android-outline: "\f224";
+$ionicon-var-social-apple: "\f227";
+$ionicon-var-social-apple-outline: "\f226";
+$ionicon-var-social-bitcoin: "\f2af";
+$ionicon-var-social-bitcoin-outline: "\f2ae";
+$ionicon-var-social-buffer: "\f229";
+$ionicon-var-social-buffer-outline: "\f228";
+$ionicon-var-social-designernews: "\f22b";
+$ionicon-var-social-designernews-outline: "\f22a";
+$ionicon-var-social-dribbble: "\f22d";
+$ionicon-var-social-dribbble-outline: "\f22c";
+$ionicon-var-social-dropbox: "\f22f";
+$ionicon-var-social-dropbox-outline: "\f22e";
+$ionicon-var-social-facebook: "\f231";
+$ionicon-var-social-facebook-outline: "\f230";
+$ionicon-var-social-foursquare: "\f34d";
+$ionicon-var-social-foursquare-outline: "\f34c";
+$ionicon-var-social-freebsd-devil: "\f2c4";
+$ionicon-var-social-github: "\f233";
+$ionicon-var-social-github-outline: "\f232";
+$ionicon-var-social-google: "\f34f";
+$ionicon-var-social-google-outline: "\f34e";
+$ionicon-var-social-googleplus: "\f235";
+$ionicon-var-social-googleplus-outline: "\f234";
+$ionicon-var-social-hackernews: "\f237";
+$ionicon-var-social-hackernews-outline: "\f236";
+$ionicon-var-social-instagram: "\f351";
+$ionicon-var-social-instagram-outline: "\f350";
+$ionicon-var-social-linkedin: "\f239";
+$ionicon-var-social-linkedin-outline: "\f238";
+$ionicon-var-social-pinterest: "\f2b1";
+$ionicon-var-social-pinterest-outline: "\f2b0";
+$ionicon-var-social-reddit: "\f23b";
+$ionicon-var-social-reddit-outline: "\f23a";
+$ionicon-var-social-rss: "\f23d";
+$ionicon-var-social-rss-outline: "\f23c";
+$ionicon-var-social-skype: "\f23f";
+$ionicon-var-social-skype-outline: "\f23e";
+$ionicon-var-social-tumblr: "\f241";
+$ionicon-var-social-tumblr-outline: "\f240";
+$ionicon-var-social-tux: "\f2c5";
+$ionicon-var-social-twitter: "\f243";
+$ionicon-var-social-twitter-outline: "\f242";
+$ionicon-var-social-usd: "\f353";
+$ionicon-var-social-usd-outline: "\f352";
+$ionicon-var-social-vimeo: "\f245";
+$ionicon-var-social-vimeo-outline: "\f244";
+$ionicon-var-social-windows: "\f247";
+$ionicon-var-social-windows-outline: "\f246";
+$ionicon-var-social-wordpress: "\f249";
+$ionicon-var-social-wordpress-outline: "\f248";
+$ionicon-var-social-yahoo: "\f24b";
+$ionicon-var-social-yahoo-outline: "\f24a";
+$ionicon-var-social-youtube: "\f24d";
+$ionicon-var-social-youtube-outline: "\f24c";
+$ionicon-var-speakerphone: "\f2b2";
+$ionicon-var-speedometer: "\f2b3";
+$ionicon-var-spoon: "\f2b4";
+$ionicon-var-star: "\f24e";
+$ionicon-var-stats-bars: "\f2b5";
+$ionicon-var-steam: "\f30b";
+$ionicon-var-stop: "\f24f";
+$ionicon-var-thermometer: "\f2b6";
+$ionicon-var-thumbsdown: "\f250";
+$ionicon-var-thumbsup: "\f251";
+$ionicon-var-toggle: "\f355";
+$ionicon-var-toggle-filled: "\f354";
+$ionicon-var-trash-a: "\f252";
+$ionicon-var-trash-b: "\f253";
+$ionicon-var-trophy: "\f356";
+$ionicon-var-umbrella: "\f2b7";
+$ionicon-var-university: "\f357";
+$ionicon-var-unlocked: "\f254";
+$ionicon-var-upload: "\f255";
+$ionicon-var-usb: "\f2b8";
+$ionicon-var-videocamera: "\f256";
+$ionicon-var-volume-high: "\f257";
+$ionicon-var-volume-low: "\f258";
+$ionicon-var-volume-medium: "\f259";
+$ionicon-var-volume-mute: "\f25a";
+$ionicon-var-wand: "\f358";
+$ionicon-var-waterdrop: "\f25b";
+$ionicon-var-wifi: "\f25c";
+$ionicon-var-wineglass: "\f2b9";
+$ionicon-var-woman: "\f25d";
+$ionicon-var-wrench: "\f2ba";
+$ionicon-var-xbox: "\f30c";
\ No newline at end of file
diff --git a/client/lib/ionic/scss/ionicons/ionicons.scss b/client/lib/ionic/scss/ionicons/ionicons.scss
new file mode 100644
index 0000000..ff4ec8d
--- /dev/null
+++ b/client/lib/ionic/scss/ionicons/ionicons.scss
@@ -0,0 +1,11 @@
+@import "ionicons-variables";
+/*!
+ Ionicons, v1.5.2
+ Created by Ben Sperry for the Ionic Framework, http://ionicons.com/
+ https://twitter.com/benjsperry https://twitter.com/ionicframework
+ MIT License: https://github.com/driftyco/ionicons
+*/
+
+@import "ionicons-font";
+@import "ionicons-animation";
+@import "ionicons-icons";
diff --git a/client/lib/ionic/version.json b/client/lib/ionic/version.json
new file mode 100644
index 0000000..5fc9071
--- /dev/null
+++ b/client/lib/ionic/version.json
@@ -0,0 +1,6 @@
+{
+ "version": "1.0.0-beta.13",
+ "codename": "lanthanum-leopard",
+ "date": "2014-09-24",
+ "time": "20:17:36"
+}
diff --git a/client/templates/friend-detail.html b/client/templates/friend-detail.html
new file mode 100644
index 0000000..d4689a2
--- /dev/null
+++ b/client/templates/friend-detail.html
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/client/templates/tab-friends.html b/client/templates/tab-friends.html
new file mode 100644
index 0000000..b6485a6
--- /dev/null
+++ b/client/templates/tab-friends.html
@@ -0,0 +1,9 @@
+
+
+
+
+ {{friend.name}}
+
+
+
+
diff --git a/client/templates/tab-shared.html b/client/templates/tab-shared.html
new file mode 100644
index 0000000..1f02e10
--- /dev/null
+++ b/client/templates/tab-shared.html
@@ -0,0 +1,9 @@
+
+
+
+
+ {{friend.name}}
+
+
+
+
diff --git a/client/templates/tab-todos.html b/client/templates/tab-todos.html
new file mode 100644
index 0000000..af4b99f
--- /dev/null
+++ b/client/templates/tab-todos.html
@@ -0,0 +1,9 @@
+
+
+
+
+ {{q.question}}
+
+
+
+
diff --git a/client/templates/tabs-container.html b/client/templates/tabs-container.html
new file mode 100644
index 0000000..20770b8
--- /dev/null
+++ b/client/templates/tabs-container.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/templates/tabs.html b/client/templates/tabs.html
new file mode 100644
index 0000000..349a38e
--- /dev/null
+++ b/client/templates/tabs.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/server/auth.js b/server/auth.js
new file mode 100644
index 0000000..6d5e185
--- /dev/null
+++ b/server/auth.js
@@ -0,0 +1,19 @@
+var passport = require('passport')
+ , LocalStrategy = require('passport-local').Strategy;
+
+var models = require('./models');
+
+passport.use(new LocalStrategy(
+ function(username, password, done) {
+ User.findOne({ username: username }, function(err, user) {
+ if (err) { return done(err); }
+ if (!user) {
+ return done(null, false, { message: 'Incorrect username.' });
+ }
+ if (!user.validPassword(password)) {
+ return done(null, false, { message: 'Incorrect password.' });
+ }
+ return done(null, user);
+ });
+ }
+));
\ No newline at end of file
diff --git a/server/config.js b/server/config.js
new file mode 100644
index 0000000..99404eb
--- /dev/null
+++ b/server/config.js
@@ -0,0 +1,19 @@
+DEBUG = true;
+
+exports.db_client = 'pg';
+exports.db_url = process.env.DATABASE_URL || 'postgresql://localhost/muchado';
+exports.DEBUG = DEBUG;
+
+exports.debug = function() {
+ if (DEBUG) {
+ console.log("[debug] ", arguments);
+ }
+}
+
+exports.warn = function() {
+ console.log("[warn] ", arguments);
+}
+
+exports.error = function() {
+ console.log("[error] ", arguments);
+}
\ No newline at end of file
diff --git a/server/data.db b/server/data.db
new file mode 100644
index 0000000..ed9851d
Binary files /dev/null and b/server/data.db differ
diff --git a/server/knexfile.js b/server/knexfile.js
new file mode 100644
index 0000000..6f33fe1
--- /dev/null
+++ b/server/knexfile.js
@@ -0,0 +1,11 @@
+// Update with your config settings.
+var config = require('./config');
+
+module.exports = {
+
+ development: {
+ client: 'pg',
+ connection: config.db_url
+ },
+
+};
\ No newline at end of file
diff --git a/server/migrations/20140927121211_initial.js b/server/migrations/20140927121211_initial.js
new file mode 100644
index 0000000..c13e2c8
--- /dev/null
+++ b/server/migrations/20140927121211_initial.js
@@ -0,0 +1,40 @@
+'use strict';
+
+exports.up = function(knex, Promise) {
+ var schema = knex.schema;
+
+ return Promise.all([
+ schema.hasTable('users').then(function(exists) {
+ if (!exists) {
+ return schema.createTable('users', function(table) {
+ table.increments('id');
+ table.string('email').notNullable();
+ table.string('password').notNullable();
+ table.timestamps();
+ });
+ } else {
+ return schema;
+ }
+ }),
+ schema.hasTable('questions').then(function(exists) {
+ if (!exists) {
+ return schema.createTable('questions', function(table) {
+ table.increments('id');
+ table.text('question');
+ table.json('answers');
+ table.boolean('show');
+ table.timestamps();
+ });
+ } else {
+ return schema;
+ }
+ })
+ ]);
+};
+
+exports.down = function(knex, Promise) {
+ return Promise.all([
+ knex.schema.dropTable('users'),
+ knex.schema.dropTable('questions')
+ ]);
+};
diff --git a/server/migrations/20140927124325_pgnotify.js b/server/migrations/20140927124325_pgnotify.js
new file mode 100644
index 0000000..b067e1e
--- /dev/null
+++ b/server/migrations/20140927124325_pgnotify.js
@@ -0,0 +1,23 @@
+'use strict';
+
+exports.up = function(knex, Promise) {
+ return Promise.all([
+ knex.raw('CREATE FUNCTION questions_notify_trigger() RETURNS trigger AS $$ \
+ DECLARE \
+ BEGIN \
+ PERFORM pg_notify(\'questions\',to_char(NEW.id, \'999\')); \
+ RETURN new; \
+ END; \
+ $$ LANGUAGE plpgsql; \
+ CREATE TRIGGER questions_show_trigger AFTER UPDATE ON questions \
+ FOR EACH ROW EXECUTE PROCEDURE questions_notify_trigger(); \
+ ')
+ ]);
+};
+
+exports.down = function(knex, Promise) {
+ return Promise.all([
+ knex.raw("DROP FUNCTION questions_notify_trigger()"),
+ knex.raw("DROP TRIGGER questions_show_trigger on questions")
+ ]);
+};
diff --git a/server/models.js b/server/models.js
new file mode 100644
index 0000000..46220fc
--- /dev/null
+++ b/server/models.js
@@ -0,0 +1,14 @@
+module.exports = function(bookshelf) {
+ var User = bookshelf.Model.extend({
+ tableName: 'users'
+ });
+
+ var Question = bookshelf.Model.extend({
+ tableName: 'questions'
+ });
+
+ return {
+ User: User,
+ Question: Question
+ }
+}
diff --git a/server/notifier.js b/server/notifier.js
new file mode 100644
index 0000000..c351719
--- /dev/null
+++ b/server/notifier.js
@@ -0,0 +1,19 @@
+var pg = require('pg');
+
+module.exports = function(bookshelf, topic_callbacks) {
+ pg.connect(bookshelf.knex.client.connectionSettings, function(err, client) {
+ if (err) {
+ console.log("Notifier, error connecting to database: " + err);
+ } else {
+ client.on('notification', function(msg) {
+ console.log("DATABASE NOTIFY: ", msg);
+ if (topic_callbacks[msg.channel]) {
+ topic_callbacks[msg.channel](msg.payload);
+ }
+ });
+ for (var topic in topic_callbacks) {
+ client.query("LISTEN " + topic);
+ }
+ }
+ });
+}
\ No newline at end of file
diff --git a/server/npm-debug.log b/server/npm-debug.log
new file mode 100644
index 0000000..e760248
--- /dev/null
+++ b/server/npm-debug.log
@@ -0,0 +1,169 @@
+0 info it worked if it ends with ok
+1 verbose cli [ 'node', '/usr/local/bin/npm', 'install', '--save', 'socket.io' ]
+2 info using npm@1.4.28
+3 info using node@v0.10.32
+4 warn package.json mobileclips@1.0.0 No description
+5 warn package.json mobileclips@1.0.0 No repository field.
+6 warn package.json mobileclips@1.0.0 No README data
+7 verbose readDependencies using package.json deps
+8 verbose cache add [ 'socket.io', null ]
+9 verbose cache add name=undefined spec="socket.io" args=["socket.io",null]
+10 verbose parsed url { protocol: null,
+10 verbose parsed url slashes: null,
+10 verbose parsed url auth: null,
+10 verbose parsed url host: null,
+10 verbose parsed url port: null,
+10 verbose parsed url hostname: null,
+10 verbose parsed url hash: null,
+10 verbose parsed url search: null,
+10 verbose parsed url query: null,
+10 verbose parsed url pathname: 'socket.io',
+10 verbose parsed url path: 'socket.io',
+10 verbose parsed url href: 'socket.io' }
+11 silly lockFile 71475bfd-socket-io socket.io
+12 verbose lock socket.io /Users/spersinger/.npm/71475bfd-socket-io.lock
+13 silly lockFile 71475bfd-socket-io socket.io
+14 silly lockFile 71475bfd-socket-io socket.io
+15 verbose addNamed [ 'socket.io', '' ]
+16 verbose addNamed [ null, '*' ]
+17 silly lockFile c03de861-socket-io socket.io@
+18 verbose lock socket.io@ /Users/spersinger/.npm/c03de861-socket-io.lock
+19 silly addNameRange { name: 'socket.io', range: '*', hasData: false }
+20 verbose request where is /socket.io
+21 verbose request registry https://registry.npmjs.org/
+22 verbose request id 3c652810999457d2
+23 verbose url raw /socket.io
+24 verbose url resolving [ 'https://registry.npmjs.org/', './socket.io' ]
+25 verbose url resolved https://registry.npmjs.org/socket.io
+26 verbose request where is https://registry.npmjs.org/socket.io
+27 info trying registry request attempt 1 at 14:30:47
+28 verbose etag "E3EDAEDTXBRB7COPCV5IC4JDN"
+29 http GET https://registry.npmjs.org/socket.io
+30 http 304 https://registry.npmjs.org/socket.io
+31 silly registry.get cb [ 304,
+31 silly registry.get { date: 'Sat, 27 Sep 2014 21:30:33 GMT',
+31 silly registry.get server: 'Apache',
+31 silly registry.get via: '1.1 varnish',
+31 silly registry.get 'last-modified': 'Sat, 27 Sep 2014 21:30:22 GMT',
+31 silly registry.get 'cache-control': 'max-age=60',
+31 silly registry.get etag: '"E3EDAEDTXBRB7COPCV5IC4JDN"',
+31 silly registry.get 'x-served-by': 'cache-sjc3130-SJC',
+31 silly registry.get 'x-cache': 'HIT',
+31 silly registry.get 'x-cache-hits': '2',
+31 silly registry.get 'x-timer': 'S1411853433.614831,VS0,VE0',
+31 silly registry.get vary: 'Accept',
+31 silly registry.get 'content-length': '0',
+31 silly registry.get 'keep-alive': 'timeout=10, max=50',
+31 silly registry.get connection: 'Keep-Alive' } ]
+32 verbose etag https://registry.npmjs.org/socket.io from cache
+33 silly addNameRange number 2 { name: 'socket.io', range: '*', hasData: true }
+34 silly addNameRange versions [ 'socket.io',
+34 silly addNameRange [ '0.3.8',
+34 silly addNameRange '0.4.0',
+34 silly addNameRange '0.4.1',
+34 silly addNameRange '0.5.1',
+34 silly addNameRange '0.5.3',
+34 silly addNameRange '0.6.0',
+34 silly addNameRange '0.6.1',
+34 silly addNameRange '0.6.3',
+34 silly addNameRange '0.6.4',
+34 silly addNameRange '0.6.5',
+34 silly addNameRange '0.6.6',
+34 silly addNameRange '0.6.7',
+34 silly addNameRange '0.6.8',
+34 silly addNameRange '0.6.9',
+34 silly addNameRange '0.6.10',
+34 silly addNameRange '0.6.11',
+34 silly addNameRange '0.6.12',
+34 silly addNameRange '0.6.14',
+34 silly addNameRange '0.6.15',
+34 silly addNameRange '0.6.16',
+34 silly addNameRange '0.6.17',
+34 silly addNameRange '0.6.18',
+34 silly addNameRange '0.7.0',
+34 silly addNameRange '0.7.1',
+34 silly addNameRange '0.7.2',
+34 silly addNameRange '0.7.3',
+34 silly addNameRange '0.7.4',
+34 silly addNameRange '0.7.5',
+34 silly addNameRange '0.7.6',
+34 silly addNameRange '0.7.7',
+34 silly addNameRange '0.7.8',
+34 silly addNameRange '0.7.9',
+34 silly addNameRange '0.7.10',
+34 silly addNameRange '0.7.11',
+34 silly addNameRange '0.8.0',
+34 silly addNameRange '0.8.1',
+34 silly addNameRange '0.8.2',
+34 silly addNameRange '0.8.3',
+34 silly addNameRange '0.8.4',
+34 silly addNameRange '0.8.5',
+34 silly addNameRange '0.8.6',
+34 silly addNameRange '0.8.7',
+34 silly addNameRange '0.9.0',
+34 silly addNameRange '0.9.1',
+34 silly addNameRange '0.9.1-1',
+34 silly addNameRange '0.9.2',
+34 silly addNameRange '0.9.3',
+34 silly addNameRange '0.9.4',
+34 silly addNameRange '0.9.5',
+34 silly addNameRange '0.9.6',
+34 silly addNameRange '0.9.7',
+34 silly addNameRange '0.9.8',
+34 silly addNameRange '0.9.9',
+34 silly addNameRange '0.9.10',
+34 silly addNameRange '0.9.11',
+34 silly addNameRange '0.9.12',
+34 silly addNameRange '0.9.13',
+34 silly addNameRange '0.9.14',
+34 silly addNameRange '0.9.15',
+34 silly addNameRange '0.9.16',
+34 silly addNameRange '1.0.0-pre',
+34 silly addNameRange '1.0.0-pre2',
+34 silly addNameRange '1.0.0-pre3',
+34 silly addNameRange '1.0.0-pre4',
+34 silly addNameRange '1.0.0-pre5',
+34 silly addNameRange '0.9.17',
+34 silly addNameRange '1.0.0',
+34 silly addNameRange '1.0.1',
+34 silly addNameRange '1.0.2',
+34 silly addNameRange '1.0.3',
+34 silly addNameRange '1.0.4',
+34 silly addNameRange '1.0.5',
+34 silly addNameRange '1.0.6',
+34 silly addNameRange '1.1.0' ] ]
+35 verbose addNamed [ 'socket.io', '1.1.0' ]
+36 verbose addNamed [ '1.1.0', '1.1.0' ]
+37 silly lockFile 8bfafe15-socket-io-1-1-0 socket.io@1.1.0
+38 verbose lock socket.io@1.1.0 /Users/spersinger/.npm/8bfafe15-socket-io-1-1-0.lock
+39 silly lockFile 9bb74f5a-rg-socket-io-socket-io-1-1-0-tgz https://registry.npmjs.org/socket.io/-/socket.io-1.1.0.tgz
+40 verbose lock https://registry.npmjs.org/socket.io/-/socket.io-1.1.0.tgz /Users/spersinger/.npm/9bb74f5a-rg-socket-io-socket-io-1-1-0-tgz.lock
+41 verbose addRemoteTarball [ 'https://registry.npmjs.org/socket.io/-/socket.io-1.1.0.tgz',
+41 verbose addRemoteTarball '0825ecb5740f34c2319a40087c537a348010dbb6' ]
+42 info retry fetch attempt 1 at 14:30:48
+43 verbose fetch to= /var/folders/np/hhzgy5654fz4_f0j9thj86vrpt23xz/T/npm-37980-nk0peKPR/registry.npmjs.org/socket.io/-/socket.io-1.1.0.tgz
+44 http GET https://registry.npmjs.org/socket.io/-/socket.io-1.1.0.tgz
+45 http 200 https://registry.npmjs.org/socket.io/-/socket.io-1.1.0.tgz
+46 silly lockFile 9bb74f5a-rg-socket-io-socket-io-1-1-0-tgz https://registry.npmjs.org/socket.io/-/socket.io-1.1.0.tgz
+47 silly lockFile 9bb74f5a-rg-socket-io-socket-io-1-1-0-tgz https://registry.npmjs.org/socket.io/-/socket.io-1.1.0.tgz
+48 silly lockFile 8bfafe15-socket-io-1-1-0 socket.io@1.1.0
+49 silly lockFile 8bfafe15-socket-io-1-1-0 socket.io@1.1.0
+50 silly lockFile c03de861-socket-io socket.io@
+51 silly lockFile c03de861-socket-io socket.io@
+52 error Error: EACCES, mkdir '/Users/spersinger/.npm/socket.io/1.1.0'
+52 error { [Error: EACCES, mkdir '/Users/spersinger/.npm/socket.io/1.1.0']
+52 error errno: 3,
+52 error code: 'EACCES',
+52 error path: '/Users/spersinger/.npm/socket.io/1.1.0',
+52 error parent: 'mobileclips' }
+53 error Please try running this command again as root/Administrator.
+54 error System Darwin 13.4.0
+55 error command "node" "/usr/local/bin/npm" "install" "--save" "socket.io"
+56 error cwd /Users/spersinger/github/muchado/server
+57 error node -v v0.10.32
+58 error npm -v 1.4.28
+59 error path /Users/spersinger/.npm/socket.io/1.1.0
+60 error code EACCES
+61 error errno 3
+62 error stack Error: EACCES, mkdir '/Users/spersinger/.npm/socket.io/1.1.0'
+63 verbose exit [ 3, true ]
diff --git a/server/package.json b/server/package.json
new file mode 100644
index 0000000..74db702
--- /dev/null
+++ b/server/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "mobileclips",
+ "version": "1.0.0",
+ "description": "",
+ "main": "server.js",
+ "dependencies": {
+ "bookshelf": "^0.7.7",
+ "express": "^4.9.5",
+ "knex": "^0.6.22",
+ "passport-local": "^1.0.0",
+ "pg": "^3.4.4",
+ "socket.io": "^1.1.0",
+ "sqlite3": "^3.0.0",
+ "websocket": "^1.0.8"
+ },
+ "devDependencies": {},
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC"
+}
diff --git a/server/pages/test.html b/server/pages/test.html
new file mode 100644
index 0000000..29a324f
--- /dev/null
+++ b/server/pages/test.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+ Test page
+
+
+
+
+
+
diff --git a/server/server.js b/server/server.js
new file mode 100644
index 0000000..8a77d04
--- /dev/null
+++ b/server/server.js
@@ -0,0 +1,52 @@
+var http = require('http'),
+ config = require('./config'),
+ express = require('express'),
+ passport = require('passport')
+ path = require('path'),
+ knex = require('knex')({client: config.db_client, connection: config.db_url, debug: config.DEBUG}),
+ bookshelf = require('bookshelf')(knex),
+ models = require('./models')(bookshelf),
+ notifier = require('./notifier')
+ ;
+
+app = express();
+server = http.createServer(app);
+io = require('socket.io')(server);
+
+app.set('bookshelf', bookshelf);
+
+logger = {
+ debug: config.debug,
+ warn: config.warn,
+ error: config.error
+};
+
+
+app.use(express.static(path.join(__dirname, '../client')));
+app.use(express.static(path.join(__dirname, './pages')));
+
+
+notifier(bookshelf,
+ {
+ 'questions': function(question_id) {
+ new models.Question({id:question_id})
+ .fetch()
+ .then(function(question) {
+ console.log("Question updated: ", question_id);
+ io.emit('questions', JSON.stringify(question));
+ });
+ }
+ }
+)
+
+app.set('port', process.env.PORT || 5000);
+
+server.listen(app.get('port'), function () {
+ console.log('Express server listening on port ' + app.get('port'));
+});
+
+io.on('connection', function(socket){
+ console.log('a user connected');
+});
+
+
diff --git a/server/test.js b/server/test.js
new file mode 100644
index 0000000..3e8e7c5
--- /dev/null
+++ b/server/test.js
@@ -0,0 +1,66 @@
+var pg = require('pg');
+
+var knex = require('knex')({
+ //dialect: 'sqlite3',
+ //dialect: 'pg',
+ client: 'pg',
+ connection: 'postgresql://localhost/muchado',
+ debug: true
+});
+
+/*
+// Create a table
+knex.schema.dropTable('accounts')
+
+.then(function() {
+ return knex.schema.dropTable('users')
+}).
+
+then(function() {
+ return knex.schema.createTable('users', function(table) {
+ table.increments('id');
+ table.string('user_name');
+ });
+})
+
+// ...and another
+.then(function() {
+ return knex.schema.createTable('accounts', function(table) {
+ table.increments('id');
+ table.string('account_name');
+ table.integer('user_id').unsigned().references('users.id');
+ });
+})
+
+// Then query the table...
+.then(function() {
+ return knex.insert({user_name: 'Tim'}).into('users');
+})
+
+// ...and using the insert id, insert into the other table.
+.then(function(rows) {
+ return knex.table('accounts').insert({account_name: 'knex', user_id: rows[0]});
+})
+
+// Query both of the rows.
+.then(function() {
+ knex('users')
+ .select('*').then(function(results) {
+ console.log("Results ", results);
+ }).catch(function(err) {
+ console.log("Error ", err);
+ })
+});
+*/
+
+pg.connect(knex.client.connectionSettings, function(err, client) {
+ if (err) {
+ console.log("Error connecting to database: " + err);
+ } else {
+ client.on('notification', function(msg) {
+ console.log("DATABASE NOTIFY: ", msg);
+ // Move some data...
+ });
+ var query = client.query("LISTEN questions");
+ }
+});