From 4da445d78f2e74b4e98ce551990a20a8bdc27644 Mon Sep 17 00:00:00 2001 From: Patrick McDonagh Date: Thu, 28 Jan 2016 10:57:33 -0600 Subject: [PATCH] Added vanity name and description, loading screen, pocloud driver --- tsdriver.py | 145 ++++++++ www/dbcreate_SQLite.sql | 4 +- www/functions_SQLite.js | 7 +- www/public/img/loading.gif | Bin 0 -> 39507 bytes www/public/js/controller.js | 2 +- www/public/js/tooltip.js | 514 +++++++++++++++++++++++++++++ www/public/partials/dashboard.html | 29 +- www/public/partials/tagVals.html | 67 ++-- www/public/partials/tags.html | 73 ++-- www/views/angularIndex.html | 3 +- 10 files changed, 767 insertions(+), 77 deletions(-) create mode 100644 tsdriver.py create mode 100644 www/public/img/loading.gif create mode 100644 www/public/js/tooltip.js diff --git a/tsdriver.py b/tsdriver.py new file mode 100644 index 0000000..d19091f --- /dev/null +++ b/tsdriver.py @@ -0,0 +1,145 @@ +#!/usr/bin/python + +import types +import traceback +import binascii +import threading +import time +import thread +import os +import struct +import sys +import serial +import minimalmodbus +import pickle +import re +from device_base import deviceBase + +import requests +try: + import json +except: + import simplejson as json + +channels = {} +min_upload_time = 30 +addr = 'http://192.168.1.30:3000' + + +def setupChannels(): + tagJSObj = json.loads(requests.get(addr + "/json/tag").text) + if tagJSObj['status'] == "OK": + for t in tagJSObj['tags']: + channels[str(t['tagName'])] = { + 'tagID': t['id'], + 'last_value': -999, + 'data_type': "float", + 'last_time_uploaded': 0, + 'change_amount': (t['maxExpected'] - t['minExpected']) / 20, + 'min_time_between_uploads': min_upload_time + } + +# channels = { +# "DC_Bus_Voltage": { +# "last_value": -999, +# "data_type": "float", +# "change_amount": 5, +# "last_time_uploaded": 0, +# "min_time_between_uploads": 30 +# }, +# "Output_Frequency": { +# "last_value": -999, +# "data_type": "float", +# "change_amount": 1, +# "last_time_uploaded": 0, +# "min_time_between_uploads": 30 +# }, +# "Output_Current": { +# "last_value": -999, +# "data_type": "float", +# "change_amount": .2, +# "last_time_uploaded": 0, +# "min_time_between_uploads": 30 +# }, +# "Output_Voltage": { +# "last_value": -999, +# "data_type": "float", +# "change_amount": 5, +# "last_time_uploaded": 0, +# "min_time_between_uploads": 30 +# } +# } + + +class start(threading.Thread, deviceBase): + + + def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, companyId=None, offset=None, mqtt=None, Nodes=None): + threading.Thread.__init__(self) + deviceBase.__init__(self, name=name, number=number, mac=mac, Q=Q, mcu=mcu, companyId=companyId, offset=offset, mqtt=mqtt, Nodes=Nodes) + + self.daemon = True + self.version = "1" + self.device_address = addr + self.finished = threading.Event() + threading.Thread.start(self) + self.sendtodbJSON("device_address", self.device_address, 0) + setupChannels() + # this is a required function for all drivers, its goal is to upload some piece of data + # about your device so it can be seen on the web + def register(self): + channels["status"]["last_value"] = "" + + def run(self): + self.runLoopStatus = "" + last_OK_state = 0 + while True: + if len(channels) > 0: + try: + for i in channels: + runLoopStatus = i + valData = self.checkTag(i) + if valData: + nowVal = valData['val'] + ch = channels[i] + if (abs(ch['last_value'] - nowVal) > ch['change_amount']) or ((time.time() - ch['last_time_uploaded']) > ch['min_time_between_uploads']): + self.sendtodbJSON(i, nowVal, 0) + ch['last_time_uploaded'] = time.time() + ch['last_value'] = nowVal + + runLoopStatus = "Complete" + OK_state = 1 + if not OK_state == last_OK_state: + self.sendtodbJSON("driver_ok", OK_state, 0) + last_OK_state = OK_state + time.sleep(3) + except Exception, e: + OK_state = 0 + if not OK_state == last_OK_state: + self.sendtodbJSON("driver_ok", OK_state, 0) + last_OK_state = OK_state + sleep_timer = 20 + print "Error during {0} of run loop: {1}\nWill try again in {2} seconds...".format(runLoopStatus, e, sleep_timer) + time.sleep(sleep_timer) + else: + setupChannels() + time.sleep(30) + + def checkTag(self, tagID): + tagData = json.loads(requests.get(self.device_address + "/json/val/"+tagID).text) + # {u'tag_val': {u'tagID': 1, u'dateAdded': u'2015-12-08T23:43:38.000Z', u'id': 60, u'val': 56}} + return(tagData["tag_val"]) + + def tagserver_sync(self, name, value): + self.sendtodb("connected", "true", 0) + return True + + def tagserver_address(self, name, value): + self.device_address = value + return True + + def tagserver_gpsUpdate(self, name, value): + gps = self.mcu.gps + print("GPS found me at {0}".format(gps)) + self.sendtodb("gps", gps, 0) + return True diff --git a/www/dbcreate_SQLite.sql b/www/dbcreate_SQLite.sql index a8fa8bc..dbcaefa 100644 --- a/www/dbcreate_SQLite.sql +++ b/www/dbcreate_SQLite.sql @@ -1,10 +1,12 @@ CREATE TABLE IF NOT EXISTS tags ( id INTEGER PRIMARY KEY, tagName TEXT, - dateAdded TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + vanityName TEXT, + description TEXT, units TEXT, minExpected REAL, maxExpected REAL, + dateAdded TIMESTAMP DEFAULT CURRENT_TIMESTAMP, deleted INTEGER DEFAULT 0 ); diff --git a/www/functions_SQLite.js b/www/functions_SQLite.js index 08b7194..51e31ee 100644 --- a/www/functions_SQLite.js +++ b/www/functions_SQLite.js @@ -127,9 +127,10 @@ exports.latestTagValue = function(req, res){ var db = new sqlite3.Database(dbFile); db.serialize(function(){ - var query = "SELECT * FROM vals WHERE id = (SELECT MAX(id) FROM vals WHERE tagID = (SELECT id FROM tags WHERE tagName = ?))"; + var query = "SELECT * FROM vals WHERE id = (SELECT MAX(id) FROM vals WHERE tagID = ?)"; var prepQuery = db.prepare(query); - prepQuery.run(req.params.tag, function(err) { + prepQuery.all(req.params.tag, function(err, rows) { + console.log(rows); prepQuery.finalize(); db.close(); if (err) { @@ -189,7 +190,7 @@ exports.allValues = function(req, res){ var db = new sqlite3.Database(dbFile); db.serialize(function(){ - var query = "SELECT t.tagName as tagName, t.units as units, t.id as t_id, t.minExpected as min, t.maxExpected as max, MAX(v.id) as v_id, v.val as val, v.dateAdded as dtime FROM vals v JOIN tags t ON v.tagID = t.id WHERE t.deleted = 0 GROUP BY v.tagID"; + var query = "SELECT t.tagName as tagName, t.vanityName as vanityName, t.description as description, t.units as units, t.id as t_id, t.minExpected as min, t.maxExpected as max, MAX(v.id) as v_id, v.val as val, v.dateAdded as dtime FROM vals v JOIN tags t ON v.tagID = t.id WHERE t.deleted = 0 GROUP BY v.tagID"; var prepQuery = db.prepare(query); prepQuery.all(req.params.id, function(err, rows) { prepQuery.finalize(); diff --git a/www/public/img/loading.gif b/www/public/img/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..780423b4e83a4c18e8c90b17c0045a59d640962a GIT binary patch literal 39507 zcmeF)*RG_Q^MaM1INe5larIWy1K>1#s2>Ol$4Y( z7!3IP`uha_HJ$(!3|&Q9Q$tc*O`4mX6CU*ET~pm5prQfD0P8=Vz(2kw016DiLU|=u zmDL@9g2!w)Se4xqf=#24CRd%)7f$rrc4@FWcOaTlGVGOnP2Nx(qehA0P)+_w66c5E zH2Ku4P^_p)>9=$BMs$?jZT}x>57dN%dMUl`^zJZm8+d#cyw~5rmFQG2p)^^XjAp( z0F*{CL#erDdnEbw$Cc6M+MS6k$#8Pzmb$&EB8^hxv6lLSxvCE%8Op517AJDwhHFAi2GIy?XTWFRrFfe~I>ulb?S7p?hY@a?VzV9PMB2jXj6 zuLluX7Oe-ra^GDKp$ul+fY2sdZ-g=y6m5jDHtcSMbM`ZCM)1s9Z$|R}F4~L|zTVx8 z7DHm%f=a!#*@}^)FW!n(;@jJbQoot?H^ZT21 zLGkYtyN12rsZRY&J87=7HaqE_zl(P=K40(cWP*{Hce4Ut+U{mU=u38UBKY=qbD=WK zdwKENwtM->mL+=yY3}=bg;~MO`$c()w)@3J1tt3>WexlLrB(ef=7X}jS=)p1rr#w8 z6>Zo12bEn&EQeLSFFzhu57L(&){OEU9M=AjVL7Us*8X@@KW|xj)Uf1!aMZXK%yQhc zmH6?vdAFeSxaF|n;JEd)pXH?Oa`xj%`|a=2la9yhgOg4Gnf0^_fza->8-=0l^gG6z z!_yvYS=O^&d>y;9J|e5Kv;J2ehi3znA*|UH&3#^Ua&4Kzy4*{cyzt!G{APVK z*V=CYQ>^%FD#X*LJA^=vFREJsuFnskvWYJjR3NdZhm{HTPL(5{K-k^=B8AHR0M5hH zgwGkCsR~LH&QCHZFsHEw?Cs#dzvBFgYeB(GsT1Dc$-nzS4jGH6 z8*f%Bknj^Fx)IU$7q-3rgi4ezY~Xsr2C=_lFRi1)>>)VtiV%yj59%%ovAWFK`g+?56Kq;7=-<$4tVJ-PqTB<`KI-)T9O*{Xuy+ zUq7bNDDD?Q*>X+-D7nP`{JzM(#SM%`>0ZZad;{LYF$F-3c~m%F*rH!5!>QKEL3~3) z!Yb-vxJ>IX?mambhe_{XRunpPI50X|R~+%rJSJsSMZStyN~g9wT3Ov;J%^e^i?)2R zW0cP5!O5g_rEMzlJ8Z=-j!7Dablg+OV;pF4Vc~~^T!jmLw)%13UVJJ5CJU8qs9(np z8(N~VYz)tSGEbe7vJymgRy51NO2+lu5oou8DLS$DCWyCX3-~fkcuh`aqE_$RoTB3R z;>KsX7+SDrGO0PZ*07jnX!#!lADjbowc0^Uy{-v1PJAaHasx zi|JYT9=pVSszm3>hTn*3#?LdM$W)6?TcBkoEUmt5*fp1*OCYAPz>UfwuteyBNh4#0 zpps8|pXK0}PQf#88J+K@e7@~m5B0VCi}wc)O(oENK|ZSYfy@f6`t!0mS2eZ-AE~7U z79eW%FG>aR31VATVDH>1z3r{-M1>ZA^pB^;wli31-OsH2`srS} zxnsy>tGB8B5{Ue4FVXvL3ZCZ;T_6HLwNpHEA%59H&jkQ*l9lf%l#`-#o0TRgc^u(ppG=i$prrkqLN+RO@SI+IBsA(uvIOC|H=fhQe^ussD&(nb^R_pwL%5_cZ z)1kF$>#w=->xR~+Bd6%rr5%-g{*Z=TnGk+Xmsp zZNJd-88o_Wi$V2n*x>m*d7|z2n~A$|pXZA#toB`5)%(fR=gT71_I;g+``Om#tE%Ys zLo3yXUyIMzO%v_M9up5M56?GUSRJP!s*f9_u-ie^j`O66$KOJ*yC2aVmxZcN`v$Q4 z`H7C}#)+q6AK1ehR_E;Pfa>#kD(rDrwex;%;`zE2_H-KE`M9GByI+Jo-%fNs-%P-s zAO1Li2n@#$M$iQ#d4f@r!DvljjCnBTEf|}~50~E$U)S%Yryo(WA4!wnt9d{2TR%!7 ze`iwnOU+Vqy)FXwb?AraqHDK2hQA}{JAlEnvR9Z<6 zK5#827uu_v;O#ofgNOPUwqCGtvaDu zl^zp|6fhJ{AwXRUfPa;BcNx+NGol61AC2M3h;RTYC-8c1)+wts4kEi#z$;gjOA$tK zcqdNvt3;fwf&ZIUEd2?9V(A5D?5xPLwbTN`u5n1@k284Zr|H5WCi8 z>_UX2ZqomX zw?d^3w@9KPF+`T9(HX)3p*uouGTH3z}x_S5I(&HEJLldb9O67PCv|CFA-q)Nan7KhX78G#GC-KWS z6m1Z)1+g0?Xp2`3_IbANb^5f&D{wB0*e}qOn=AuURJIF0urbVoeoa?lQL)Hyrs{PG zVi!`x+i3+gs`vUk*1}O8>Am-9u7piSS0I4wC5`txm@Kc0>)O*aX$Ezq)cL3aAF1rQ zTC~3VN2N6pKpbdn7j(G!awo$#oy7U|W+kNR*xQdKYFh`Q+>}H5tLh@^?+AZ--$)H_njWc>HjK1UQCF zU>@A|sHRuL8whv$9rL?iX`X~xw9b&iqB`NHKZRmd&4o_iwyUtO_~)Xmi_#1)ztbhRaTqFPFLk@tWohRItNge;|PG7}osO<5Pg$pHMC^@g&3 zv6nZgqJfMqu(17bvC8!62P+ap)^-FA+NtA*&4f(-gAcKlbCV;yzh53&u4wC1_RLl) zIFP+HziIj5wR!RQxG00ijQ7J+`%22e{>Yi-!8yI}8Z??>;fo+*G4owgJf|XLq=LZXNdfET#W4X zHF(^39_LqAG}sOMy7Y?(MtH|!^?(zX`S^=Ur#T^Y2?xIEar-H-X>?)qQ~1*3_V!fs z=m(a$jM~O^{*dNB>VsaZL^p_vdJTW;z)PihyC+^KsG9S9R{ZfTgJEOST&DwGsgBUz zP=mli_pxi8sL|2KPWgEiz@H6A8YcB6dcdm8)1Gi@u z%a^myj&1Ynm|o3_2*TxHboudN_{deg}SeH-&Mv~ShJcA6{``-OxnO(B$OMl%~+M`Ou8p&@7^`9R9F8-LL}Bu%hI!lBTe-`LK%HuqvYP8vgJ) z-S7s_@TTPOmZtEw`S6b0@Ghc=@B9(Hx)J@J5rfGQ!%Y#R^AY2>5kH6`C;20%bt7jz zBj=MNe>Fue%}1`>My?S>ZSY5J=|=tbjM`0(+HZ+#k};2A^H|W(j4jYX< z=(MBdq_efY^%Ma??a`o{Mpal2kb*s)lM!Q!0aG5XZKuNl=Dq+thSSsDw4-?!0GG$n z1uT4=ZaY1)GWWf`3|IYfN0?su{Cwk5_x5D@05~a%zE0wAqqMo)REx4lfF? zbp!*(o<4GSb89v>1+E0ncy%OV_Xg_8AT|%RY8W0dB}NZq zASk}tjd-1U?{*N%QBw`Y{rLe2l^93VdE#?aFF7rpL_fUOs{0x>&yUVyUB-K!#vK63vh`Hp{vCB$*rK2rZ>D0i1e18c(4mDOe+YUjs^{ zS(KC~_YO6AG}?8hG^H+8cVJip1SZI>mSQ;>4ihu=<|uYT>@pge)oht{tHNZm#;f!O zm*9dcLzN3xarFmv(-@hLH$yGy)JF{+Q6~H_t`h2=+qo_TXx&p zSZ4b9FnxE&AT4{)p{=%Ri?(N#)}Zy?oHlHRFAs0qz(H%oAX-ifs6jZA(jS@+1TLF! z(~lMsQx|?`oRwk6IM7*@ylt}7aa!Un^LR74aOHempLSV#2WPvog~ZBrugzs@Op$pE&QL6U=I~Wr;@bU$#KYH0>9QAGUXY(q_qE?*4j5nW<$XWad z(0kWN%!6tU4oK=r*N0VZ?SY~ydDO?Hn4Bh8>`zti-U&fpE?)OJg;z)DjjjeS-BvSS z?+Cn&cuP5;JV-F>;6vtZj)x~TV7FWn?PzN<@ZNoJ*w6+k$oefTOuNIt+&PH?*aTw<>64Z*@pDo9ThhF7$Zv(ui`CN z5YxFW`vuV_y8J7GixQ}2BXp)ONUk#G;h-1MpfSS86-ha5U(;EadgSHaVMh7R`!_MM z{lA&LVr+LLTB8;rnC3B9>J|#rq>D=Z#ahMOP3_^FbY3Q^S_a2{F@g%UKNNsyhwDM{59A>;R zfSj@ENcywrnf<1RjX{tYPq>?*O+d!Y)5=-!RW!?qgC_xGFq)QNZT3lk{G;)DD!l6) zueiWuACc?Tcp9C0r{GR;&W>2=O z;eJdF`Tj=+oB!(CUHWgS-Gvx()bPLH{R`f|;Qe=kw`&(3_%q1|A&A`rpyV&!MnF-A z;6X(vd(vTTlEZ^x(*$|fapR82{Yh8P%)@>!;mPb-9)oBs2#N74{zXrmIRMPVW4B+D z_GNejivdz^4`|-Qd=KhfOkzMKcxxgc_`s8&x8pvpPj|&1D?6k?Z6`2*3Mc(^e-7tl zz0&QWbP#yF5n1*P{6f{d3c^Vi3x!1ay+8;BTx7eBl{wwcOS2>~z}-j%jx^ZQYslRU z-yuY%esr{A$XqqztA0}c$kQh1RFj=V{f=M0e=tIU@tSqLI_9J%j9ABDQ}0=LfkVUJ zn~52Hi%pb>4JIRA#X@iTj;9_&CEuF`-%8la=9~w)_1PdKFq7u8td6iXq#)^h)5pt~ z7ou}XjkiA7hqeDR3f1d9hQ%NbTmL8OcfuxQpdoDGursLKRxa|>pd2I+CqS_-i#D`L zLQcDe(%zi%w{}J9&m32&7Byqm?GvKr~FCHF{rC^>6RjTtNc?-z1 zZ74dxO#m!M)^EiX9hc4Xr`NWviKBMqUq_@;xEHH{e=tH=5O6TI#;AUYFzO z(s#-%60>Nc!5uTfA0jOg54>j+TM`PR$Ohl*ywPeFrvIZ-meyWV*$4Z3126P5L7`bE%3{LsHQ;4o zYQA7)VA0|p*+R$ch`wQ98daH>gJ}LqfZ1R=I6h=-rhtOup=I~CYYTk#5t-jT^ibQm zRj}Stiz7v1{R_0M_4SFuPaoileXFJDGds&Vsap3vU-lIujf39ZM1Qawlufxxf;C)a zYd@(Kzxdc2*?JX~ys?as-Bz8d^*yHhE%+9(+Nf}UwL943J6wfRfMTgUg{8)KluwR9 zh}@f93VD4<-PPiZZJYj=x-B>Yr(aFdHW3NG4N$RHam$x!)2F&NsnQR|Ob97ckaN2$ z2Az4Z{LpIPAuz#$`o*A)iPN&Bmr7`%cEp zhp4?okGh5Sxis&Gm9Ckarjt>e*PTM&zP;qHx$~V-h5uZMCd=2JeLE6z!Lf?d$=k!W zvgc&AG>f;xuY~*j$?&W0))I_eU=TIxJm!PpHmRdmvsQ;~;z%1EZOGk_ql!j!sqmJt zs{FX3@mLnt;ts`5{ZA8a*C3eBo-D^*cQ*Uaq!FJTO~=Nr>I}CC9?u19jYaT9A9rKB0HZSgh7;@k2gN%qEELYsQ1@9 zZvp46ub@#1(2Pz8d_$!lYMuP!OMU>}7Qqi^dI&%z0-&ax0sP-6k=Y$&I`HhB5E2rZ^B4o=McpG)>6MmURebxk(X{h!=7|grb zD2fq3j*dm+qCLmf!&b69kB46~msuGU)#B@o)#d2|ROA)m^0;X;q^N1K<%Z#bti<{@ zI=OpBDG0>bEsHe=1SVF}(WXt>Q!EfSC6?CXj!P7LMB!p{R$3Ln3kNmP^W}e&WqVnh zhIP0tCdHtl8Cy%Wm^TRPARcjQm`Xe;%cI`2fk7SoRnrmQ@4T&T#<6~>mch1V_4Xum zg7#P=>PU!z3GtVTe??97$tA-9{=IZq*i;TtsFhSvvuw06US4ZFl>l#w5SY9-Z&b=g zTIwGAK^{Mg6R|*P`&R7({d7L+kPRn|JWjy+H`K`6?^H8aJr=cNoUN+mJ&JA&iuOEJVhD$Ss&N{dL9I7J??rVbBg z#PSw#AsRPAXVn%$ty=9o>Fa`N4u^@@hUaH zvYiPvzp9KOJ2FQW1c$zIFD}+9m$)3zXMv^EhK)!TSgtE97C2wlBNCg4z%MSkdnZzs znXgGWx4v7bh^b+)KQiKIU7GfDY>Z`d(9cZmuP0e+wf6!laM*7r*8lMl$wyvZ{ zNwsn%I2Z|_`~X$mbR2A#z_Ntq9KF>$6?uR!$gJQ7E6ny!K@x8mIP|xEDrw*sYS!5%o!RFnBZUj}Vi7>!RhehoU+e4sDzckyO+en7pzeJHQ&j zt~ix;dNoS*;pM2BagCTc>N0z(7mRXQ@&as-1_e#tjVUL-{<0ilWhkgO0eAZ;Q!B?< zY=>|tpnpGedvr%?l?Vv7tV|>``aw3P2V6T;cI0f^sw+F zs-dn>`N-Db;n%!_-eJkWk%P}-3!;)crHk?=T=K)xuF5~wNsrw#4LU-}!L2*WC*F%f z%cuP=!136hb<$Q~qdy0DRzCHw^qEuOZTe1-;rN>4aUCYZuSTVE=7TI8hEUYhzqsu5 z8r^e~@~cOIh2AI0ud2R`uz|YV)^z)L!?u6Tb<&f6r5%G@9PGb)rhoTL|L&Ro^LwUQ zA?)|Nc}WiUdqss6-d~V_H9-KW>{$B%G))d1)>KNj9@YPuyz3*Kjtc>uxrpG?h6iI| zy#bolhjR)a^|wORdb9Bp_}kg^e^QH#Qe(nn$KIu% zgp*Wf6Pd{MGr4~XwvK?1%;^e24>_Y$FuF(J2HHNkLOEx+thNF8o!nh;T(=4QIQTq(O#DCVZ#0pxv_`4NS)QGL(nHx^&n>53U!acHpy~h+AirGvrL^}EX?Y%QKciN8Bz5r~{ z(On}mS(t&>NL~dj6PTHTzr*EXqXwbmNvmVEmZUiUVa&`!b&ysh!5r2bt{)dh^0r8< zo^jx7uxkR1Z?^1=URT2Y1W@jS%FKp_^NiyR7NPAzpOPQ)XwjrxZ`}is#lSfvGeM$S zIjZnB6502v(K3-GDuhE*5(Ta?^7Yj3@`ttZ3d{@S((;%Zhi59(uV_sXb96c6XKNA( zO6lbfh^&Z##$WYCLUf^qTE7%%R<0_Y2!@O&Y!y16uPVHb*0pLCdS;tXs~|yE&s=@KH)S|G_pM#OR0mYF! zcFM#ui@`4&7|P}BeZ!}hk>*lDFOS}xco`agnXT`$KbG+>HB9H$Y0>sQw#f}&R;6!j z2|RM3qCj6c2%AgWqO15c!2)Gq_5cnew>`v#z*qeql*fqn7AK|9H1dXy$0NCquSrdX zBtp?uWhrQ%&|_d!jv>f^RE-<6xSN^QzqnI(=87Hotxsy1y$X-9DJ zZrpRcHsi*Q3Xd=gfMayYsDIp%fDw0m=0gDxOm}4n#eRIktj#53154p~PbRO_W^#?~ z32S_t%BpZp=XhbOCaK?@%FbJa)Bc|Awf;=DN|k3M%7Hq(XHRj(Ro7 z51SNfr%9r>sHY4Fd6^)r`MA9&EHK6^%6l}TE|lY5KbS!lqD!pUjg3>ln{G-(@xeIE zf|CH+yik}-^7phdH9rEoY%~TGN#c#IAo7Up7a#n4@b;ZTIQG_WVQMJ7iOEh7QC4cj zKwVL~b7-)DYmQR#Z3nFa4xQ{JIZ*NEFvNazxOiX=R`)J0hXgc&b;yk1XOk2cbxb4+ z7Y)uj5w3uKOccetO~NxzTp_raU=P|*3JcwK5Ro(B6Ol`riXw4Lb{+%45Tg|5mcvE@ zVw083DF~bUU#MfnWkqb!3gisDmN|{fM#!g^RhLIH=1wRsTVdpyQ~(L$CRX!UkO?r2 zyja0aYEGk*zHE|Kea1~LnYNHEVjSJQ!}?ZjPEJR-pmfTMm6B~}A>++Ippk=>S`tq$ z+Yr*RBY&2%7MRD!#@+5PeEMzgbKXVR&_G1i>x^JStG9G;FxAi0>S+&cgKy7@C%__k znFxqQ;vrt6o-bXqF~o}`mr;KPLMAfrG%cmcsDHZJy5_!UW|W~roeXuwPq}1e5=Gzp zXK@pF_#KeY_=xls?1usRDW)Gwhjq^AQ6cf>?q8&%+5fC z>Pn4ohIQaD){00EOHGkWwY&78RS@Yz1B>MO{`0F!zWCA)3_^3DL(Yo$F*|)Kp9MwR zm(>etd-n~pzuXhA>k5?FY;{V1&Bo4_R7IBCSt&05g8jN~XiCStLg+F``<+M$5CMNG zBnChE+!}k8D*$Iw!)?L2DoyDMm-N>8c|MPp`EmzWc<}P^`O6li_6m!u>*qJ;To}z6d=}64aGtMlabE%Qr&%uMF(%fqv>?NDmaU` zPqWxIA{wTNIgjst)ya}MH%KFXp6J*nPCb$Y8>Pi@i7ov+e4Nuv>x{&m?ozfyo=ZEy zN8T0g7cwlADK+-7fZGAIY$m>%_|xu-YmV%p^&++Rm`?^<9@E?1SO@)|jW4?LikEDl zVcwJ95|4`UjP`z2kW4lCUL_hR8|bMD%v9H3r75<*Gdd8M%`LdH^Ch!3%}tpjpmWcS z9J4i-)Le*ZV5<`#+c$kpIyG**TPMzTWNNjTwb;*V*Vt~Z6Ri)dVlp;QwNJRM&H_7@ z9<2n|c{k&cgmQ zfruuW`rrJ;+W)`*t@|H=H3-??>FD3-=-=t+zh^r7Z^V}4UG2o0G?jAF>=z zm(x0qPgnC+l~3189w$#XYatxZw_8b$&v(0pmCyHwjVFI*q9j1qBN7qtRpA0T>nm`% zY7+bSfKK^zxFZS$D8VSxCNQ)?9DttF58m8V=GAQ%GP{~Tfq*IE8zONGg+PDOvNgoF zyU@oV&OkC+D#@+FZaimf06}wIV&6)FP#-&(EB?KNu5}OLZ?zEQ?Dyy}A4aiPaPO#u zX{iz3d-jsizYBd(e2<5IF2%?U4b|X*V3!j~(^$rY@osIPbb5l>oMXNk)8>kmSod*# z`59rRluNRJBK^h^C(6Dzhh&bZUl7hT8ljs8=L$tm1hYEiD=RI*-eC8iMK&lO?G_FE z&>*9VbFi;w77a(SG`2!)oJzdOlbHOlddhixky1Xh&d{)Si!=LGcs{Gu(D%EA^Tfuj zOm?h6MWc+~QYr zvy4&5pZ{&}F!Ca8m)26CI%(Y9ed^nqYoTy)=-82sOUA8m!E=Tltlj6gONxRprD&yW zw=s!pQj<~^Ka z*HHj_$ExSE8tnmM@{^$6%YxVGc{29iQyIVOpqxiGO7B_*QqZ)D;GOme1H3g8OEe;t z?Dyq6?__{@%@P&m63Mqs8fBOXMT#!~)pkLx81kP*=IQ&|Ttr%xB`)Q-b}ZU)w%Vb< z>(AE8EOPbUIPe%^02qnh=7+r<3qQ=TnDFMz z4|C5}PX*X%tPbN0dnZ;y@-wtvr)m$m=hYS(+1X}?EPRI@6J(Hc+1nAK&u!|>G*Hf% z$eFepfF3pfvzM+aNJ5nB3<^s=S@!_m7;1>EJAH_dZX>?*|yxEseW z+c4k1cWqQ$KGvUWa=Q8m`fUK-gga?8L1dE~B{jILfOY^4CnqQd+6kUFZS7TDi^mOMPt;!W3ZyWO;Vf8c~F@ zeB20TyzP6zw;HAxvCQ4k1P}+WMi3ip27_<=xMWtNSV-3)kNktjq%|(DZ$E$v9fykQ z2P5AteK?jEsCvoO6}KE>v0%LQ=QGHC*I{QE5%CS?Yp3mkS~}X%`G>c-3(k<%h<1L} z)3Lo;7nd_u9G(!}@zsl38NgRW{Hq5I`xmb4?n3Lx-MjCf3o>#i7R6*L@7f_zkZg`u zwnz3Yo#bbAx6ChW1d~pt@t+6t3DazLtG#K=zHr6&As^tL+zjLg(-%qR*q&5-O%jH> zrVPy;u!fk=0u^o-7*19(TO@shn3gU9$}#UaZY!32F6F)$0v9pKi}>%Zv&VLHOyUrJ zT_*6^hq9am8w&Lxa?TOw+UW1*0fs!4VF2Aqk%PVMk;kFvxYI>7V)& zjb8h&~%EvzjQzKp+yh_EaiGgew_yMk7i?fcuE35 zmJjV#Ha09oPKx>->Laa`gPTMI#&L)aRf{mgd~qtpr2p>E`Ii-f#wIBSTh(Agj9l!m zP3=TsXAyjt?_W0W^zgNuMLI>~zS@zK6%0EG^2{?M$Eq3-**}Z^jF3kG-`p)hat?(k za$kfIcAYf`NMg8ip2^* z0=ZO~Io$ye7BU-z##5y9RATVL4BT|@R@sh6ucJdfoetMP37md3#8SkDKfTCUy z9>(tLcdJ0(`v~G|)yubyDUYC&Gh-9d?zc^>O}je0q78pH%xwtR{pux+g0At?7~_E}!DtZe(4k7D6?vE8L6hDpR>QXLn$Tom3b^R+ReJl+fkKRRx6{D-Ik zEZ$=&@-kh9H_d~7Gi);4b_=0A&0cEkMez*WzqY)IyFC)B6JN&~vnn>hdPdM|mEt}_ zxefS+bW~PixD<8*9_MQuzFdUHF(lnc~2&jV49rx%0$m@Plr@{2Ia2SOzX&Ioq%tO zLjKZ%%WQw07?^?1pd>sg*j%=xcy_8VRp5QM4aZ-cXXx9ilYVQ$Cz@KpW7i-Vbz~H2 z=;K>sT-My#-eED|wz#8`S+(5~V_Z71Y);T9z7g4BiG5k(T;+Bg=h8C5WZtYM=8VEiMRRs_C=!%67cDQ*APH}$)bP&5fSH$xjZ_C z)gg_HkBQ-xo206P!z@u9UP-*Pw3W9F-NJiG{f3_z2<3n9in#vfP5Uh<+H#96G8@C{ z`(4>XFi=+AAYTu7 zcXay1+I`=={|W~Ym;Ngm?SIcf#Q*n7jy3z=Q0{Li_cxULuO7<%)8KnE*!};246>$EW?Vd#*O`c^PJ$~NG<+s-S&9r+5B&nQU@KC=HuK4$@FGbyMWPsE2rSjcM1 zkEp4{c!vq+GrayjVjNZD9oLpi5h6dTN1_qm8NWi4P^<{WpGpRq(x0%gg6498hM_o}}GX+F|@NkHdD3{X`4G28$UqH+y5M zr_Q=s3hop`^>D#J2Sz9eK@A+HtqGvGEs-ZJ#C~Rv&qPJtmXs9)ptgZIFYAi{eumz} z4%&=Tb888J6CCp^KC4x!SVh@eI9f!b02LLiY%d57W{fFBLIta6=xhG&)=bCZ5>pPj z{}Fqwk)_+mD2Y@x9dB9ZA<{>$jzz6mqU}bdH^%%r;nz&q#-*pK#kMXMw^nTjZ?R3^ zz7f`>b~rsp*?L;3xV5b|B+3o&>f6_T&M=GR44l|Er*2JdtthPt8BqEKF&BVU8ps@dB{N)gqHb;vdT}nfk~Yi#TB^hM z$|d%IbwX&{rthB z_xebhHe<$_f^wbEex=7=@C4q;u#yC3)2Q;6T}UPAaVs0amnLNRr zt;n=dQrNYO_wr2mLB((U>uMWE2W(lIlO1d}ukNJrn6Se5?ec?$Nsf`rg4_j9xi@7p z&MQ~`MZ)oc5NJbL%K zr-W8a>^-B>mUC=mm&=7FZ+c}iDDN9QPro~84ZO*qap&mTP%iX{))c8sVnA7AvAvwL zFzk$n3Ga5Uw7TqgT}Ipp$!T*q>_zFF!U_25Yeakfx^eiP9{ptT$q_ox`0U1u{N=Qk zC3cihthCF;TC3LP<394|rg^LkSf`P}?MO;s!4^g4X{~Md)rUm?(*)B;n%(C=*Px~* z2UhPhzlvq&JO=$=a{>F`bQUq`KeIRczjzkWlj*Nj|6290RsR*OdK$zTk&R*X{|P4t zd&uukLo~R>02mS{00?6Na#A_(;MBnMeD44xQ*vB*F=<+vcZ`5j&dcngHYWA6a2fnC zpsuNp<>drm{Aflnc8kXy8x!6K4<^|m0{HHGBOU8H==UIv_HJ*W@vT6XWKh3FF#7}BKyJ!d6*>o=t4LXV&6 z_IU>&S5Sv5x}e0nvXaHaRg1ukcWJQOV$(^1M*HJu)6v=B5J>zq#}uO`?E4@iPB^7H zSyzBoPp|xfb23fjG7kw=Of}|C-M>z=NUPrPUA_R006lNcZ&zmdY<+BaB`v_*UQTV9 ze|pTmJ_E1XMlgtJwvgQ%u*x6QN0y)V_dhE_J+oDpB$;g~NT5_tXEw#DnUi-vE`L=H zF)a7e^#`&l=q(2IzAD6&*6@}_2CxbchC^Mx5L6*`*;ydR&S&_)tj?v~{)oq*9{`I@ ztTsVpF?ulqjjwU3$9TWNxQN#L>-ma0&Zvl0i&Ujeg`l~b&cXU=e=r~f{HriI+M zKzV*q;K%*7K50)He33H{yPX*%O{*Wpo@>LnEau+-vXyR}={;$z*;f(%5<&J?(Y($R zl^ejZPr}6f9DX1`4oq+6$vwVsUV=!_Z!x5$p_Z2yI;0 zuPnQIWwwdYuEylhXk6x_e-?RL<24>2=ow1UwuF~Hw^sa|QdB7rOk?7+ww#enN!2bZ z7_GEEB-~oIT>^O>0g%qW$C{lOJ^qlV2u*^ z69$r?{b>nch`sQz?g0^j{gKxG%*;RXgNbCY0ZAn@5ZtLGGNxFl!k^n3nJCh9D^7lL zWo87v$zmk7)nRa)W}p{qeH_{`p-32F#A8I#T$tE^rg<*GC;om=*}Eu-Ei=#zM3$xH z9T*Anfg%Xf|90gQ6cK6$T8GF{8Gnipw4=qklItQ?a1M#aH=#tN1cUIoY5dyEv3d$+ zR8nGNN!D{237X$>r<^AgO{36Af0I8PJ58vvGn2)@9@5&c@pkXEV3#zL*Po2>&d2}F zB#Wz{|BUNBq)E?!tcI(_rw+`-(X&Sj#OhJUrv7lr=PUHWHKvYDnF?6vS4LHd#gB`e zFEi$;4^@e`n~b;`O#@xu!Ib{2ivqwCjf6wHJ98ow$$^~SMaaeHFw%j=Ur7A;R54BaJ8kQ?1haZf;xx6N5|hG* zP-q0RMmE!XMtOm`A1O%FRGjA4l5km5@f~%PY%}y~(K?#Bm~K=68JIy-;77snC6x%l zZ;cRHjaM9{Zj4DYDRX5zh3?X6V7F>2wpyL0!lQjEnq^##hB7R7 zE*rHVmn2>=U9nnrx!ZX4^R8ULn&o@7;}*xOJ1%qGvm^tD4RdAAK`=*)^;@QhOPUd3 zGLg@QO+GgK7L$BjJ!Ksd;hD+%ChM)DXgqn+F&~NM-UAoN0Q>u$jtlU%3!mEDigj3* zXl!}J06QZODJwsRrFa~L!k9k$gy)%wqegP(QfQ`JL2n9Wa!T6j2-cU#5AAnLc}deP zj+XaQ%%)7kbJ!vrZTqa>aV0K@F3A&)dX8(WV-Za&A=^**pSU>Yn{b-EEd1$t*~L8* zCYm4}gdi znJM+LaF9Vm3%IFdEG!_7Q9l&;o5Cu!f@yVGq)+$8udXNkb6rK`_tc%9y?XS7PR0iv{MZ zMh^>G-yM6rx~sh2BzCeXrR=;FIn!xEKRbddpR7K{tYEhq#@x4#Naq_z9npSTUFJT$ zF3LNPBot?QxV`5$UhmoywI_8s?5%~~OqMDCDta*Oqjm2MtS0qr93iq7V%aR9r++ir z=Y^T%DS^PbL1_0h9UY=>#`96ZpNfH@tUJS5MaPs97I7-grh~`KmwsP*Cvo4h#QTso zx_Xt=;S9?^7t)8oQvbse!T)shpwa)eEDdM+lg<2-&HR(i{CCY}YFS^%w)s%r{~gr} z2(|)E6aaI>-|SBS^8z6G^55Cah0C=p!JHTNr|@((1u;bX3;#9M2u(;HJ$g*-y29Q& z>FHS}gl`*{;{agXYfV{(!O}UpN0I=o+5l)g z0dQ-Y2e!MkROnP=nP^mbC~g8*BPHJk2nzD;QFf6F874&0wDPh1)YcaF06>p!?B4o9JyT{D{AmG;ponNW~7(-a7@pJBr zn5F?qb$q+vGtL;QUK94plp>{7F6rWCxn@){Q%K>R)1Nwog+ySU6Oa(w?{hvuB`o9% z$c@PZufQ6y&3A6_PyxVS-V(r8FG5-}kErJtK9oJFSfDboq3d0uQ-eOU@w|hTp4~Q+DwFp3nEWK*^+NXf5pkPFpLi(x=n6 ziLJ@UFVBm{=>Xd^$$9eEb>zKXHu+mt7m)qhPzwboMm1Lu4wbF8^p$aLbU&!{oKph~O;~LFErD!Ij~M`Q$f6)EC&VD9|OA7f0D}a(#oMBS-i= z%GSXrW+P(>`{}+{wXM$mN8eqN&9v#resqzW_#!@9A>-U-!vt2hGCV4xPPk=5z4bVD z@eXR3u>GO}7vIw$Q=Kj&X7GqZd4yOli^AB73JGwSPKc+VYc0g=n5dzh-%^py9h>eM zFQx|aWYPJ3_8xr=(<`-xn8fHZm+I60jee7Wz5-D<`uWMqwj;ibK1WAXk3~51aqS3~ zw*S&o<0Ec#lM&bM{nY^MkE&6=!4Bk`fB?f}=s*kJOX5w3j@2)JSQe@Htk=87X)>+v z{AU&t-V|q+1kI1FWG+YogSiED4!V(SRn!yDkpQ$DweyDGjDD@P81dK7GB(_gh$qw5 zGdr?h>1EvrLV;5|Cz#KYD)`vDN{mddf>`N(i4a#E=rS1^gcb>`|#`K>Jq4- zgIY)|UL9gT&ZWR_p87Hb|7!e>kw(9f$w8i?g2Nov55rQwXM8g&zN+Nd$G4NRu()Jo z**m>6wJ@ScQfJc;osyNV>4m&`Wg|KAu3ubgSIJARmSLb+GGZklbv%wil@5~H*^F!j;r)%XhhlYI#&)3oeXJ_kNM#QkbK^#-t z)ps@Qv*sP6PWiE#IrXPyj6Pb57)a_y!WhC;GVW-8z)4QyVB6GRBCU;22Yn)_vZrB6 zm}7fV**s%iCZ(X@1|F~`bC2Ho9w$Q zQTqb#K_q_+fX?Uz$%A=7EbA>7yyqb5=x0REkBnsC+iz*%xf(wZE!5Tqp;xGKDU{^^ z1{J{&0nSeV%`>%d)!Xm5X;i6MtnagG#=ntHL=)bntM)Oe^v-OuJ}zTqH%&BGbF|j&rJKQ4fu+*{BCKM6L)% z+TF?HpnBUO`r9qm;((uZ%{N#9ycZqF903Y<(2`tqkMW=pk?_)Qj)U?_=dN@?`Pa|NuhFP59X z;`DY*k}}sJ2)VeA%0G(l2B&Pi=?pQ2(l@$$ZTyp9tbIkAce{sT{gYwzS@z;lIq}}Et4tbp- zaBZ=Ehpk%01XddmLipf zm&W+bI#H+PftHbBY_;2jDyjNF`ROwOVbm*KU(czMtcH%?bI;dWg1v2lFA}f%t0`u%}>`fBBt*D|kk`%_l>Y z*y^GU%5|~)ih)x5!{Pz0&A5+~gD7Y=!g$j9Ri>^5qE5I&UEKa4K)UC8QvHdWkW6xh0wfp>1-px!rFqJmvI`V$EbI=^y!YX+& zY+*)C?ndh1X}HkW1c%^+po3*$;=}rG)BgR`#uT=gCV)tF6_&Y^W2=qRU-y+R6{RwfoqC}^0kw0S#;MF4HaC8U&h<(-^c8 z$Y^rU_6^v0frCNn+scQKCvnP0q;7NPhjT9^nhj@Rapy;COexJLgJ{?9r$1PDgU@!7 ztcV0)UV{1jkUE(7mPGX@m0R$=o4F@+5v|$6m4P&`=JmOMVkyD5Z!mw!CX?_@qET}V z;SK8UW83di$?@lB{$evs;wlnAZFb@w>3+@-%2LbFN%Hm~M+j)k?8fZX`- zXgC0W(e^+{GC@!h;Jc2)>4Xm^o;ulb^G)FNJs6S?+u>ZK2HjA0!<5A&fg_EL1g(bs z)Jph8yGfdEjKh9vD^1R&ESQFwqis^_r9$csEeKCTLNcytMXCd>`y%{O+E8+#WXFqO zN>@_m5?W`qc7Wgmz{H7I$RQf_3U8w9D2qx^MU`6-U5AE^H&^>iDq ze14NrPfa@a{i_@IDM$>$G(CXJ7VDscjX`aG!#w(z4iESuAHDCb&bx-ct{LZJ5av;n zPxDJy8-Md#Y+-fYxkj&3-;;oDC(S*-f+19vXGJfevO6fD&(u#F zcE@!XJiW+%jOo%V_ki`7cd*XaJ-se2N1IW#MR;Xo%=`$4 zHTJxQ`;CdrHnO9ewq+y~8v}-SJ7L&qtT-4EFn1cua&cEhkdNaiK+W1h><|y4Z?Zhd zKJ>tRhLkTca*4^s+UDae8z9RNJ38! zh95W1^iAKf@}FHSJa~i`TYE;BDnFS$E>1IlrC)kFYDV|dL`>PmN+53jbL<~_-`T1n X@z-En^T9tHy#7-)m(E4Kh2nn!gWw** literal 0 HcmV?d00001 diff --git a/www/public/js/controller.js b/www/public/js/controller.js index 2cb050e..a0d98a5 100644 --- a/www/public/js/controller.js +++ b/www/public/js/controller.js @@ -157,7 +157,7 @@ tsCtrlrs.controller('tagsCtrl', function($scope, $route, $http, $routeParams, Pa tsCtrlrs.controller('tagValsCtrl', function($scope, $route, $http, $routeParams, Page, Alerts, $log, tags) { Page.setTitle('Tag Series'); - Page.setPage('dashboard'); + Page.setPage('tags'); $scope.loading = true; var getTag = tags.getTag($routeParams.tagID); getTag.then(function(tagData){ diff --git a/www/public/js/tooltip.js b/www/public/js/tooltip.js new file mode 100644 index 0000000..250e48e --- /dev/null +++ b/www/public/js/tooltip.js @@ -0,0 +1,514 @@ +/* ======================================================================== + * Bootstrap: tooltip.js v3.3.6 + * http://getbootstrap.com/javascript/#tooltip + * Inspired by the original jQuery.tipsy by Jason Frame + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // TOOLTIP PUBLIC CLASS DEFINITION + // =============================== + + var Tooltip = function (element, options) { + this.type = null + this.options = null + this.enabled = null + this.timeout = null + this.hoverState = null + this.$element = null + this.inState = null + + this.init('tooltip', element, options) + } + + Tooltip.VERSION = '3.3.6' + + Tooltip.TRANSITION_DURATION = 150 + + Tooltip.DEFAULTS = { + animation: true, + placement: 'top', + selector: false, + template: '', + 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/dashboard.html b/www/public/partials/dashboard.html index 23b4773..6798d9a 100644 --- a/www/public/partials/dashboard.html +++ b/www/public/partials/dashboard.html @@ -1,22 +1,29 @@ -
+
-
-

