From 90f10611ac162907a65e4ecb26805b760948f4cf Mon Sep 17 00:00:00 2001 From: Patrick McDonagh Date: Mon, 1 Feb 2016 18:13:33 -0600 Subject: [PATCH] Added ability to specify datetime limits --- .remote-sync.json | 14 + python/tagserver_SQLite.py | 32 +- www/app.js | 4 +- www/bower.json | 6 +- www/functions_SQLite.js | 89 +++++- www/public/js/controller.js | 134 +++++++- www/public/js/sugar.min.js | 132 ++++++++ www/public/js/tooltip.js | 514 ------------------------------- www/public/partials/tagVals.html | 18 +- www/views/angularIndex.html | 9 +- 10 files changed, 397 insertions(+), 555 deletions(-) create mode 100644 .remote-sync.json create mode 100644 www/public/js/sugar.min.js delete mode 100644 www/public/js/tooltip.js diff --git a/.remote-sync.json b/.remote-sync.json new file mode 100644 index 0000000..7f03967 --- /dev/null +++ b/.remote-sync.json @@ -0,0 +1,14 @@ +{ + "transport": "scp", + "uploadOnSave": true, + "deleteLocal": false, + "hostname": "10.10.10.6", + "port": "22", + "target": "/home/pi/tagserver/", + "ignore": [ + ".remote-sync.json", + ".git/**" + ], + "username": "pi", + "password": "raspberry" +} diff --git a/python/tagserver_SQLite.py b/python/tagserver_SQLite.py index 0213eee..313e463 100644 --- a/python/tagserver_SQLite.py +++ b/python/tagserver_SQLite.py @@ -53,22 +53,24 @@ def main(): print(tagList) while True: - for r in tagList: - r["val"] = readTag(PLC_IP_ADDRESS, str(r['name']))[0] - print("{0} - {1}".format(r["name"], r["val"])) - if not r["val"] == r["lastVal"]: - with con: - cur = con.cursor() - aQuery = """INSERT INTO vals (tagID, val) VALUES ('%d', '%f');""" % (r["id"], float(r["val"])) - print(aQuery) - cur.execute(aQuery) - con.commit() - r["lastVal"] = r["val"] + try: + for r in tagList: + r["val"] = readTag(PLC_IP_ADDRESS, str(r['name']))[0] + print("{0} - {1}".format(r["name"], r["val"])) + if not r["val"] == r["lastVal"]: + with con: + cur = con.cursor() + aQuery = """INSERT INTO vals (tagID, val) VALUES ('%d', '%f');""" % (r["id"], float(r["val"])) + print(aQuery) + cur.execute(aQuery) + con.commit() + r["lastVal"] = r["val"] - time.sleep(scan_rate) - # except Exception as err: - # print err - # pass + time.sleep(scan_rate) + + except Exception as err: + print err + main() if __name__ == '__main__': main() diff --git a/www/app.js b/www/app.js index 6913382..83e17b0 100644 --- a/www/app.js +++ b/www/app.js @@ -72,7 +72,9 @@ app.get('/json/tag/:id', fns.getTag); // Lists all app.get('/json/tag', fns.getAllTags); // Lists all tags in the scan list app.get('/json/val/:tag', fns.latestValueSingleTag); // Gets the latest value of a single tag app.get('/json/series/:tag/:hours', fns.seriesTagValues); // Gets all the values of a tag for the last X hours -app.get('/json/CSV/:tag/:hours', fns.seriesCSV); // Gets all the values of a tag for the last X hours +app.get('/json/valBetween/:tag/:startDatetime/:endDatetime', fns.seriesTagValuesBetween); // Gets the latest value of a single tag +app.get('/json/CSV/:tag/:startDatetime/:endDatetime', fns.seriesCSV); // Gets all the values of a tag for the last X hours +app.get('/json/CSV/:tag/:hours', fns.seriesCSV); // Gets all the values of a tag for the last X hours app.get('/json/all', fns.latestValueAllTags); // Gets the latest values of all tags in the scan list app.get('*', angular); diff --git a/www/bower.json b/www/bower.json index 03b9b8b..6df52db 100644 --- a/www/bower.json +++ b/www/bower.json @@ -21,6 +21,10 @@ "angular": "angularjs#~1.4.9", "bootstrap": "~3.3.6", "angular-route": "~1.4.9", - "font-awesome": "~4.5.0" + "font-awesome": "~4.5.0", + "ngQuickDate": "^1.3.4" + }, + "resolutions": { + "angular": "~1.4.9" } } diff --git a/www/functions_SQLite.js b/www/functions_SQLite.js index 5959ba0..ca9ebde 100644 --- a/www/functions_SQLite.js +++ b/www/functions_SQLite.js @@ -2,6 +2,41 @@ var dbFile = "/mnt/usb/data.db"; // var dbFile = '/Users/patrickjmcd/data.db'; +var dString_to_sqlite = function(dString){ + /** + * Takes a date string in the form YYYYMMDD_HHmmSS and returns it in SQLite format (YYYY-MM-DD HH:mm:SS) + * @param {String} dString + * @return {String} sqliteString + */ + var re = /(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})/; + var fd = re.exec(dString); + if (fd){ + var sqliteString = ""; + return sqliteString.concat(fd[1], "-", fd[2], "-", fd[3], " ", fd[4], ":", fd[5], ":", fd[6]); + } else { + return null; + } +}; + + + +var sqlite_to_dString = function(sqliteDate){ + /** + * Takes a sqlite date string in the form YYYY-MM-DD HH:mm:SS and returns it in format YYYYMMDD_HHmmSS + * @param {String} sqliteDate + * @return {String} dString + */ + var re = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/; + var fd = re.exec(sqliteDate); + if (fd){ + var dString = ""; + return dString.concat(fd[1], fd[2], fd[3], "_", fd[4], fd[5], fd[6]); + } else { + return null; + } +}; + + exports.getAllTags = function(req, res){ var sqlite3 = require('sqlite3').verbose(); var db = new sqlite3.Database(dbFile); @@ -118,7 +153,27 @@ exports.seriesTagValues = function(req, res){ } else { res.json({status:"OK", tag: req.params.tag, vals:rows}); } - }) + }); + }); +}; + +exports.seriesTagValuesBetween = function(req, res){ + var sqlite3 = require('sqlite3').verbose(); + var db = new sqlite3.Database(dbFile); + + db.serialize(function(){ + var query = "SELECT * FROM vals WHERE tagID = ? AND dateAdded >= DATETIME(?) AND dateAdded <= DATETIME(?)"; + var prepQuery = db.prepare(query); + prepQuery.all(parseInt(req.params.tag), dString_to_sqlite(req.params.startDatetime), dString_to_sqlite(req.params.endDatetime), function(err, rows){ + prepQuery.finalize(); + db.close(); + if (err){ + console.log(err); + res.json({status:"error", message:err, query:query}); + } else { + res.json({status:"OK", tag: req.params.tag, startDatetime: dString_to_sqlite(req.params.startDatetime), endDatetime: dString_to_sqlite(req.params.endDatetime), vals:rows}); + } + }); }); }; @@ -148,11 +203,39 @@ exports.seriesCSV = function(req, res){ res.set('Content-Disposition', "attachment;filename=tagdata.csv"); res.send(csvString); } - }) + }); }); +}; +exports.seriesCSVBetween = function(req, res){ + var sqlite3 = require('sqlite3').verbose(); + var db = new sqlite3.Database(dbFile); -} + db.serialize(function(){ + var query = "SELECT * FROM vals WHERE tagID = ? AND dateAdded >= DATETIME(?) AND dateAdded <= DATETIME(?)"; + var prepQuery = db.prepare(query); + prepQuery.all(parseInt(req.params.tag), dString_to_sqlite(req.params.startDatetime), dString_to_sqlite(req.params.endDatetime), function(err, rows){ + prepQuery.finalize(); + db.close(); + if (err){ + console.log(err); + res.json({status:"error", message:err, query:query}); + } else { + var csvString = ""; + var h = ["ID", "Value", "DateAdded"]; + csvString = csvString + h.join(",") + "\r"; + for (var i= 0; i < rows.length; i++){ + var r = [rows[i].id, rows[i].val, rows[i].dateAdded]; + csvString = csvString + r.join(",") + "\r"; + } + + res.set('Content-Type', 'text/csv'); + res.set('Content-Disposition', "attachment;filename=tagdata.csv"); + res.send(csvString); + } + }); + }); +}; exports.latestValueSingleTag = function(req, res){ var sqlite3 = require('sqlite3').verbose(); diff --git a/www/public/js/controller.js b/www/public/js/controller.js index 218bab6..b39f4f2 100644 --- a/www/public/js/controller.js +++ b/www/public/js/controller.js @@ -1,4 +1,67 @@ -var tsCtrlrs = angular.module('tsCtrlrs', ['ngJustGage', 'n3-line-chart']); +var tsCtrlrs = angular.module('tsCtrlrs', ['ngJustGage', 'n3-line-chart', "ngQuickDate"]); + +function isValidDate(d) { + if ( Object.prototype.toString.call(d) !== "[object Date]" ) + return false; + return !isNaN(d.getTime()); +} + +tsCtrlrs.config(function(ngQuickDateDefaultsProvider) { + // Configure with icons from font-awesome + return ngQuickDateDefaultsProvider.set({ + closeButtonHtml: "", + buttonIconHtml: "", + nextLinkHtml: "", + prevLinkHtml: "", + // Take advantage of Sugar.js date parsing + parseDateFunction: function(str) { + d = Date.create(str); + return d.isValid() ? d : null; + } + }); +}); + +var dString_to_sqlite = function(dString){ + /** + * Takes a date string in the form YYYYMMDD_HHmmSS and returns it in SQLite format (YYYY-MM-DD HH:mm:SS) + * @param {String} dString + * @return {String} sqliteString + */ + var re = /(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})/; + var fd = re.exec(dString); + if (fd){ + var sqliteString = ""; + return sqliteString.concat(fd[1], "-", fd[2], "-", fd[3], " ", fd[4], ":", fd[5], ":", fd[6]); + } else { + return null; + } +}; + +var sqlite_to_dString = function(sqliteDate){ + /** + * Takes a sqlite date string in the form YYYY-MM-DD HH:mm:SS and returns it in format YYYYMMDD_HHmmSS + * @param {String} sqliteDate + * @return {String} dString + */ + var re = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/; + var fd = re.exec(sqliteDate); + if (fd){ + var dString = ""; + return dString.concat(fd[1], fd[2], fd[3], "_", fd[4], fd[5], fd[6]); + } else { + return null; + } +}; + +var date_to_dString = function(inpDate){ + var year = inpDate.getUTCFullYear().pad(4); + var month = (inpDate.getUTCMonth() + 1).pad(2); + var day = inpDate.getUTCDate().pad(2); + var hour = inpDate.getUTCHours().pad(2); + var min = inpDate.getUTCMinutes().pad(2); + var sec = inpDate.getUTCSeconds().pad(2); + return "".concat(year, month, day, "_", hour, min, sec); +}; tsCtrlrs.factory('Page', function($log) { var title = 'default'; @@ -92,6 +155,26 @@ tsCtrlrs.factory('tags',function($q, $http, $log){ return deferred.promise; }; + var getTagHistoryBetween = function(id, start, end){ + var deferred = $q.defer(); + var url = '/json/valBetween/'+ id + "/" + date_to_dString(start) + "/" + date_to_dString(end); + $log.info("getting value for: " + url); + $http.get(url).success(function(data) { + if(data.status == "OK"){ + deferred.resolve({ + vals:data.vals, + status: data.status + }); + } else { + deferred.resolve({ + status:data.status, + message: data.message + }); + } + }); + return deferred.promise; + }; + var getCurrentValues = function(){ var deferred = $q.defer(); $http.get('/json/all').success(function(data) { @@ -155,6 +238,7 @@ tsCtrlrs.factory('tags',function($q, $http, $log){ getTag: getTag, getTagList: getTagList, getTagHistory: getTagHistory, + getTagHistoryBetween: getTagHistoryBetween, getCurrentValues: getCurrentValues, createTag: createTag, updateTag: updateTag, @@ -208,7 +292,7 @@ tsCtrlrs.controller('tagsCtrl', function($scope, $route, $http, $routeParams, Pa var createStatus = tags.createTag($scope.newTag); $scope.createStatus = createStatus.status; $scope.loadTagList(); - } + }; $scope.openDeleteTag = function(id){ var getTag = tags.getTag(id); @@ -233,7 +317,7 @@ tsCtrlrs.controller('tagsCtrl', function($scope, $route, $http, $routeParams, Pa } else { $scope.error = data.message; } - }) + }); }; $scope.openEditTag = function(id){ @@ -254,22 +338,36 @@ tsCtrlrs.controller('tagsCtrl', function($scope, $route, $http, $routeParams, Pa $log.info(editStatus); $scope.editStatus = editStatus.status; $scope.loadTagList(); - } + }; }); tsCtrlrs.controller('tagValsCtrl', function($scope, $route, $http, $routeParams, Page, Alerts, $log, tags) { Page.setTitle('Tag Series'); Page.setPage('tags'); - $scope.loadTagVals = function(){ + + if ($routeParams.endDatetime){ + $scope.endDatetime = Date.parse(dString_to_sqlite($routeParams.endDatetime)); + } else { + $scope.endDatetime = new Date(); + } + + if ($routeParams.startDatetime){ + $scope.startDatetime = Date.parse(dString_to_sqlite($routeParams.startDatetime)); + } else { + $scope.startDatetime = new Date(); + $scope.startDatetime.setHours($scope.endDatetime.getHours() - 2); + } + + $scope.loadTagVals = function(sDTime, eDTime){ $scope.loading = true; var getTag = tags.getTag($routeParams.tagID); getTag.then(function(tagData){ if (tagData.status == "OK"){ $scope.tag = tagData.tag; Page.setTitle('Tag Series: '+ tagData.tag.tagName); - var getTagHistory = tags.getTagHistory($routeParams.tagID); - getTagHistory.then(function(data) { + var getTagHistoryBetween = tags.getTagHistoryBetween($routeParams.tagID, sDTime, eDTime); + getTagHistoryBetween.then(function(data) { $scope.loading = false; if (data.status == "OK"){ $scope.data = data; @@ -304,11 +402,19 @@ tsCtrlrs.controller('tagValsCtrl', function($scope, $route, $http, $routeParams, } }); }; - $scope.loadTagVals(); - - - - - - + $scope.loadTagVals($scope.startDatetime, $scope.endDatetime); +}); + +tsCtrlrs.filter('dString', function myDateFormat($filter){ + return function(text){ + var tempdate= new Date(text); + return $filter('date')(tempdate, "yyyyMMdd'_'HHmmss"); + }; +}); + +tsCtrlrs.filter('sqlite_to_local', function sqliteformat($filter){ + return function(text){ + var utcdate= new Date(text + " UTC"); + return $filter('date')(utcdate, "yyyy-MM-dd hh:mm:ss a"); + }; }); diff --git a/www/public/js/sugar.min.js b/www/public/js/sugar.min.js new file mode 100644 index 0000000..6bc9d59 --- /dev/null +++ b/www/public/js/sugar.min.js @@ -0,0 +1,132 @@ +/* + * Sugar Library v1.4.1 + * + * Freely distributable and licensed under the MIT-style license. + * Copyright (c) 2014 Andrew Plummer + * http://sugarjs.com/ + * + * ---------------------------- */ +(function(){function aa(a){return function(){return a}} +var m=Object,p=Array,q=RegExp,r=Date,s=String,t=Number,u=Math,ba="undefined"!==typeof global?global:this,v=m.prototype.toString,da=m.prototype.hasOwnProperty,ea=m.defineProperty&&m.defineProperties,fa="function"===typeof q(),ga=!("0"in new s("a")),ia={},ja=/^\[object Date|Array|String|Number|RegExp|Boolean|Arguments\]$/,w="Boolean Number String Array Date RegExp Function".split(" "),la=ka("boolean",w[0]),y=ka("number",w[1]),z=ka("string",w[2]),A=ma(w[3]),C=ma(w[4]),D=ma(w[5]),F=ma(w[6]); +function ma(a){var b="Array"===a&&p.isArray||function(b,d){return(d||v.call(b))==="[object "+a+"]"};return ia[a]=b}function ka(a,b){function c(c){return G(c)?v.call(c)==="[object "+b+"]":typeof c===a}return ia[b]=c} +function na(a){a.SugarMethods||(oa(a,"SugarMethods",{}),H(a,!1,!0,{extend:function(b,c,d){H(a,!1!==d,c,b)},sugarRestore:function(){return pa(this,a,arguments,function(a,c,d){oa(a,c,d.method)})},sugarRevert:function(){return pa(this,a,arguments,function(a,c,d){d.existed?oa(a,c,d.original):delete a[c]})}}))}function H(a,b,c,d){var e=b?a.prototype:a;na(a);I(d,function(d,f){var h=e[d],l=J(e,d);F(c)&&h&&(f=qa(h,f,c));!1===c&&h||oa(e,d,f);a.SugarMethods[d]={method:f,existed:l,original:h,instance:b}})} +function K(a,b,c,d,e){var g={};d=z(d)?d.split(","):d;d.forEach(function(a,b){e(g,a,b)});H(a,b,c,g)}function pa(a,b,c,d){var e=0===c.length,g=L(c),f=!1;I(b.SugarMethods,function(b,c){if(e||-1!==g.indexOf(b))f=!0,d(c.instance?a.prototype:a,b,c)});return f}function qa(a,b,c){return function(d){return c.apply(this,arguments)?b.apply(this,arguments):a.apply(this,arguments)}}function oa(a,b,c){ea?m.defineProperty(a,b,{value:c,configurable:!0,enumerable:!1,writable:!0}):a[b]=c} +function L(a,b,c){var d=[];c=c||0;var e;for(e=a.length;cb&&(d=1/d);return c(a*d)/d}var Ea=48,Fa=57,Ga=65296,Ha=65305,Ia=".",Ja="",Ka={},La;function Ma(){return"\t\n\x0B\f\r \u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u2028\u2029\u3000\ufeff"}function Na(a,b){var c="";for(a=a.toString();0>=1)a+=a;return c} +function Oa(a,b){var c,d;c=a.replace(La,function(a){a=Ka[a];a===Ia&&(d=!0);return a});return d?parseFloat(c):parseInt(c,b||10)}function T(a,b,c,d){d=P(a).toString(d||10);d=Na("0",b-d.replace(/\.\d+/,"").length)+d;if(c||0>a)d=(0>a?"-":"+")+d;return d}function Pa(a){if(11<=a&&13>=a)return"th";switch(a%10){case 1:return"st";case 2:return"nd";case 3:return"rd";default:return"th"}} +function Qa(a,b){function c(a,c){if(a||-1(f?1:2)))return Xa(a,e,b[0],f,c);d=[];L(b,function(b){if(la(b))return!1;d.push(Xa(a,e,b,f,c))});return d}function Xa(a,b,c,d,e){d&&(c%=b,0>c&&(c=b+c));return e?a.charAt(c):a[c]}function Ya(a,b){K(b,!0,!1,a,function(a,b){a[b+("equal"===b?"s":"")]=function(){return m[b].apply(null,[this].concat(L(arguments)))}})}na(m);I(w,function(a,b){na(ba[b])});var Za,$a; +for($a=0;9>=$a;$a++)Za=s.fromCharCode($a+Ga),Ja+=Za,Ka[Za]=s.fromCharCode($a+Ea);Ka[","]="";Ka["\uff0e"]=Ia;Ka[Ia]=Ia;La=q("["+Ja+"\uff0e,"+Ia+"]","g"); +"use strict";H(m,!1,!1,{keys:function(a){var b=[];if(!G(a)&&!D(a)&&!F(a))throw new TypeError("Object required");I(a,function(a){b.push(a)});return b}}); +function ab(a,b,c,d){var e=a.length,g=-1==d,f=g?e-1:0;c=isNaN(c)?f:parseInt(c>>0);0>c&&(c=e+c);if(!g&&0>c||g&&c>=e)c=f;for(;g&&0<=c||!g&&cc&&(c=a.length+c);c=isNaN(c)?0:c;for(!0===d&&(e+=c);c>>0==e&&4294967295!=e)&&e>=c&&d.push(parseInt(e));d.sort().each(function(c){return b.call(a,a[c],c,a)})}function mb(a,b,c,d,e,g){var f,h,l;0g||n&&f=Ea&&c<=Fa||c>=Ga&&c<=Ha)&&(d>=Ea&&d<=Fa||d>=Ga&&d<=Ha))&&(c=Oa(a.slice(f)),d=Oa(b.slice(f)))):(e=e!==a.charAt(f),g=g!==b.charAt(f),e!==g&&0===h&&(h=e-g)),f+=1;while(null!=c&&null!=d&&c===d);return c===d?h:c-d} +function Cb(a,b,c){z(a)||(a=s(a));c&&(a=a.toLowerCase());b&&(a=a.replace(b,""));return a}var Ab="AlphanumericSortOrder",xb="AlphanumericSortIgnore",yb="AlphanumericSortIgnoreCase",zb="AlphanumericSortEquivalents",Bb="AlphanumericSortNatural";H(p,!1,!0,{create:function(){var a=[];L(arguments,function(b){if(!ua(b)&&"length"in b&&("[object Arguments]"===v.call(b)||b.callee)||!ua(b)&&"length"in b&&!z(b)&&!va(b))b=p.prototype.slice.call(b,0);a=a.concat(b)});return a}}); +H(p,!0,!1,{find:function(a,b){ta(a);return mb(this,a,0,!1,!1,b)},findIndex:function(a,b){var c;ta(a);c=mb(this,a,0,!1,!0,b);return N(c)?-1:c}}); +H(p,!0,!0,{findFrom:function(a,b,c){return mb(this,a,b,c)},findIndexFrom:function(a,b,c){b=mb(this,a,b,c,!0);return N(b)?-1:b},findAll:function(a,b,c){var d=[],e;0a&&(a=0);return this.slice(0,a)},last:function(a){return N(a)?this[this.length-1]:this.slice(0>this.length-a?0:this.length-a)}, +from:function(a){return this.slice(a)},to:function(a){N(a)&&(a=this.length);return this.slice(0,a)},min:function(a,b){return tb(this,a,"min",b)},max:function(a,b){return tb(this,a,"max",b)},least:function(a,b){return tb(this.groupBy.apply(this,[a]),"length","min",b)},most:function(a,b){return tb(this.groupBy.apply(this,[a]),"length","max",b)},sum:function(a){a=a?this.map(a):this;return 0f?1:0)*(b?-1:1)});return c},randomize:function(){for(var a=this.concat(),b=a.length,c,d;b;)c=u.random()*b|0,d=a[--b],a[b]=a[c],a[c]=d;return a},zip:function(){var a=L(arguments);return this.map(function(b,c){return[b].concat(a.map(function(a){return c in a?a[c]:null}))})},sample:function(a){var b=this.randomize();return 0=k[1]&&c<=(k[2]|| +k[1])&&e.push(a)}),d=e),d=Ub(d));h?h="(?:"+d+")":(c||g.push(E),h="("+d+")");B&&(h+="?");return h});b?(b=Vb(f,e),e=["t","[\\s\\u3000]"].concat(f.timeMarker),h=a.match(/\\d\{\d,\d\}\)+\??$/),Wb(f,"(?:"+b+")[,\\s\\u3000]+?"+a,Jb.concat(g),d),Wb(f,a+"(?:[,\\s]*(?:"+e.join("|")+(h?"+":"*")+")"+b+")?",g.concat(Jb),d)):Wb(f,a,g,d)}}; +function Xb(a,b,c){var d,e,g=b[0],f=b[1],h=b[2];b=a[c]||a.relative;if(F(b))return b.call(a,g,f,h,c);e=a.units[8*(a.plural&&1Ba?Ba+7:Ba))}function h(){var a=B.i[k.edge];fc(function(a){if(M(k[a]))return E=a,!1},4);if("year"===E)k.e="month";else if("month"===E||"week"===E)k.e="day";n[(0>a.value?"endOf":"beginningOf")+Yb(E)]();-2===a.value&&n.reset()}function l(){var a;fc(function(b,c,d){"day"===b&&(b="date");if(M(k[b])){if(d>=wb)return n.setTime(NaN),!1;a=a||{};a[b]=k[b]; +delete k[b]}});a&&e(function(){n.set(a,!0)})}var n,x,ha,vb,B,k,E,wb,Ba,ra,ca;n=cc();vb=[];n.utc(d);C(a)?n.utc(a.isUTC()).setTime(a.getTime()):y(a)?n.setTime(a):G(a)?(n.set(a,!0),k=a):z(a)&&(ha=Y(b),a=hc(a),ha&&I(ha.o?[ha.o].concat(ha.g):ha.g,function(c,d){var g=a.match(d.q);if(g){B=d.locale;k=gc(g,d.to);B.o=d;k.utc&&n.utc();if(k.timestamp)return k=k.timestamp,!1;d.r&&(!z(k.month)&&(z(k.date)||Zb(ha,b)))&&(ca=k.month,k.month=k.date,k.date=ca);k.year&&2===k.t.length&&(k.year=100*R(U(cc(),"FullYear")/ +100)-100*R(k.year/100)+k.year);k.month&&(k.month=B.getMonth(k.month),k.shift&&!k.unit&&(k.unit=B.units[7]));k.weekday&&k.date?delete k.weekday:k.weekday&&(k.weekday=B.getWeekday(k.weekday),k.shift&&!k.unit&&(k.unit=B.units[5]));k.day&&(ca=B.i[k.day])?(k.day=ca.value,n.reset(),x=!0):k.day&&-1<(Ba=B.getWeekday(k.day))&&(delete k.day,k.num&&k.month?(e(f),k.day=1):k.weekday=Ba);k.date&&!y(k.date)&&(k.date=$b(B,k.date));k.ampm&&k.ampm===B.ampm[1]&&12>k.hour?k.hour+=12:k.ampm===B.ampm[0]&&12===k.hour&& +(k.hour=0);if("offset_hours"in k||"offset_minutes"in k)n.utc(),k.offset_minutes=k.offset_minutes||0,k.offset_minutes+=60*k.offset_hours,"-"===k.offset_sign&&(k.offset_minutes*=-1),k.minute-=k.offset_minutes;k.unit&&(x=!0,ra=ac(B,k.num),wb=B.units.indexOf(k.unit)%8,E=W.units[wb],l(),k.shift&&(ra*=(ca=B.i[k.shift])?ca.value:0),k.sign&&(ca=B.i[k.sign])&&(ra*=ca.value),M(k.weekday)&&(n.set({weekday:k.weekday},!0),delete k.weekday),k[E]=(k[E]||0)+ra);k.edge&&e(h);"-"===k.year_sign&&(k.year*=-1);fc(function(a, +b,c){b=k[a];var d=b%1;d&&(k[Ob[c-1].name]=R(d*("second"===a?1E3:60)),k[a]=Q(b))},1,4);return!1}}),k?x?n.advance(k):(n._utc&&n.reset(),kc(n,k,!0,!1,c)):("now"!==a&&(n=new r(a)),d&&n.addMinutes(-n.getTimezoneOffset())),g(),n.utc(!1));return{c:n,set:k}}function lc(a){var b,c=P(a),d=c,e=0;fc(function(a,f,h){b=Q(Da(c/f.b(),1));1<=b&&(d=b,e=h)},1);return[d,e,a]} +function mc(a){var b=lc(a.millisecondsFromNow());if(6===b[1]||5===b[1]&&4===b[0]&&a.daysFromNow()>=cc().daysInMonth())b[0]=P(a.monthsFromNow()),b[1]=6;return b}function nc(a,b,c){function d(a,c){var d=U(a,"Month");return Y(c).months[d+12*b]}Z(a,d,c);Z(Yb(a),d,c,1)}function Z(a,b,c,d){X[a]=function(a,g){var f=b(a,g);c&&(f=f.slice(0,c));d&&(f=f.slice(0,d).toUpperCase()+f.slice(d));return f}} +function oc(a,b,c){X[a]=b;X[a+a]=function(a,c){return T(b(a,c),2)};c&&(X[a+a+a]=function(a,c){return T(b(a,c),3)},X[a+a+a+a]=function(a,c){return T(b(a,c),4)})}function pc(a){var b=a.match(/(\{\w+\})|[^{}]+/g);Qb[a]=b.map(function(a){a.replace(/\{(\w+)\}/,function(b,e){a=X[e]||e;return e});return a})} +function qc(a,b,c,d){var e;if(!a.isValid())return"Invalid Date";Date[b]?b=Date[b]:F(b)&&(e=mc(a),b=b.apply(a,e.concat(Y(d))));if(!b&&c)return e=e||mc(a),0===e[1]&&(e[1]=1,e[0]=1),a=Y(d),Xb(a,e,0=b-n&&f<=h+x} +function sc(a,b,c){b=new r(b);a=(new r(c)).utc(a.isUTC());23!==U(a,"Hours")&&(b=b.getTimezoneOffset(),a=a.getTimezoneOffset(),b!==a&&(c+=(a-b).minutes()));return c} +function kc(a,b,c,d,e){function g(a){return M(b[a])?b[a]:b[a+"s"]}function f(a){return M(g(a))}var h;if(y(b)&&d)b={milliseconds:b};else if(y(b))return a.setTime(b),a;M(b.date)&&(b.day=b.date);fc(function(d,e,g){var l="day"===d;if(f(d)||l&&f("weekday"))return b.e=d,h=+g,!1;!c||("week"===d||l&&f("week"))||Sa(a,e.method,l?1:0)});Rb.forEach(function(c){var e=c.name;c=c.method;var h;h=g(e);N(h)||(d?("week"===e&&(h=(b.day||0)+7*h,c="Date"),h=h*d+U(a,c)):"month"===e&&f("day")&&Sa(a,"Date",15),Sa(a,c,h), +d&&"month"===e&&(e=h,0>e&&(e=e%12+12),e%12!=U(a,"Month")&&Sa(a,"Date",0)))});d||(f("day")||!f("weekday"))||a.setWeekday(g("weekday"));var l;a:{switch(e){case -1:l=a>cc();break a;case 1:l=ar.create(a).getTime()-(b||0)},isBefore:function(a,b){return this.getTime()d},isLeapYear:function(){var a=U(this,"FullYear");return 0===a%4&&0!==a%100||0===a%400}, +daysInMonth:function(){return 32-U(new r(U(this,"FullYear"),U(this,"Month"),32),"Date")},format:function(a,b){return qc(this,a,!1,b)},relative:function(a,b){z(a)&&(b=a,a=null);return qc(this,a,!0,b)},is:function(a,b,c){var d,e;if(this.isValid()){if(z(a))switch(a=a.trim().toLowerCase(),e=this.clone().utc(c),!0){case "future"===a:return this.getTime()>cc().getTime();case "past"===a:return this.getTime()U(e,"Day");case "weekend"===a:return 0=== +U(e,"Day")||6===U(e,"Day");case -1<(d=W.weekdays.indexOf(a)%7):return U(e,"Day")===d;case -1<(d=W.months.indexOf(a)%12):return U(e,"Month")===d}return rc(this,a,null,b,c)}},reset:function(a){var b={},c;a=a||"hours";"date"===a&&(a="days");c=Rb.some(function(b){return a===b.name||a===b.name+"s"});b[a]=a.match(/^days?/)?1:0;return c?this.set(b,!0):this},clone:function(){var a=new r(this.getTime());a.utc(!!this._utc);return a}}); +H(r,!0,!0,{iso:function(){return this.toISOString()},getWeekday:r.prototype.getDay,getUTCWeekday:r.prototype.getUTCDay});function uc(a,b){function c(){return R(this*b)}function d(){return tc(arguments)[a.j](this)}function e(){return tc(arguments)[a.j](-this)}var g=a.name,f={};f[g]=c;f[g+"s"]=c;f[g+"Before"]=e;f[g+"sBefore"]=e;f[g+"Ago"]=e;f[g+"sAgo"]=e;f[g+"After"]=d;f[g+"sAfter"]=d;f[g+"FromNow"]=d;f[g+"sFromNow"]=d;t.extend(f)}H(t,!0,!0,{duration:function(a){a=Y(a);return Xb(a,lc(this),"duration")}}); +W=Ib=r.addLocale("en",{plural:!0,timeMarker:"at",ampm:"am,pm",months:"January,February,March,April,May,June,July,August,September,October,November,December",weekdays:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",units:"millisecond:|s,second:|s,minute:|s,hour:|s,day:|s,week:|s,month:|s,year:|s",numbers:"one,two,three,four,five,six,seven,eight,nine,ten",articles:"a,an,the",tokens:"the,st|nd|rd|th,of","short":"{Month} {d}, {yyyy}","long":"{Month} {d}, {yyyy} {h}:{mm}{tt}",full:"{Weekday} {Month} {d}, {yyyy} {h}:{mm}:{ss}{tt}", +past:"{num} {unit} {sign}",future:"{num} {unit} {sign}",duration:"{num} {unit}",modifiers:[{name:"sign",src:"ago|before",value:-1},{name:"sign",src:"from now|after|from|in|later",value:1},{name:"edge",src:"last day",value:-2},{name:"edge",src:"end",value:-1},{name:"edge",src:"first day|beginning",value:1},{name:"shift",src:"last",value:-1},{name:"shift",src:"the|this",value:0},{name:"shift",src:"next",value:1}],dateParse:["{month} {year}","{shift} {unit=5-7}","{0?} {date}{1}","{0?} {edge} of {shift?} {unit=4-7?}{month?}{year?}"], +timeParse:"{num} {unit} {sign};{sign} {num} {unit};{0} {num}{1} {day} of {month} {year?};{weekday?} {month} {date}{1?} {year?};{date} {month} {year};{date} {month};{shift} {weekday};{shift} week {weekday};{weekday} {2?} {shift} week;{num} {unit=4-5} {sign} {day};{0?} {date}{1} of {month};{0?}{month?} {date?}{1?} of {shift} {unit=6-7}".split(";")});Ob=Rb.concat().reverse();Nb=Rb.concat();Nb.splice(2,1); +K(r,!0,!0,Rb,function(a,b,c){function d(a){a/=f;var c=a%1,d=b.error||0.999;c&&P(c%1)>d&&(a=R(a));return 0>a?Aa(a):Q(a)}var e=b.name,g=Yb(e),f=b.b(),h,l;b.j="add"+g+"s";h=function(a,b){return d(this.getTime()-r.create(a,b).getTime())};l=function(a,b){return d(r.create(a,b).getTime()-this.getTime())};a[e+"sAgo"]=l;a[e+"sUntil"]=l;a[e+"sSince"]=h;a[e+"sFromNow"]=h;a[b.j]=function(a,b){var c={};c[e]=a;return this.advance(c,b)};uc(b,f);3>c&&["Last","This","Next"].forEach(function(b){a["is"+b+g]=function(){return rc(this, +b+" "+e,"en")}});4>c&&(a["beginningOf"+g]=function(){var a={};switch(e){case "year":a.year=U(this,"FullYear");break;case "month":a.month=U(this,"Month");break;case "day":a.day=U(this,"Date");break;case "week":a.weekday=0}return this.set(a,!0)},a["endOf"+g]=function(){var a={hours:23,minutes:59,seconds:59,milliseconds:999};switch(e){case "year":a.month=11;a.day=31;break;case "month":a.day=this.daysInMonth();break;case "week":a.weekday=6}return this.set(a,!0)})}); +W.addFormat("([+-])?(\\d{4,4})[-.]?{full_month}[-.]?(\\d{1,2})?",!0,["year_sign","year","month","date"],!1,!0);W.addFormat("(\\d{1,2})[-.\\/]{full_month}(?:[-.\\/](\\d{2,4}))?",!0,["date","month","year"],!0);W.addFormat("{full_month}[-.](\\d{4,4})",!1,["month","year"]);W.addFormat("\\/Date\\((\\d+(?:[+-]\\d{4,4})?)\\)\\/",!1,["timestamp"]);W.addFormat(Vb(W),!1,Jb);Pb=W.g.slice(0,7).reverse();W.g=W.g.slice(7).concat(Pb);oc("f",function(a){return U(a,"Milliseconds")},!0); +oc("s",function(a){return U(a,"Seconds")});oc("m",function(a){return U(a,"Minutes")});oc("h",function(a){return U(a,"Hours")%12||12});oc("H",function(a){return U(a,"Hours")});oc("d",function(a){return U(a,"Date")});oc("M",function(a){return U(a,"Month")+1});(function(){function a(a,c){var d=U(a,"Hours");return Y(c).ampm[Q(d/12)]||""}Z("t",a,1);Z("tt",a);Z("T",a,1,1);Z("TT",a,null,2)})(); +(function(){function a(a,c){var d=U(a,"Day");return Y(c).weekdays[d]}Z("dow",a,3);Z("Dow",a,3,1);Z("weekday",a);Z("Weekday",a,null,1)})();nc("mon",0,3);nc("month",0);nc("month2",1);nc("month3",2);X.ms=X.f;X.milliseconds=X.f;X.seconds=X.s;X.minutes=X.m;X.hours=X.h;X["24hr"]=X.H;X["12hr"]=X.h;X.date=X.d;X.day=X.d;X.year=X.yyyy;K(r,!0,!0,"short,long,full",function(a,b){a[b]=function(a){return qc(this,b,!1,a)}}); +"\u3007\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07".split("").forEach(function(a,b){9=this.start&&a.start<=this.end&&a.end>=this.start&&a.end<=this.end:a>=this.start&&a<=this.end},every:function(a,b){var c,d=this.start,e=this.end,g=e=e:f<=e;)l.push(f),b&&b(f,h),f=c(f,a),h++;return l},union:function(a){return new Range(this.starta.end?this.end:a.end)},intersect:function(a){return a.start>this.end||a.enda.start?this.start:a.start,this.endc?b:c;return vc(ab?b:a)}});[t,s,r].forEach(function(a){H(a,!1,!0,{range:function(b,c){a.create&&(b=a.create(b),c=a.create(c));return new Range(b,c)}})}); +H(t,!0,!0,{upto:function(a,b,c){return t.range(this,a).every(c,b)},clamp:function(a,b){return(new Range(a,b)).clamp(this)},cap:function(a){return this.clamp(void 0,a)}});H(t,!0,!0,{downto:t.prototype.upto});H(p,!1,function(a){return a instanceof Range},{create:function(a){return a.every()}}); +"use strict";function Ac(a,b,c,d,e){Infinity!==b&&(a.timers||(a.timers=[]),y(b)||(b=1),a.n=!1,a.timers.push(setTimeout(function(){a.n||c.apply(d,e||[])},b)))} +H(Function,!0,!0,{lazy:function(a,b,c){function d(){g.lengthb;)x=Function.prototype.apply.apply(e,g.shift()),a--;Ac(d,l,function(){f=!1;h()})}};return d},throttle:function(a){return this.lazy(a,!0,1)},debounce:function(a){function b(){b.cancel();Ac(b,a,c,this,arguments)}var c=this;return b},delay:function(a){var b= +L(arguments,null,1);Ac(this,a,this,this,b);return this},every:function(a){function b(){c.apply(c,d);Ac(c,a,b)}var c=this,d=arguments,d=1=c.length)&&c.splice(b,0,a)});return a.apply(this,c)}}}); +"use strict";function Bc(a,b,c,d,e,g){var f=a.toFixed(20),h=f.search(/\./),f=f.search(/[1-9]/),h=h-f;0h&&(e=-3,b=P(h)-9,d=c.slice(0,1));c=g?za(2,10*e):za(10,3*e);return Da(a/c,b||0).format()+d.trim()} +H(t,!1,!0,{random:function(a,b){var c,d;1==arguments.length&&(b=a,a=0);c=Ca(a||0,N(b)?1:b);d=S(a||0,N(b)?1:b)+1;return Q(u.random()*(d-c)+c)}}); +H(t,!0,!0,{log:function(a){return u.log(this)/(a?u.log(a):1)},abbr:function(a){return Bc(this,a,"kmbt",0,4)},metric:function(a,b){return Bc(this,a,"n\u03bcm kMGTPE",4,N(b)?1:b)},bytes:function(a,b){return Bc(this,a,"kMGTPE",0,N(b)?4:b,!0)+"B"},isInteger:function(){return 0==this%1},isOdd:function(){return!isNaN(this)&&!this.isMultipleOf(2)},isEven:function(){return this.isMultipleOf(2)},isMultipleOf:function(a){return 0===this%a},format:function(a,b,c){var d,e,g,f="";N(b)&&(b=",");N(c)&&(c=".");d= +(y(a)?Da(this,a||0).toFixed(S(a,0)):this.toString()).replace(/^-/,"").split(".");e=d[0];g=d[1];for(d=e.length;0this?"-":"")+f},hex:function(a){return this.pad(a||1,!1,16)},times:function(a){if(a)for(var b=0;ba||Infinity===a)throw new RangeError("Invalid number");return a}function Jc(a,b){return Na(M(b)?b:" ",a)}function Kc(a,b,c,d,e){var g;if(a.length<=b)return a.toString();d=N(d)?"...":d;switch(c){case "left":return a=e?Lc(a,b,!0):a.slice(a.length-b),d+a;case "middle":return c=Aa(b/2),g=Q(b/2),b=e?Lc(a,c):a.slice(0,c),a=e?Lc(a,g,!0):a.slice(a.length-g),b+d+a;default:return b=e?Lc(a,b):a.slice(0,b),b+d}} +function Lc(a,b,c){if(c)return Lc(a.reverse(),b).reverse();c=q("(?=["+Ma()+"])");var d=0;return a.split(c).filter(function(a){d+=a.length;return d<=b}).join("")}function Mc(a,b,c){z(b)&&(b=a.indexOf(b),-1===b&&(b=c?a.length:0));return b}var Nc,Oc;H(s,!0,!1,{repeat:function(a){a=Ic(a);return Na(this,a)}}); +H(s,!0,function(a){return D(a)||2/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")},unescapeHTML:function(){return this.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(///g, +"/").replace(/&/g,"&")},encodeBase64:function(){return Nc(unescape(encodeURIComponent(this)))},decodeBase64:function(){return decodeURIComponent(escape(Oc(this)))},each:function(a,b){var c,d,e;F(a)?(b=a,a=/[\s\S]/g):a?z(a)?a=q(Ra(a),"gi"):D(a)&&(a=q(a.source,Qa(a,"g"))):a=/[\s\S]/g;c=this.match(a)||[];if(b)for(d=0,e=c.length;d]*>","gi"),"")});return a},removeTags:function(){var a=this;sa(0]*(?:\\/>|>.*?<\\/\\1>)","gi");a=a.replace(b,"")});return a},truncate:function(a,b,c){return Kc(this,a,b,c)},truncateOnWord:function(a,b,c){return Kc(this,a,b,c,!0)},pad:function(a,b){var c,d;a=Ic(a);c=S(0,a-this.length)/2;d=Q(c);c=Aa(c);return Jc(d,b)+this+Jc(c,b)},padLeft:function(a,b){a=Ic(a);return Jc(S(0,a- +this.length),b)+this},padRight:function(a,b){a=Ic(a);return this+Jc(S(0,a-this.length),b)},first:function(a){N(a)&&(a=1);return this.substr(0,a)},last:function(a){N(a)&&(a=1);return this.substr(0>this.length-a?0:this.length-a)},toNumber:function(a){return Oa(this,a)},capitalize:function(a){var b;return this.toLowerCase().replace(a?/[^']/g:/^\S/,function(a){var d=a.toUpperCase(),e;e=b?a:d;b=d!==a;return e})},assign:function(){var a={};sa(arguments,function(b,c){G(b)?xa(a,b):a[c+1]=b});return this.replace(/\{([^{]+?)\}/g, +function(b,c){return J(a,c)?a[c]:b})}});H(s,!0,!0,{insert:s.prototype.add}); +(function(a){if(ba.btoa)Nc=ba.btoa,Oc=ba.atob;else{var b=/[^A-Za-z0-9\+\/\=]/g;Nc=function(b){var d="",e,g,f,h,l,n,x=0;do e=b.charCodeAt(x++),g=b.charCodeAt(x++),f=b.charCodeAt(x++),h=e>>2,e=(e&3)<<4|g>>4,l=(g&15)<<2|f>>6,n=f&63,isNaN(g)?l=n=64:isNaN(f)&&(n=64),d=d+a.charAt(h)+a.charAt(e)+a.charAt(l)+a.charAt(n);while(x>4,g=(g&15)<<4|h>>2,f=(h&3)<<6|l,d+=s.fromCharCode(e),64!=h&&(d+=s.fromCharCode(g)),64!=l&&(d+=s.fromCharCode(f));while(n
', - trigger: 'hover focus', - title: '', - delay: 0, - html: false, - container: false, - viewport: { - selector: 'body', - padding: 0 - } - } - - Tooltip.prototype.init = function (type, element, options) { - this.enabled = true - this.type = type - this.$element = $(element) - this.options = this.getOptions(options) - this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) - this.inState = { click: false, hover: false, focus: false } - - if (this.$element[0] instanceof document.constructor && !this.options.selector) { - throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') - } - - var triggers = this.options.trigger.split(' ') - - for (var i = triggers.length; i--;) { - var trigger = triggers[i] - - if (trigger == 'click') { - this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) - } else if (trigger != 'manual') { - var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' - var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' - - this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) - this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) - } - } - - this.options.selector ? - (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : - this.fixTitle() - } - - Tooltip.prototype.getDefaults = function () { - return Tooltip.DEFAULTS - } - - Tooltip.prototype.getOptions = function (options) { - options = $.extend({}, this.getDefaults(), this.$element.data(), options) - - if (options.delay && typeof options.delay == 'number') { - options.delay = { - show: options.delay, - hide: options.delay - } - } - - return options - } - - Tooltip.prototype.getDelegateOptions = function () { - var options = {} - var defaults = this.getDefaults() - - this._options && $.each(this._options, function (key, value) { - if (defaults[key] != value) options[key] = value - }) - - return options - } - - Tooltip.prototype.enter = function (obj) { - var self = obj instanceof this.constructor ? - obj : $(obj.currentTarget).data('bs.' + this.type) - - if (!self) { - self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) - $(obj.currentTarget).data('bs.' + this.type, self) - } - - if (obj instanceof $.Event) { - self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true - } - - if (self.tip().hasClass('in') || self.hoverState == 'in') { - self.hoverState = 'in' - return - } - - clearTimeout(self.timeout) - - self.hoverState = 'in' - - if (!self.options.delay || !self.options.delay.show) return self.show() - - self.timeout = setTimeout(function () { - if (self.hoverState == 'in') self.show() - }, self.options.delay.show) - } - - Tooltip.prototype.isInStateTrue = function () { - for (var key in this.inState) { - if (this.inState[key]) return true - } - - return false - } - - Tooltip.prototype.leave = function (obj) { - var self = obj instanceof this.constructor ? - obj : $(obj.currentTarget).data('bs.' + this.type) - - if (!self) { - self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) - $(obj.currentTarget).data('bs.' + this.type, self) - } - - if (obj instanceof $.Event) { - self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false - } - - if (self.isInStateTrue()) return - - clearTimeout(self.timeout) - - self.hoverState = 'out' - - if (!self.options.delay || !self.options.delay.hide) return self.hide() - - self.timeout = setTimeout(function () { - if (self.hoverState == 'out') self.hide() - }, self.options.delay.hide) - } - - Tooltip.prototype.show = function () { - var e = $.Event('show.bs.' + this.type) - - if (this.hasContent() && this.enabled) { - this.$element.trigger(e) - - var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) - if (e.isDefaultPrevented() || !inDom) return - var that = this - - var $tip = this.tip() - - var tipId = this.getUID(this.type) - - this.setContent() - $tip.attr('id', tipId) - this.$element.attr('aria-describedby', tipId) - - if (this.options.animation) $tip.addClass('fade') - - var placement = typeof this.options.placement == 'function' ? - this.options.placement.call(this, $tip[0], this.$element[0]) : - this.options.placement - - var autoToken = /\s?auto?\s?/i - var autoPlace = autoToken.test(placement) - if (autoPlace) placement = placement.replace(autoToken, '') || 'top' - - $tip - .detach() - .css({ top: 0, left: 0, display: 'block' }) - .addClass(placement) - .data('bs.' + this.type, this) - - this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) - this.$element.trigger('inserted.bs.' + this.type) - - var pos = this.getPosition() - var actualWidth = $tip[0].offsetWidth - var actualHeight = $tip[0].offsetHeight - - if (autoPlace) { - var orgPlacement = placement - var viewportDim = this.getPosition(this.$viewport) - - placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : - placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : - placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : - placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : - placement - - $tip - .removeClass(orgPlacement) - .addClass(placement) - } - - var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) - - this.applyPlacement(calculatedOffset, placement) - - var complete = function () { - var prevHoverState = that.hoverState - that.$element.trigger('shown.bs.' + that.type) - that.hoverState = null - - if (prevHoverState == 'out') that.leave(that) - } - - $.support.transition && this.$tip.hasClass('fade') ? - $tip - .one('bsTransitionEnd', complete) - .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : - complete() - } - } - - Tooltip.prototype.applyPlacement = function (offset, placement) { - var $tip = this.tip() - var width = $tip[0].offsetWidth - var height = $tip[0].offsetHeight - - // manually read margins because getBoundingClientRect includes difference - var marginTop = parseInt($tip.css('margin-top'), 10) - var marginLeft = parseInt($tip.css('margin-left'), 10) - - // we must check for NaN for ie 8/9 - if (isNaN(marginTop)) marginTop = 0 - if (isNaN(marginLeft)) marginLeft = 0 - - offset.top += marginTop - offset.left += marginLeft - - // $.fn.offset doesn't round pixel values - // so we use setOffset directly with our own function B-0 - $.offset.setOffset($tip[0], $.extend({ - using: function (props) { - $tip.css({ - top: Math.round(props.top), - left: Math.round(props.left) - }) - } - }, offset), 0) - - $tip.addClass('in') - - // check to see if placing tip in new offset caused the tip to resize itself - var actualWidth = $tip[0].offsetWidth - var actualHeight = $tip[0].offsetHeight - - if (placement == 'top' && actualHeight != height) { - offset.top = offset.top + height - actualHeight - } - - var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) - - if (delta.left) offset.left += delta.left - else offset.top += delta.top - - var isVertical = /top|bottom/.test(placement) - var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight - var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' - - $tip.offset(offset) - this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) - } - - Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { - this.arrow() - .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') - .css(isVertical ? 'top' : 'left', '') - } - - Tooltip.prototype.setContent = function () { - var $tip = this.tip() - var title = this.getTitle() - - $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) - $tip.removeClass('fade in top bottom left right') - } - - Tooltip.prototype.hide = function (callback) { - var that = this - var $tip = $(this.$tip) - var e = $.Event('hide.bs.' + this.type) - - function complete() { - if (that.hoverState != 'in') $tip.detach() - that.$element - .removeAttr('aria-describedby') - .trigger('hidden.bs.' + that.type) - callback && callback() - } - - this.$element.trigger(e) - - if (e.isDefaultPrevented()) return - - $tip.removeClass('in') - - $.support.transition && $tip.hasClass('fade') ? - $tip - .one('bsTransitionEnd', complete) - .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : - complete() - - this.hoverState = null - - return this - } - - Tooltip.prototype.fixTitle = function () { - var $e = this.$element - if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { - $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') - } - } - - Tooltip.prototype.hasContent = function () { - return this.getTitle() - } - - Tooltip.prototype.getPosition = function ($element) { - $element = $element || this.$element - - var el = $element[0] - var isBody = el.tagName == 'BODY' - - var elRect = el.getBoundingClientRect() - if (elRect.width == null) { - // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 - elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) - } - var elOffset = isBody ? { top: 0, left: 0 } : $element.offset() - var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } - var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null - - return $.extend({}, elRect, scroll, outerDims, elOffset) - } - - Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { - return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : - placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : - placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : - /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } - - } - - Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { - var delta = { top: 0, left: 0 } - if (!this.$viewport) return delta - - var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 - var viewportDimensions = this.getPosition(this.$viewport) - - if (/right|left/.test(placement)) { - var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll - var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight - if (topEdgeOffset < viewportDimensions.top) { // top overflow - delta.top = viewportDimensions.top - topEdgeOffset - } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow - delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset - } - } else { - var leftEdgeOffset = pos.left - viewportPadding - var rightEdgeOffset = pos.left + viewportPadding + actualWidth - if (leftEdgeOffset < viewportDimensions.left) { // left overflow - delta.left = viewportDimensions.left - leftEdgeOffset - } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow - delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset - } - } - - return delta - } - - Tooltip.prototype.getTitle = function () { - var title - var $e = this.$element - var o = this.options - - title = $e.attr('data-original-title') - || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) - - return title - } - - Tooltip.prototype.getUID = function (prefix) { - do prefix += ~~(Math.random() * 1000000) - while (document.getElementById(prefix)) - return prefix - } - - Tooltip.prototype.tip = function () { - if (!this.$tip) { - this.$tip = $(this.options.template) - if (this.$tip.length != 1) { - throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') - } - } - return this.$tip - } - - Tooltip.prototype.arrow = function () { - return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) - } - - Tooltip.prototype.enable = function () { - this.enabled = true - } - - Tooltip.prototype.disable = function () { - this.enabled = false - } - - Tooltip.prototype.toggleEnabled = function () { - this.enabled = !this.enabled - } - - Tooltip.prototype.toggle = function (e) { - var self = this - if (e) { - self = $(e.currentTarget).data('bs.' + this.type) - if (!self) { - self = new this.constructor(e.currentTarget, this.getDelegateOptions()) - $(e.currentTarget).data('bs.' + this.type, self) - } - } - - if (e) { - self.inState.click = !self.inState.click - if (self.isInStateTrue()) self.enter(self) - else self.leave(self) - } else { - self.tip().hasClass('in') ? self.leave(self) : self.enter(self) - } - } - - Tooltip.prototype.destroy = function () { - var that = this - clearTimeout(this.timeout) - this.hide(function () { - that.$element.off('.' + that.type).removeData('bs.' + that.type) - if (that.$tip) { - that.$tip.detach() - } - that.$tip = null - that.$arrow = null - that.$viewport = null - }) - } - - - // TOOLTIP PLUGIN DEFINITION - // ========================= - - function Plugin(option) { - return this.each(function () { - var $this = $(this) - var data = $this.data('bs.tooltip') - var options = typeof option == 'object' && option - - if (!data && /destroy|hide/.test(option)) return - if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) - if (typeof option == 'string') data[option]() - }) - } - - var old = $.fn.tooltip - - $.fn.tooltip = Plugin - $.fn.tooltip.Constructor = Tooltip - - - // TOOLTIP NO CONFLICT - // =================== - - $.fn.tooltip.noConflict = function () { - $.fn.tooltip = old - return this - } - -}(jQuery); diff --git a/www/public/partials/tagVals.html b/www/public/partials/tagVals.html index d5c057b..c003cb0 100644 --- a/www/public/partials/tagVals.html +++ b/www/public/partials/tagVals.html @@ -3,6 +3,9 @@ margin: 10px 20px; } +.timeLabel { + font-size: 1.2em; +} @@ -27,16 +30,21 @@
+
+
+

{{tag.vanityName}}

+ From: To: +
+
- - Download Data + + Download Data
-

{{tag.vanityName}}

@@ -49,11 +57,11 @@ - +
{{val.id}} {{val.val}} {{tag.units}}{{val.dateAdded}}{{val.dateAdded | sqlite_to_local}}
- \ No newline at end of file + diff --git a/www/views/angularIndex.html b/www/views/angularIndex.html index 03b1c4e..96c4277 100644 --- a/www/views/angularIndex.html +++ b/www/views/angularIndex.html @@ -7,6 +7,7 @@ + @@ -21,9 +22,13 @@ + + + + - + @@ -48,7 +53,7 @@