Error Caught!

-
{{message}}
+
+

Loading Dashboard...

+
-
-
-
-
- +
+
+
+
+

Error Caught!

+
{{message}}
-
-
{{vals}}
+
+
+
+
+ +
+
+
\ No newline at end of file diff --git a/www/public/partials/tagVals.html b/www/public/partials/tagVals.html index 39540b4..a7ac036 100644 --- a/www/public/partials/tagVals.html +++ b/www/public/partials/tagVals.html @@ -1,37 +1,48 @@ -
+
-
-

Error Caught!

-
{{message}}
+
+

Loading Tag data...

+
-
-
-
-
- +
+
+
+
+

Error Caught!

+
{{message}}
-
-

{{tag.tagName}}

- - - - - - - - - - - - - - - -
IDValueDate Added
{{val.id}}{{val.val}} {{tag.units}}{{val.dateAdded}}
+
+ +
+
+
+
+ +
+
+
+

{{tag.vanityName}}

+ + + + + + + + + + + + + + + +
IDValueDate Added
{{val.id}}{{val.val}} {{tag.units}}{{val.dateAdded}}
+
-
+
\ No newline at end of file diff --git a/www/public/partials/tags.html b/www/public/partials/tags.html index d1cb55e..50ea561 100644 --- a/www/public/partials/tags.html +++ b/www/public/partials/tags.html @@ -1,39 +1,50 @@ -
+
-
-

Error Caught!

-
{{message}}
+
+

Loading Tags..

+
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - -
IDNameMin Expected ValueMax Expected ValueUnits
{{tag.id}}{{tag.tagName}}{{tag.minExpected}}{{tag.maxExpected}}{{tag.units}}
+
+
+
+
+

Error Caught!

+
{{message}}
+
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
IDNameMin Expected ValueMax Expected ValueUnits
{{tag.id}}{{tag.vanityName}} ({{tag.tagName}}){{tag.minExpected}}{{tag.maxExpected}}{{tag.units}}
+
\ No newline at end of file diff --git a/www/views/angularIndex.html b/www/views/angularIndex.html index 115be5c..b1aae5e 100644 --- a/www/views/angularIndex.html +++ b/www/views/angularIndex.html @@ -38,7 +38,7 @@ --> - + @@ -65,7 +65,6 @@