Compare commits

...

31 Commits

Author SHA1 Message Date
d702653adf Add NMR option to route notes 2020-03-18 15:52:49 -06:00
6eb2e028e0 Add support for addresses with letters in number (close #32) 2020-03-18 15:43:58 -06:00
5f81dd876e Add map scale control 2020-03-18 15:17:41 -06:00
2736bd3c6e Close searchbar on back 2020-01-18 02:12:22 -07:00
5ac43e4aa3 Emulate Android back button with Esc 2020-01-18 02:00:57 -07:00
a7c41a7577 Save note with Ctrl-S 2020-01-18 01:53:52 -07:00
142349de64 Improve edit and delete buttons 2020-01-18 01:48:53 -07:00
3a362168bd Convert package list to use accordions instead of swipes 2020-01-18 00:37:38 -07:00
c0c9aad049 Improve route notes 2020-01-18 00:02:29 -07:00
61e5296fb9 Clear number and unit fields when focus gained 2020-01-17 23:06:35 -07:00
7763d2a81d Reset lastchange when logging in to prevent clobbering server settings 2020-01-13 21:16:46 -07:00
50321d2bdc Add setting to manually clear the tile and geocode caches 2020-01-13 20:37:05 -07:00
410a410d35 s/manage/add 2020-01-13 19:00:48 -07:00
e1330dfa13 Only cache stuff from maps.netsyms.net in serviceworker 2020-01-13 18:56:01 -07:00
e7f1a9caa0 Use GPS to send correct item locations to server, support multiple location types, close #6 2020-01-13 18:54:46 -07:00
f81c43d053 Adjust styles, fix broken link 2020-01-12 20:52:39 -07:00
62513fd443 Rename manage page to add, adjust css 2020-01-12 20:26:47 -07:00
4e7d2af957 Move ZIP to separate box for more reliable/easier address geocoding 2020-01-10 01:41:11 -07:00
b587281315 Fix padding 2020-01-10 01:20:59 -07:00
c5efd7c3e7 Don't cache app assets on Cordova or NW.js platforms 2020-01-08 14:58:34 -07:00
7f215b850c Warn user when adding an address noted as vacant/on hold/etc, close #22 2020-01-08 14:49:29 -07:00
af5edd83ec Add street autofill to notes page (#22) 2020-01-08 14:23:12 -07:00
2c59b7d82d Fix save button overlapping notes box 2020-01-08 14:00:23 -07:00
53aa20ebf5 Add Roboto font for cross-platform consistency 2020-01-08 13:50:00 -07:00
af783bb415 Improve caching of map tiles and everything else 2020-01-08 02:58:24 -07:00
08d7ff224d Disable service worker on Cordova and NW.js platforms 2020-01-08 01:11:51 -07:00
664d54ae97 Add route notes feature (#22) TODO: street autofill and integration 2020-01-08 01:10:09 -07:00
4117adfe3a Add ServiceWorker to cache resources for offline usage 2020-01-07 21:51:03 -07:00
756e1e327a Fix issue where a new client will overwrite the server-stored settings on first sync 2020-01-07 16:47:14 -07:00
a93d33d18d Bump app version to 1.4.0 2020-01-07 16:09:00 -07:00
68117dda32 Add settings sync/backup feature 2020-01-07 16:06:12 -07:00
61 changed files with 2095 additions and 291 deletions

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.netsyms.PackageHelper" version="1.3.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<widget id="com.netsyms.PackageHelper" version="1.4.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>PackageHelper</name>
<description>
Assistant app for door-to-door package delivery.

View File

@ -1,7 +1,7 @@
{
"name": "com.netsyms.packagehelper",
"displayName": "PackageHelper",
"version": "1.3.0",
"version": "1.4.0",
"description": "Assistant app for door-to-door package delivery.",
"main": "www/index.html",
"scripts": {

View File

@ -19,6 +19,10 @@ Framework7 and FontAwesome both have a .fab class
line-height: var(--f7-line-height);
}
.page-content-fab-pad {
padding-bottom: var(--f7-fab-size);
}
/*
* Material icons are too big and disrupt the flow of text
*/

View File

@ -0,0 +1,60 @@
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src:
local('Roboto'),
local('Roboto-Regular'),
url('Roboto_400.woff') format('woff'),
url('Roboto_400.woff2') format('woff2');
}
@font-face {
font-family: 'Roboto';
font-style: italic;
font-weight: 400;
src:
local('Roboto Italic'),
local('Roboto-Italic'),
url('Roboto_400i.woff') format('woff'),
url('Roboto_400i.woff2') format('woff2');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src:
local('Roboto Medium'),
local('Roboto-Medium'),
url('Roboto_500.woff') format('woff'),
url('Roboto_500.woff2') format('woff2');
}
@font-face {
font-family: 'Roboto';
font-style: italic;
font-weight: 500;
src:
local('Roboto Medium Italic'),
local('Roboto-MediumItalic'),
url('Roboto_500i.woff') format('woff'),
url('Roboto_500i.woff2') format('woff2');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src:
local('Roboto Bold'),
local('Roboto-Bold'),
url('Roboto_700.woff') format('woff'),
url('Roboto_700.woff2') format('woff2');
}
@font-face {
font-family: 'Roboto';
font-style: italic;
font-weight: 700;
src:
local('Roboto Bold Italic'),
local('Roboto-BoldItalic'),
url('Roboto_700i.woff') format('woff'),
url('Roboto_700i.woff2') format('woff2');
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1 +1,61 @@
<svg class="svg-inline--fa fa-box-open fa-w-19" aria-hidden="true" data-icon="box-open" data-prefix="fal" focusable="false" role="img" version="1.1" viewBox="0 0 608 512" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><metadata><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><path d="M606.4 143.8L557.5 41c-2.7-5.6-8.1-9-13.9-9C543 32 304 64 304 64S65 32 64.4 32c-5.8 0-11.2 3.5-13.9 9L1.6 143.8c-4.4 9.2.3 20.2 9.6 23l49.5 14.9V393c0 14.7 9.5 27.5 23 31l205.4 54.1c13 3.4 23.7 1.5 29.5 0L524.2 424c13.5-3.6 23-16.4 23-31V181.7l49.5-14.9c9.4-2.8 14-13.8 9.7-23zM73 65.3l180.9 24.3-57.1 99.8-159.9-48.1 36.1-76zm18.2 125.6C208.3 226.1 200.5 224 203.6 224c5.4 0 10.5-2.9 13.3-7.9l71.9-125.5V445L91.2 393V190.9zM516.8 393l-197.6 52V90.5L391.1 216c2.9 5 8 7.9 13.3 7.9 3.1 0-5 2.1 112.4-33.1V393zM411.3 189.3l-57.1-99.8L535 65.3l36.1 76-159.8 48z" fill="none" stroke="#9e9e9e" stroke-dasharray="5, 5" stroke-width="10"/></svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
class="svg-inline--fa fa-box-open fa-w-19"
aria-hidden="true"
data-icon="box-open"
data-prefix="fal"
focusable="false"
role="img"
version="1.1"
viewBox="0 0 608 512"
id="svg879"
sodipodi:docname="box-open-dashed.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<defs
id="defs883" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1015"
id="namedview881"
showgrid="false"
height="1"
inkscape:zoom="1.3037281"
inkscape:cx="241.86287"
inkscape:cy="228.99761"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg879" />
<metadata
id="metadata875">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<path
d="M 598.3861,153.84429 550.9386,54.115498 c -2.61984,-5.432698 -7.85943,-8.731118 -13.48716,-8.731118 -0.58218,0 -232.4831,31.043977 -232.4831,31.043977 0,0 -231.900991,-31.043977 -232.48317,-31.043977 -5.627722,0 -10.867325,3.395437 -13.487128,8.731118 L 11.550524,153.84429 c -4.2693062,8.92517 0.291089,19.59653 9.314845,22.31285 l 48.029702,14.45485 v 204.98726 c 0,14.26083 9.217821,26.67839 22.31683,30.07381 L 290.51092,478.1568 c 12.61384,3.29842 22.99601,1.45522 28.62375,0 l 199.49302,-52.48374 c 13.09902,-3.49238 22.31682,-15.90999 22.31682,-30.07381 V 190.61199 l 48.0297,-14.45485 c 9.1208,-2.71632 13.58418,-13.38768 9.41189,-22.31285 z M 80.829724,77.68952 256.35645,101.26354 200.95251,198.08199 45.802004,151.41893 Z m 17.659403,121.84763 c 113.621753,34.14839 106.053493,32.11104 109.061403,32.11104 5.23958,0 10.18807,-2.81336 12.90492,-7.66393 l 69.76438,-121.7506 V 446.04572 L 98.489127,395.59925 Z M 511.44748,395.59925 319.71687,446.04572 V 102.13665 l 69.76429,121.75064 c 2.8139,4.85059 7.76242,7.66395 12.90497,7.66395 3.00794,0 -4.85147,2.03721 109.06135,-32.1111 z M 409.08115,197.98489 353.67719,101.16653 529.10691,77.68952 l 35.02771,73.72941 z"
id="path877"
inkscape:connector-curvature="0"
style="fill:none;stroke:#9e9e9e;stroke-width:10.00000095;stroke-dasharray:4.9999999, 4.9999999" />
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 892 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="512"
height="512"
viewBox="0 0 135.46666 135.46667"
version="1.1"
id="svg7166"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="logo.svg"
inkscape:export-filename="/home/skylar/Documents/Projects/Assets/PackageHelper/logo.png"
inkscape:export-xdpi="96.000008"
inkscape:export-ydpi="96.000008">
<defs
id="defs7160">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath7298">
<rect
style="fill:#000000;fill-opacity:1;stroke-width:0.49260601"
id="rect7300"
width="252.21428"
height="252.21428"
x="108.23811"
y="62.791664"
rx="14.778181"
ry="14.778181" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.7"
inkscape:cx="-35.714286"
inkscape:cy="502.85714"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1015"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata7163">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-161.53332)">
<g
id="g7294"
transform="matrix(0.5371094,0,0,0.5371094,-58.135705,127.80733)"
clip-path="url(#clipPath7298)">
<g
inkscape:label="Layer 1"
id="layer1-7"
transform="translate(98.878579,27.365487)">
<rect
style="opacity:1;fill:#2196f3;fill-opacity:1;stroke:none;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect930"
width="270.93332"
height="270.93332"
x="2.9541011e-06"
y="26.06665" />
<path
sodipodi:type="star"
style="opacity:1;fill:#1976d2;fill-opacity:1;stroke:none;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path932"
sodipodi:sides="3"
sodipodi:cx="37.468803"
sodipodi:cy="275.44373"
sodipodi:r1="43.112488"
sodipodi:r2="21.556244"
sodipodi:arg1="0.52359878"
sodipodi:arg2="1.5707963"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 74.805313,296.99997 -74.67301934,0 37.33651034,-64.66873 z"
inkscape:transform-center-y="-25.330169"
transform="matrix(2.1686357,0,0,2.3501502,1.4459442,-410.066)" />
<path
transform="matrix(1.6940182,0,0,1.3303796,116.31992,-124.58106)"
inkscape:transform-center-y="-14.338981"
d="m 74.805313,296.99997 -74.67301934,0 37.33651034,-64.66873 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="1.5707963"
sodipodi:arg1="0.52359878"
sodipodi:r2="21.556244"
sodipodi:r1="43.112488"
sodipodi:cy="275.44373"
sodipodi:cx="37.468803"
sodipodi:sides="3"
id="path934"
style="opacity:0.5;fill:#1976d2;fill-opacity:1;stroke:none;stroke-width:1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
sodipodi:type="star" />
<path
inkscape:connector-curvature="0"
style="fill:#acd6ff;fill-opacity:0.46733668;stroke-width:0.06555524"
d="m 203.06397,109.73197 c 0.26877,-0.70144 0.41955,-1.46844 0.41955,-2.26821 0,-3.47443 -2.81888,-6.29331 -6.2933,-6.29331 -1.29145,0 -2.49766,0.39334 -3.4941,1.062 -1.81588,-3.146647 -5.20509,-5.257527 -9.09252,-5.257527 -5.79508,0 -10.48885,4.693747 -10.48885,10.488837 0,0.177 0.007,0.354 0.0131,0.531 -3.67109,1.29144 -6.30641,4.79209 -6.30641,8.90895 0,5.21164 4.22832,9.43996 9.43996,9.43996 h 24.12433 c 4.63476,0 8.39108,-3.75631 8.39108,-8.39107 0,-4.05787 -2.88443,-7.44708 -6.71286,-8.22063 z"
id="path2" />
</g>
<g
inkscape:label="Layer 1"
id="layer1-3"
transform="matrix(0.82142858,0,0,0.82142858,178.59848,0.5726301)">
<g
transform="matrix(0.26458333,0,0,0.26458333,8.5989574,161.53331)"
class="fa-group"
id="g6">
<path
style="opacity:1;fill:#7ac0f8;fill-opacity:1"
inkscape:connector-curvature="0"
class="fa-secondary"
d="m 256,32 32,128 v 112 a 16,16 0 0 1 -16,16 H 176 A 16,16 0 0 1 160,272 V 160 L 192,32 Z"
id="path2-5" />
<path
style="fill:#f7fbff;fill-opacity:1"
inkscape:connector-curvature="0"
class="fa-primary"
d="m 446.7,160 c 0.4,-0.5 0.5,-0.7 0.9,-1.2 L 391.3,53.9 A 32,32 0 0 0 360.9,32 H 256 l 32,128 z M 160,160 192,32 H 87.1 A 32,32 0 0 0 56.7,53.9 L 0.4,158.8 c 0.4,0.5 0.5,0.7 0.9,1.2 z m 128,32 v 80 a 16,16 0 0 1 -16,16 H 176 A 16,16 0 0 1 160,272 V 192 H 0 v 256 a 32,32 0 0 0 32,32 h 384 a 32,32 0 0 0 32,-32 V 192 Z"
id="path4" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
class="svg-inline--fa fa-box-open fa-w-19"
aria-hidden="true"
data-icon="box-open"
data-prefix="fal"
focusable="false"
role="img"
version="1.1"
viewBox="0 0 608 512"
id="svg879"
sodipodi:docname="note-dashed.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<defs
id="defs883" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1015"
id="namedview881"
showgrid="false"
height="1"
inkscape:zoom="1.3037281"
inkscape:cx="241.86287"
inkscape:cy="228.99761"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg879" />
<metadata
id="metadata875">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<path
d="M 392,320 H 528 V 56.000003 c 0,-13.3 -10.7,-24 -24,-24 H 104 c -13.3,0 -24,10.7 -24,24 V 456 c 0,13.3 10.7,24 24,24 H 368 V 344 c 0,-13.2 10.8,-24 24,-24 z m 129,55 -98,98 c -4.5,4.5 -10.6,7 -17,7 h -6 V 352 h 128 v 6.1 c 0,6.3 -2.5,12.4 -7,16.9 z"
id="path2"
style="fill:none;stroke:#9e9e9e;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:5, 5;stroke-dashoffset:0;stroke-opacity:1"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -35,18 +35,34 @@ $("#addpackagebtn").click(function () {
});
return;
}
if ($("input[name=zipcode]").val().trim() == "") {
playSound("error");
app.toast.show({
text: "Please fill in a ZIP code.",
position: "bottom",
destroyOnClose: true,
closeTimeout: 1000 * 10
});
return;
}
// Save city/state if changed
if (localStorage.getItem("citystate") != $("input[name=citystate]").val().trim()) {
localStorage.setItem("citystate", $("input[name=citystate]").val().trim());
if (getStorage("citystate") != $("input[name=citystate]").val().trim()) {
setStorage("citystate", $("input[name=citystate]").val().trim());
}
if (getStorage("zipcode") != $("input[name=zipcode]").val().trim()) {
setStorage("zipcode", $("input[name=zipcode]").val().trim());
}
var address = ($("input[name=number]").val() + " " + $("input[name=street]").val()).toUpperCase();
$("#no-history").addClass("display-none");
addPackageByAddress(
$("input[name=number]").val().toUpperCase(),
$("input[name=unit]").val().toUpperCase(),
$("input[name=street]").val().toUpperCase(),
$("input[name=citystate]").val().toUpperCase(),
$("input[name=zipcode]").val().toUpperCase(),
$("input[name=itemtype]:checked").val(),
function (ids) {
var packageObj = getPackage(ids.packageID);
@ -67,7 +83,7 @@ $("#addpackagebtn").click(function () {
// Remove any pre-existing click handlers from the history list,
// otherwise the user will see a number of confirm prompts equal to the number
// of times they've opened the manage page
// of times they've opened the add items page
$(".view-main").off("click", "#historylist .history-list-item");
$(".view-main").on("click", "#historylist .history-list-item", function () {
@ -81,7 +97,36 @@ $(".view-main").on("click", "#historylist .history-list-item", function () {
});
});
document.getElementById("housenumberinput").onfocus = function () {
document.getElementById("housenumberinput").value = "";
}
document.getElementById("unitinput").onfocus = function () {
document.getElementById("unitinput").value = "";
}
// Restore user's last entered city/state combo
if (localStorage.getItem("citystate") != null) {
$("input[name=citystate]").val(localStorage.getItem("citystate"));
if (inStorage("citystate")) {
$("input[name=citystate]").val(getStorage("citystate").replace(/\s*[0-9]{5}$/, ""));
}
if (inStorage("zipcode") && getStorage("zipcode").trim() != "") {
$("input[name=zipcode]").val(getStorage("zipcode"));
} else {
if (inStorage("citystate") && /^.+ [0-9]{5}$/.test(getStorage("citystate"))) {
$("input[name=zipcode]").val(getStorage("citystate").split(" ").pop());
}
}
function toggleLettersInAddressNumber() {
if ($("#address-has-letters-checkbox").prop("checked")) {
// disallow
$("#address-has-letters-checkbox").prop("checked", false);
$("#housenumberinput").attr("type", "number");
$("#housenumberinput").attr("placeholder", "1234");
} else {
// allow
$("#address-has-letters-checkbox").prop("checked", true);
$("#housenumberinput").attr("type", "text");
$("#housenumberinput").attr("placeholder", "1A2B3");
}
}

View File

@ -7,15 +7,15 @@
var sfx = {};
function initSFX() {
if (localStorage.getItem("alertsound") == null) {
localStorage.setItem("alertsound", "sonar");
if (getStorage("alertsound") == null) {
setStorage("alertsound", "sonar");
}
if (localStorage.getItem("alertvolume") == null) {
localStorage.setItem("alertvolume", 100);
if (getStorage("alertvolume") == null) {
setStorage("alertvolume", 100);
}
var alertNoiseFile = SETTINGS.alertsounds[localStorage.getItem("alertsound")].file;
var alertVolume = localStorage.getItem("alertvolume");
var alertNoiseFile = SETTINGS.alertsounds[getStorage("alertsound")].file;
var alertVolume = getStorage("alertvolume");
sfx = {
"alert": new Audio("assets/audio/" + alertNoiseFile),

View File

@ -7,12 +7,12 @@
var autofillDB = {};
var autofillStreetDB = [];
if (localStorage.getItem("autofill_db") != null) {
autofillDB = JSON.parse(localStorage.getItem("autofill_db"));
if (getStorage("autofill_db") != null) {
autofillDB = JSON.parse(getStorage("autofill_db"));
}
if (localStorage.getItem("autofill_streetdb") != null) {
autofillStreetDB = JSON.parse(localStorage.getItem("autofill_streetdb"));
if (getStorage("autofill_streetdb") != null) {
autofillStreetDB = JSON.parse(getStorage("autofill_streetdb"));
}
function addressToNumberAndStreet(address) {
@ -54,7 +54,7 @@ function addAutofillEntry(address) {
}
}
localStorage.setItem("autofill_db", JSON.stringify(autofillDB));
setStorage("autofill_db", JSON.stringify(autofillDB));
var found = false;
@ -70,7 +70,7 @@ function addAutofillEntry(address) {
autofillStreetDB.push([street, 1]);
}
localStorage.setItem("autofill_streetdb", JSON.stringify(autofillStreetDB));
setStorage("autofill_streetdb", JSON.stringify(autofillStreetDB));
}
function searchAutofill(q, number) {
@ -132,4 +132,21 @@ function searchAutofillByStreet(q) {
}
return streets;
}
function setupStreetAutofill(streetBox, numberBox) {
app.autocomplete.create({
inputEl: streetBox,
openIn: 'dropdown',
/* If we set valueProperty to "id" then input value on select will be set according to this property */
valueProperty: 'name', //object's "value" property name
textProperty: 'name', //object's "text" property name
limit: 10, //limit to 10 results
typeahead: true,
dropdownPlaceholderText: '',
source: function (query, render) {
var streets = searchAutofill(query, $(numberBox).val());
render(streets);
}
});
}

View File

@ -15,9 +15,10 @@ $(".view-main").on("click", "#addresslist .package-list-item .directions-btn", f
window.open($(this).attr("href"), "_system");
});
$(".view-main").on("swipeout:delete", "#addresslist .package-list-item", function () {
console.log("Info", "Deleting package", $(this).data("packageid"));
deletePackage($(this).data("packageid"));
$(".view-main").on("click", "#addresslist .package-list-item .delete-btn", function () {
var id = $(this).parents(".package-list-item").data("packageid");
console.log("Info", "Deleting package", id);
deletePackage(id);
});
// Searchbar is setup in routes.js, this is for forcing a wider scope
@ -41,13 +42,13 @@ function updateDistances(latitude, longitude) {
function loadPackageList(sortType) {
// If no sort type is specified, use the saved pref or a default one
if (typeof sortType == 'undefined') {
if (localStorage.getItem("sorttype") == null) {
localStorage.setItem("sorttype", "distance_asc");
if (getStorage("sorttype") == null) {
setStorage("sorttype", "distance_asc");
}
sortType = localStorage.getItem("sorttype");
sortType = getStorage("sorttype");
} else {
// save the current sorting order so it'll stay consistent next time the list is refreshed
localStorage.setItem("sorttype", sortType);
setStorage("sorttype", sortType);
}
updateDistances(userPosition.coords.latitude, userPosition.coords.longitude);
@ -92,7 +93,7 @@ function loadPackageList(sortType) {
});
$("#addresslist").html("");
$("#addresslist ul").html("");
if (packages.length == 0) {
$("#no-packages-display").removeClass("display-none");
@ -103,40 +104,66 @@ function loadPackageList(sortType) {
for (var i = 0; i < sortedPackages.length; i++) {
for (var j = 0; j < sortedPackages[i].value.items.length; j++) {
var item = sortedPackages[i].value.items[j];
var icon = getIconForType(item.type);
var icon1 = getIconForType(item.type);
var icon2 = "";
var classes = "";
var delivered = false;
if (item.delivered) {
delivered = true;
icon = "far fa-check-circle";
icon2 = icon1;
icon1 = "far fa-check-circle fa-fw";
classes = "text-color-green";
} else if (typeof sortedPackages[i].value.distance != 'undefined' && sortedPackages[i].value.distance * 1 < localStorage.getItem("alertradius") * 1) {
} else if (typeof sortedPackages[i].value.distance != 'undefined' && sortedPackages[i].value.distance * 1 < getStorage("alertradius") * 1) {
classes = "text-color-deeporange";
}
$("#addresslist").append(
'<li class="swipeout package-list-item" data-packageid="' + item.id + '" data-coordid=' + sortedPackages[i].value.id + '>'
+ '<div class="item-content swipeout-content ' + classes + '" data-packageid="' + item.id + '" data-latitude="' + sortedPackages[i].value.coords[0] + '" data-longitude="' + sortedPackages[i].value.coords[1] + '">'
+ ' <div class="item-media ' + classes + '">'
+ ' <i class="icon ' + icon + '"></i>'
+ ' </div>'
$("#addresslist ul").append(
'<li class="package-list-item accordion-item" data-packageid="' + item.id + '" data-coordid=' + sortedPackages[i].value.id + '>'
+ '<div class="item-content item-link ' + classes + '" data-packageid="' + item.id + '" data-latitude="' + sortedPackages[i].value.coords[0] + '" data-longitude="' + sortedPackages[i].value.coords[1] + '">'
+ ' <div class="item-inner">'
+ ' <div class="item-title">'
+ ' <i class="' + icon1 + '"></i>'
+ ' ' + item.address
+ ' </div>'
+ ' <div class="item-footer">'
+ ' <i class="' + icon2 + '"></i>'
+ ' <span class="distance">' + (typeof sortedPackages[i].value.distance != 'undefined' ? getDisplayDistance(sortedPackages[i].value.distance) : '...') + '</span>'
+ ' </div>'
+ ' </div>'
+ '</div>'
+ '<div class="swipeout-actions-left">'
+ ' <a href="#" class="color-green deliver-btn" data-packageid="' + item.id + '">' + (delivered ? "<i class='fas fa-undo'></i>&nbsp; Undeliver" : "<i class='fas fa-check'></i>&nbsp; Deliver") + '</a>'
+ ' <a href="geo:' + sortedPackages[i].value.coords[0] + ',' + sortedPackages[i].value.coords[1] + '" class="color-blue directions-btn"><i class="material-icons">directions</i></a>'
+ '</div>'
+ '<div class="swipeout-actions-right">'
+ ' <a href="#" class="swipeout-delete"><i class="material-icons">delete</i>&nbsp; Delete</a>'
+ '<div class="accordion-item-content">'
+ ' <div class="row padding-horizontal">'
+ ' <a href="#" class="col deliver-btn button button-outline color-green" data-packageid="' + item.id + '">' + (delivered ? "<i class='fas fa-undo'></i> Undeliver" : "<i class='fas fa-check'></i>&nbsp; Deliver") + '</a>'
+ ' <a href="geo:' + sortedPackages[i].value.coords[0] + ',' + sortedPackages[i].value.coords[1] + '" class="col directions-btn button button-outline"><i class="fas fa-directions"></i> Directions</a>'
+ ' <a href="#" class="col delete-btn color-red button button-outline"><i class="fas fa-trash"></i> Delete</a>'
+ ' </div>'
+ ' </div>'
+ '</div>'
+ '</li>'
);
// $("#addresslist").append(
// '<li class="swipeout package-list-item" data-packageid="' + item.id + '" data-coordid=' + sortedPackages[i].value.id + '>'
// + '<div class="item-content swipeout-content ' + classes + '" data-packageid="' + item.id + '" data-latitude="' + sortedPackages[i].value.coords[0] + '" data-longitude="' + sortedPackages[i].value.coords[1] + '">'
// + ' <div class="item-media ' + classes + '">'
// + ' <i class="icon ' + icon + '"></i>'
// + ' </div>'
// + ' <div class="item-inner">'
// + ' <div class="item-title">'
// + ' ' + item.address
// + ' </div>'
// + ' <div class="item-footer">'
// + ' <span class="distance">' + (typeof sortedPackages[i].value.distance != 'undefined' ? getDisplayDistance(sortedPackages[i].value.distance) : '...') + '</span>'
// + ' </div>'
// + ' </div>'
// + '</div>'
// + '<div class="swipeout-actions-left">'
// + ' <a href="#" class="color-green deliver-btn" data-packageid="' + item.id + '">' + (delivered ? "<i class='fas fa-undo'></i>&nbsp; Undeliver" : "<i class='fas fa-check'></i>&nbsp; Deliver") + '</a>'
// + ' <a href="geo:' + sortedPackages[i].value.coords[0] + ',' + sortedPackages[i].value.coords[1] + '" class="color-blue directions-btn"><i class="material-icons">directions</i></a>'
// + '</div>'
// + '<div class="swipeout-actions-right">'
// + ' <a href="#" class="swipeout-delete"><i class="material-icons">delete</i>&nbsp; Delete</a>'
// + '</div>'
// + '</li>'
// );
}
}
@ -157,7 +184,7 @@ function confirmDeleteAllPackages() {
function () {
// clear
packages = [];
localStorage.setItem("packages", JSON.stringify(packages));
setStorage("packages", JSON.stringify(packages));
loadPackageList();
if (map != null) {
map.updatePackageLayer(packages);

View File

@ -11,13 +11,14 @@ var userPosition = {
latitude: 0.0,
longitude: 0.0,
accuracy: 999999
}
},
updated: 0
};
// Preload last known location while GPS warms up
if (localStorage.getItem("user_latitude") != null && localStorage.getItem("user_longitude") != null) {
userPosition.coords.latitude = localStorage.getItem("user_latitude");
userPosition.coords.longitude = localStorage.getItem("user_longitude");
if (getStorage("user_latitude") != null && getStorage("user_longitude") != null) {
userPosition.coords.latitude = getStorage("user_latitude");
userPosition.coords.longitude = getStorage("user_longitude");
}
// Request the user's IP geolocation as a poor substitute for an actual location
@ -31,6 +32,7 @@ $.ajax({
userPosition.coords.latitude = resp.location.latitude;
userPosition.coords.longitude = resp.location.longitude;
userPosition.coords.accuracy = 99999;
userPosition.updated = time();
}
}
});
@ -41,16 +43,17 @@ var mapLocationControlStarted = false;
if ("geolocation" in navigator) {
navigator.geolocation.watchPosition(function (position) {
userPosition = position;
localStorage.setItem("user_latitude", userPosition.coords.latitude);
localStorage.setItem("user_longitude", userPosition.coords.longitude);
userPosition.coords = position.coords;
userPosition.updated = time();
setStorage("user_latitude", userPosition.coords.latitude);
setStorage("user_longitude", userPosition.coords.longitude);
if (mapLocationControlStarted) {
// Don't refresh at an interval less than ten seconds
var currentTimestamp = Math.floor(Date.now() / 1000);
if (lastGpsUpdateTimestamp < (currentTimestamp - 10)) {
updateDistances(position.coords.latitude, position.coords.longitude);
var alertinterval = localStorage.getItem("alertinterval");
var alertinterval = getStorage("alertinterval");
if (alertinterval == null) {
alertinterval = 30;
} else {
@ -59,7 +62,7 @@ if ("geolocation" in navigator) {
lastGpsUpdateTimestamp = currentTimestamp;
for (var i = 0; i < packages.length; i++) {
if (packages[i].distance * 1 < localStorage.getItem("alertradius") * 1) {
if (packages[i].distance * 1 < getStorage("alertradius") * 1) {
if (packages[i].lastAlert > currentTimestamp - alertinterval) {
continue;
@ -178,7 +181,7 @@ function getDisplayDistance(meters, space) {
if (typeof space == 'undefined') {
space = true;
}
var units = localStorage.getItem("units");
var units = getStorage("units");
if (units == null) {
units = "metric";

37
www/assets/js/login.js Normal file
View File

@ -0,0 +1,37 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
$(window).on("message", function (e) {
var data = e.originalEvent.data;
var parts = data.split("&");
var u = "";
var p = "";
for (var i = 0; i < parts.length; i++) {
if (parts[i].startsWith("user:")) {
u = parts[i].replace("user:", "");
} else if (parts[i].startsWith("password:")) {
p = parts[i].replace("password:", "");
}
}
if (u != "" && p != "") {
setStorage("username", u);
setStorage("password", p);
// Reset last change so we won't overwrite server settings
setStorage("lastchange", 0, true);
app.toast.show({
text: "You are now logged in!",
position: "bottom",
destroyOnClose: true,
closeTimeout: 1000 * 3
});
restartApplication();
} else {
app.dialog.alert("There was a problem. Try again later.", "Error");
}
});

View File

@ -37,18 +37,6 @@ function restartApplication() {
window.location = "index.html";
}
/**
* Generate a UUID.
* From https://stackoverflow.com/a/2117523
* @returns {String}
*/
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
router.on("pageInit", function (pagedata) {
pagedata.$el.find('script').each(function (el) {
@ -62,24 +50,46 @@ router.on("pageInit", function (pagedata) {
});
});
/**
* Perform back button behavior.
* Call this function whenever the equivalent to the Android back button is pressed.
* @returns {undefined}
*/
function handleBackButton() {
// Close map sheet if it's open
if ($(".sheet-modal").hasClass("modal-in")) {
app.sheet.close();
} else if ($(".searchbar-enabled")[0]) {
app.searchbar.disable();
} else if (scanningBarcode) {
return;
} else {
router.back({force: true, ignoreCache: true});
}
}
$(document).keyup(function (e) {
if (e.key === "Escape" || e.keyCode == 27) {
handleBackButton();
}
});
router.on("routeChange", function (newRoute) {
console.log("Info", "Navigating to ", newRoute.path);
});
// Set alert radius to 100 meters by default
if (localStorage.getItem("alertradius") == null) {
localStorage.setItem("alertradius", 100);
if (getStorage("alertradius") == null) {
setStorage("alertradius", 100);
}
// Set default alert sound volume
if (localStorage.getItem("alertvolume") == null) {
localStorage.setItem("alertvolume", 100);
if (getStorage("alertvolume") == null) {
setStorage("alertvolume", 100);
}
if (localStorage.getItem("darktheme") == "true") {
if (getStorage("darktheme") == "true") {
$("#app").addClass("theme-dark");
}
initPlatform();
router.navigate("/home");

View File

@ -9,10 +9,10 @@ var map = null;
var maptype = "mapbox";
function createMap() {
if (localStorage.getItem("maptype") == null) {
localStorage.setItem("maptype", "mapbox");
if (getStorage("maptype") == null) {
setStorage("maptype", "mapbox");
}
maptype = localStorage.getItem("maptype");
maptype = getStorage("maptype");
if (maptype == "mapbox") {
if (mapboxgl.supported()) {
map = mapboxMap();

View File

@ -16,13 +16,13 @@ function leafletMap() {
map.maptype = "leaflet";
if (localStorage.getItem("mapsource") == null) {
localStorage.setItem("mapsource", "liberty");
if (getStorage("mapsource") == null) {
setStorage("mapsource", "liberty");
}
$("#mapbox").css("background-color", SETTINGS.maptileurls[localStorage.getItem("mapsource")].bgcolor);
$("#mapbox").css("background-color", SETTINGS.maptileurls[getStorage("mapsource")].bgcolor);
L.tileLayer(SETTINGS.maptileurls[localStorage.getItem("mapsource")].url, {
L.tileLayer(SETTINGS.maptileurls[getStorage("mapsource")].url, {
minZoom: 1,
maxZoom: 19
}).addTo(map);
@ -32,7 +32,7 @@ function leafletMap() {
showPopup: false,
locateOptions: {
enableHighAccuracy: true,
maxZoom: localStorage.getItem("trackzoom") == null ? 16 : localStorage.getItem("trackzoom") * 1
maxZoom: getStorage("trackzoom") == null ? 16 : getStorage("trackzoom") * 1
},
setView: "untilPanOrZoom",
icon: "far fa-compass",

View File

@ -10,16 +10,16 @@ var firstload = true;
function mapboxMap() {
if (localStorage.getItem("mapsource") == null) {
localStorage.setItem("mapsource", "liberty");
if (getStorage("mapsource") == null) {
setStorage("mapsource", "liberty");
}
$("#mapbox").css("background-color", SETTINGS.maptileurls[localStorage.getItem("mapsource")].bgcolor);
$("#mapbox").css("background-color", SETTINGS.maptileurls[getStorage("mapsource")].bgcolor);
mapboxgl.accessToken = '';
var map = new mapboxgl.Map({
container: 'mapbox',
style: SETTINGS.maptileurls[localStorage.getItem("mapsource")].json,
style: SETTINGS.maptileurls[getStorage("mapsource")].json,
attributionControl: false,
dragPan: true,
pitch: 0,
@ -40,12 +40,20 @@ function mapboxMap() {
timeout: 10 * 1000
},
fitBoundsOptions: {
maxZoom: localStorage.getItem("trackzoom") == null ? 16 : localStorage.getItem("trackzoom") * 1
maxZoom: getStorage("trackzoom") == null ? 16 : getStorage("trackzoom") * 1
},
trackUserLocation: true
}), 'top-left'
);
if (getStorage("mapscale") !== "false") {
map.addControl(
new mapboxgl.ScaleControl({
unit: getStorage("units") == "imperial" ? "imperial" : "metric"
})
);
}
map.startLocateControl = function () {
// stub
}

223
www/assets/js/notes.js Normal file
View File

@ -0,0 +1,223 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
if (inStorage("notes")) {
var notes = JSON.parse(getStorage("notes"));
} else {
var notes = [];
setStorage("notes", "[]");
}
function findNote(number, street) {
for (i in notes) {
if (notes[i].number == number && notes[i].street.toUpperCase() == street.toUpperCase()) {
return notes[i];
}
}
return null;
}
function isDeliverable(number, street) {
var note = findNote(number, street);
var ok = true;
var reason = "";
if (note != null) {
for (t in note.toggles) {
if (!note.toggles.hasOwnProperty(t)) {
continue;
}
for (tt in SETTINGS.routenotetoggles) {
if (SETTINGS.routenotetoggles[tt].id == t) {
if (SETTINGS.routenotetoggles[tt].preventsDelivery == true && note.toggles[t] == true) {
ok = false;
reason = SETTINGS.routenotetoggles[tt].reason;
}
}
}
}
}
return {
ok: ok,
reason: reason
};
}
function saveNote(id) {
var exists = false;
var index = -1;
for (var i = 0; i < notes.length; i++) {
if (notes[i].id == id) {
exists = true;
index = i;
}
}
var note = {
id: id,
number: "",
street: "",
zipcode: "",
route: "",
notes: "",
toggles: {}
};
if (exists) {
note = notes[index];
}
note.number = $("input[name=number]").val().trim();
note.street = $("input[name=street]").val().trim();
note.zipcode = $("input[name=zipcode]").val().trim();
note.route = $("input[name=route]").val().trim().toUpperCase();
note.notes = $("textarea#notes").val().trim();
if (note.number == "") {
app.dialog.alert("Fill in an address number.", "Error");
return false;
}
if (note.street == "") {
app.dialog.alert("Fill in a street.", "Error");
return false;
}
if (note.zipcode == "") {
app.dialog.alert("Fill in a zip code.", "Error");
return false;
}
if (note.route == "" || /^[CRHG][0-9]{3}$/.test(note.route) != true) {
app.dialog.alert("Fill in a route (examples: C123, R001, H050).", "Error");
return false;
}
setStorage("lastrouteid", note.route);
setStorage("zipcode", note.zipcode);
for (i in SETTINGS.routenotetoggles) {
var toggle = SETTINGS.routenotetoggles[i];
note.toggles[toggle.id] = $(".note-toggle[data-id=" + toggle.id + "]").is(":checked");
}
if (exists) {
notes[index] = note;
} else {
notes.push(note);
}
setStorage("notes", JSON.stringify(notes));
app.toast.show({
text: "<i class='fas fa-check'></i> Note saved!",
position: "bottom",
destroyOnClose: true,
closeTimeout: 1000 * 3
});
}
function deleteNote(id) {
for (var i = 0; i < notes.length; i++) {
if (notes[i].id == id) {
notes.splice(i, 1);
}
}
setStorage("notes", JSON.stringify(notes));
}
$(".view-main").on("click", "#savenotebtn", function () {
saveNote($(this).data("noteid"));
});
$(".view-main").on("click", ".editnotebtn", function () {
var noteid = $(this).data("noteid");
var note = {};
for (var i = 0; i < notes.length; i++) {
if (notes[i].id == noteid) {
note = notes[i];
}
}
var toggles = [];
for (t in SETTINGS.routenotetoggles) {
var toggle = SETTINGS.routenotetoggles[t];
toggles.push({
id: toggle.id,
name: toggle.name,
checked: note.toggles[toggle.id] == true
});
}
router.navigate("/myroute/editnote", {
context: {
title: "Edit Note",
toggles: toggles,
noteid: noteid,
note: note
}
});
});
$(".view-main").on("click", ".deletenotebtn", function () {
var noteid = $(this).data("noteid");
app.dialog.confirm("Delete note?", "Confirm", function () {
deleteNote(noteid);
router.navigate("/myroute", {
reloadCurrent: true
})
});
});
$(".view-main").on("blur", "#notenumberinput,#notestreetinput", function () {
if (findNote($("#notenumberinput").val(), $("#notestreetinput").val()) != null) {
app.dialog.alert("A note already exists for that address.", "Warning");
}
});
/*
* Save note with Ctrl-S
*/
$(".view-main").on("keydown", ".page[data-name=editnote]", function (event) {
if (event.ctrlKey || event.metaKey) {
switch (String.fromCharCode(event.which).toLowerCase()) {
case 's':
event.preventDefault();
saveNote($("#savenotebtn").data("noteid"));
break;
}
}
});
function getToggleName(id) {
for (i in SETTINGS.routenotetoggles) {
if (SETTINGS.routenotetoggles[i].id == id) {
return SETTINGS.routenotetoggles[i].name;
}
}
return "";
}
function getToggleIcon(id) {
for (i in SETTINGS.routenotetoggles) {
if (SETTINGS.routenotetoggles[i].id == id) {
return SETTINGS.routenotetoggles[i].icon;
}
}
return "";
}
Template7.registerHelper('notetogglename', function (key) {
return getToggleName(key);
});
Template7.registerHelper('notetoggleicon', function (key) {
return getToggleIcon(key);
});
Template7.registerHelper('newlinestobr', function (text) {
return text.replace(/(?:\r\n|\r|\n)/g, '<br>');
});

View File

@ -6,8 +6,8 @@
var packages = [];
if (localStorage.getItem("packages") != null) {
packages = JSON.parse(localStorage.getItem("packages"));
if (getStorage("packages") != null) {
packages = JSON.parse(getStorage("packages"));
}
/**
@ -91,6 +91,13 @@ function addPackage(address, latitude, longitude, type, callback, deadline) {
deadline = false;
}
if (typeof address == "object") {
var extendedaddress = address;
address = extendedaddress.address;
} else {
var extendedaddress = false;
}
// Extra precision makes the map stupider,
// and doesn't really increase accuracy since four decimal places ~= 11m
latitude = +(parseFloat("" + latitude).toFixed(4));
@ -103,6 +110,7 @@ function addPackage(address, latitude, longitude, type, callback, deadline) {
if (packages[i].coords[0] == latitude && packages[i].coords[1] == longitude) {
coordsID = packages[i].id;
packages[i].items.push({
extended: extendedaddress,
address: address,
delivered: false,
type: type,
@ -124,6 +132,7 @@ function addPackage(address, latitude, longitude, type, callback, deadline) {
address: address,
items: [
{
extended: extendedaddress,
address: address,
delivered: false,
type: type,
@ -133,7 +142,7 @@ function addPackage(address, latitude, longitude, type, callback, deadline) {
]
});
}
localStorage.setItem("packages", JSON.stringify(packages));
setStorage("packages", JSON.stringify(packages));
playSound("ok");
@ -196,17 +205,103 @@ function importPackageList(newlist) {
}
}
}
localStorage.setItem("packages", JSON.stringify(packages));
setStorage("packages", JSON.stringify(packages));
if (map != null) {
reloadMap();
}
return skipped;
}
function mapCalibrate(item, packagesentry) {
// Determine if the delivery location isn't near the map pin
if (userPosition.coords.accuracy < 20 && timeDiff(userPosition.updated) < 10) {
// User location is accurate, check distance
var distance = getDistance(packagesentry.coords[0], packagesentry.coords[1], userPosition.coords.latitude, userPosition.coords.longitude);
var lat = userPosition.coords.latitude;
var lon = userPosition.coords.longitude;
if (distance > 100) { // Over 100 meters distance
if (typeof item.extended == "object") {
// we have all the info we need
var fixmap = function (item, latitude, longitude, locationtype) {
$.ajax({
type: "POST",
url: SETTINGS.mapfixapi,
data: {
number: item.extended.number,
unit: item.extended.unit,
street: item.extended.street,
citystate: item.extended.citystate,
zip: item.extended.zip,
latitude: latitude,
longitude: longitude,
locationtype: locationtype
},
success: function () {
app.toast.show({
text: "Calibration recorded. Thank you for improving the map!",
position: "bottom",
destroyOnClose: true,
closeTimeout: 1000 * 3
});
},
error: function () {
// try again in five minutes
setTimeout(function () {
fixmap(item, latitude, longitude, locationtype);
}, 1000 * 60 * 5);
},
dataType: "json"
});
};
app.dialog.create({
title: 'Map Calibration',
text: "Your actual location doesn't match the map location for the " + SETTINGS.itemtypes[item.type].name + " at " + item.address + ". Where are you?",
buttons: [
{
text: 'Address',
close: true
},
{
text: 'Mailbox/CBU',
close: true
},
{
text: 'Parcel Locker',
close: true
},
{
text: "Other/Cancel",
close: true
}
],
verticalButtons: true,
onClick: function (dialog, index) {
switch (index) {
case 0:
fixmap(item, lat, lon, "address");
break;
case 1:
fixmap(item, lat, lon, "mailbox");
break;
case 2:
fixmap(item, lat, lon, "locker");
break;
default:
return;
}
}
}).open();
}
}
}
}
function markDelivered(id, delivered) {
for (var i = 0; i < packages.length; i++) {
for (var j = 0; j < packages[i].items.length; j++) {
if (packages[i].items[j].id == id) {
if (typeof delivered == 'undefined') {
if (packages[i].items[j].delivered == false) {
delivered = true;
@ -218,12 +313,16 @@ function markDelivered(id, delivered) {
packages[i].items[j].delivered = delivered;
if (delivered) {
packages[i].items[j].deliverytimestamp = Date.now();
setStorage("packages", JSON.stringify(packages));
mapCalibrate(packages[i].items[j], packages[i]);
return; // so we don't keep looping over the rest of the packages
}
}
}
}
localStorage.setItem("packages", JSON.stringify(packages));
}
function confirmDeletePackage(package, callback) {
@ -250,7 +349,7 @@ function deletePackage(id, callback) {
packages.splice(i, 1);
}
localStorage.setItem("packages", JSON.stringify(packages));
setStorage("packages", JSON.stringify(packages));
loadPackageList();
if (typeof callback == 'function') {
@ -284,30 +383,40 @@ function countPackages() {
return count;
}
function addPackageByAddress(number, street, citystate, type, callback) {
function addPackageByAddress(number, unit, street, citystate, zip, type, callback) {
var requestfinished = false;
var searchingdialogopen = false;
var deadline = false;
var ajaxlookup = function () {
var geocodecache = localStorage.getItem("geocode_cache");
var geocodecache = getStorage("geocode_cache");
if (geocodecache == null) {
geocodecache = "{}";
localStorage.setItem("geocode_cache", "{}");
setStorage("geocode_cache", "{}");
}
geocodecache = JSON.parse(geocodecache);
var cachekey = number + " || " + street + " || " + citystate;
var cachekey = number + " || " + street + " || " + citystate + " || " + zip + " || " + SETTINGS.itemtypes[type].allowedlocationtypes;
if (unit != '') {
cachekey = number + " || " + unit + " || " + street + " || " + citystate + " || " + zip + " || " + SETTINGS.itemtypes[type].allowedlocationtypes;
}
var cacheitem = geocodecache[cachekey];
var timestamp = Math.floor(Date.now() / 1000);
if (typeof cacheitem != 'undefined') {
if (cacheitem.added + SETTINGS.geocodecacheexpiry < timestamp) {
console.log("Info", "Removing expired geocode cache item " + cachekey);
delete geocodecache[cachekey];
localStorage.setItem("geocode_cache", JSON.stringify(geocodecache));
setStorage("geocode_cache", JSON.stringify(geocodecache));
} else {
console.log("Info", "Using cached geocode result", cacheitem);
addPackage(cacheitem.address, cacheitem.latitude, cacheitem.longitude, type, callback, deadline);
addPackage({
address: cacheitem.address,
number: number,
unit: unit,
street: street,
citystate: citystate,
zip: zip
}, cacheitem.latitude, cacheitem.longitude, type, callback, deadline);
return;
}
}
@ -317,8 +426,11 @@ function addPackageByAddress(number, street, citystate, type, callback) {
dataType: 'json',
data: {
number: number,
unit: unit,
street: street,
citystate: citystate
citystate: citystate,
zip: zip,
type: SETTINGS.itemtypes[type].allowedlocationtypes
},
timeout: 15 * 1000,
success: function (resp) {
@ -329,14 +441,23 @@ function addPackageByAddress(number, street, citystate, type, callback) {
requestfinished = true;
if (resp.status == "OK") {
if (resp.accuracy.ok) {
addPackage(resp.address.street, resp.coords[0], resp.coords[1], type, callback, deadline);
addPackage({
address: resp.address.street,
number: number,
unit: unit,
street: street,
citystate: citystate,
zip: zip
}, resp.coords[0], resp.coords[1], type, callback, deadline);
geocodecache[cachekey] = {
number: resp.address.number,
unit: unit,
address: resp.address.street,
latitude: resp.coords[0],
longitude: resp.coords[1],
added: Math.floor(Date.now() / 1000)
};
localStorage.setItem("geocode_cache", JSON.stringify(geocodecache));
setStorage("geocode_cache", JSON.stringify(geocodecache));
} else {
playSound("error");
app.dialog.confirm(
@ -344,9 +465,23 @@ function addPackageByAddress(number, street, citystate, type, callback) {
"Accuracy Warning",
function (ok) {
if (resp.address.street == "") {
addPackage(address, resp.coords[0], resp.coords[1], type, callback, deadline);
addPackage({
address: address,
number: number,
unit: unit,
street: street,
citystate: citystate,
zip: zip
}, resp.coords[0], resp.coords[1], type, callback, deadline);
} else {
addPackage(resp.address.street, resp.coords[0], resp.coords[1], type, callback, deadline);
addPackage({
address: resp.address.street,
number: number,
unit: unit,
street: street,
citystate: citystate,
zip: zip
}, resp.coords[0], resp.coords[1], type, callback, deadline);
}
}
);
@ -376,71 +511,90 @@ function addPackageByAddress(number, street, citystate, type, callback) {
}, 750);
}
if (type == "express") {
if (localStorage.getItem("deadlinealarm_minutes") == null) {
localStorage.setItem("deadlinealarm_minutes", 20);
}
var minutes = localStorage.getItem("deadlinealarm_minutes");
app.dialog.create({
title: 'Express Item',
text: 'Set a reminder for ' + minutes + ' minutes before:',
buttons: [
{
text: '10:30 AM',
close: true
},
{
text: '12:00 PM',
close: true
},
{
text: '3:00 PM',
close: true
},
{
text: "No reminder",
color: "red",
close: true
}
],
verticalButtons: true,
onClick: function (dialog, index) {
deadline = new Date();
switch (index) {
case 0:
deadline.setMinutes(30);
deadline.setHours(10);
break;
case 1:
deadline.setMinutes(00);
deadline.setHours(12);
break;
case 2:
deadline.setMinutes(00);
deadline.setHours(12 + 3);
break;
case 3:
default:
deadline = false;
break;
}
if (deadline != false) {
deadline = deadline.getTime() / 1000;
}
ajaxlookup();
var prelookup = function () {
if (type == "express") {
if (getStorage("deadlinealarm_minutes") == null) {
setStorage("deadlinealarm_minutes", 20);
}
}).open();
var minutes = getStorage("deadlinealarm_minutes");
app.dialog.create({
title: 'Express Item',
text: 'Set a reminder for ' + minutes + ' minutes before:',
buttons: [
{
text: '10:30 AM',
close: true
},
{
text: '12:00 PM',
close: true
},
{
text: '3:00 PM',
close: true
},
{
text: "No reminder",
color: "red",
close: true
}
],
verticalButtons: true,
onClick: function (dialog, index) {
deadline = new Date();
switch (index) {
case 0:
deadline.setMinutes(30);
deadline.setHours(10);
break;
case 1:
deadline.setMinutes(00);
deadline.setHours(12);
break;
case 2:
deadline.setMinutes(00);
deadline.setHours(12 + 3);
break;
case 3:
default:
deadline = false;
break;
}
if (deadline != false) {
deadline = deadline.getTime() / 1000;
}
ajaxlookup();
}
}).open();
} else {
ajaxlookup();
}
}
var deliverable = isDeliverable(number, street);
if (deliverable.ok) {
prelookup();
} else {
ajaxlookup();
app.dialog.confirm(
"A route note says this address " + deliverable.reason + ". Add item anyways?",
"Confirm",
function () {
prelookup();
},
function () {
// cancel
}
);
}
}
function checkDeadlines() {
if (localStorage.getItem("deadlinealarm_minutes") == null) {
localStorage.setItem("deadlinealarm_minutes", 20);
if (getStorage("deadlinealarm_minutes") == null) {
setStorage("deadlinealarm_minutes", 20);
}
var minutes = localStorage.getItem("deadlinealarm_minutes");
var minutes = getStorage("deadlinealarm_minutes");
var currentTime = new Date().getTime() / 1000;
var deadlineTime = currentTime + (minutes * 60);
for (i in packages) {
@ -457,7 +611,7 @@ function checkDeadlines() {
}
);
packages[i].items[j].deadlinealarmed = true;
localStorage.setItem("packages", JSON.stringify(packages));
setStorage("packages", JSON.stringify(packages));
}
}
}

View File

@ -24,6 +24,8 @@ var scanBarcode = function (success, error) {
app.dialog.alert("You can't scan barcodes with this device.", "Sorry!");
}
var scanningBarcode = false;
var getLocation = function (success, error) {
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(function (position) {
@ -68,6 +70,7 @@ function setupHTML5BarcodeScanner() {
$("body").append('<script src="node_modules/@zxing/library/umd/index.min.js"></script>');
scanBarcode = function (success, error) {
scanningBarcode = true;
$("#web-barcode-ui").removeClass("hidden");
// Stolen from https://zxing-js.github.io/library/examples/multi-camera/
const codeReader = new ZXing.BrowserMultiFormatReader();
@ -83,6 +86,7 @@ function setupHTML5BarcodeScanner() {
selectedDeviceId = videoInputDevices[0].deviceId;
codeReader.decodeFromInputVideoDeviceContinuously(selectedDeviceId, 'barcode-viewer', (result, err) => {
scanningBarcode = false;
if (result) {
codeReader.reset();
$("#web-barcode-ui").addClass("hidden");
@ -99,10 +103,12 @@ function setupHTML5BarcodeScanner() {
});
})
.catch((err) => {
scanningBarcode = false;
console.error(err);
});
$("#web-barcode-ui").on("click", function () {
codeReader.reset();
scanningBarcode = false;
$("#web-barcode-ui").addClass("hidden");
});
};
@ -111,16 +117,9 @@ function setupHTML5BarcodeScanner() {
function initCordova() {
platform_type = "cordova";
// Handle back button to close things
document.addEventListener("backbutton", function (event) {
// Close map sheet if it's open
if ($(".sheet-modal").hasClass("modal-in")) {
app.sheet.close();
} else {
router.back({force: true, ignoreCache: true});
}
}, false);
document.addEventListener("backbutton", handleBackButton, false);
document.addEventListener("deviceready", function () {
if (localStorage.getItem("wakelock") == "true") {
if (getStorage("wakelock") == "true") {
window.powerManagement.acquire(function () {
console.log("Info", 'Wakelock acquired');
}, function () {
@ -144,13 +143,16 @@ function initCordova() {
if (typeof device != "undefined" && device.platform != "browser") {
scanBarcode = function (success, error) {
scanningBarcode = true;
cordova.plugins.barcodeScanner.scan(
function (result) {
scanningBarcode = false;
if (!result.cancelled) {
success(result.text);
}
},
function (err) {
scanningBarcode = false;
if (typeof error == "function") {
error(err);
}

View File

@ -5,48 +5,70 @@
*/
function logout() {
app.dialog.confirm(
"Are you sure you want to log out?",
"Log out?",
function () {
localStorage.removeItem('password');
localStorage.removeItem('username');
localStorage.removeItem('lastsync');
restartApplication();
}
);
}
function resyncAndRestart() {
app.toast.show({
text: "Syncing settings and restarting...",
position: "bottom",
destroyOnClose: true,
closeTimeout: 1000 * 10
});
syncNow(function () {
restartApplication();
});
}
function clearCaches() {
app.toast.show({
text: "Clearing caches. You may need to restart the app to see a difference.",
position: "bottom",
destroyOnClose: true,
closeTimeout: 1000 * 10
});
setStorage("geocode_cache", "{}");
if ('caches' in window) {
clearAllCaches();
}
}
$('.item-content[data-setting=darktheme] .toggle input').on("change", function () {
var checked = $(this).prop('checked');
localStorage.setItem("darktheme", checked);
setStorage("darktheme", checked);
if (localStorage.getItem("darktheme") == "true") {
$("#app").addClass("theme-dark");
} else {
$("#app").removeClass("theme-dark");
}
loadSettings();
});
$('.item-content[data-setting=showhelp] .toggle input').on("change", function () {
var checked = $(this).prop('checked');
localStorage.setItem("show_help", checked);
setStorage("show_help", checked);
});
$('.item-link[data-setting=units] select').on("change", function () {
localStorage.setItem("units", $('.item-link[data-setting=units] select').val());
setStorage("units", $('.item-link[data-setting=units] select').val());
});
$('.item-link[data-setting=trackzoom] select').on("change", function () {
localStorage.setItem("trackzoom", $('.item-link[data-setting=trackzoom] select').val());
setStorage("trackzoom", $('.item-link[data-setting=trackzoom] select').val());
});
$('.item-content[data-setting=wakelock] .toggle input').on("change", function () {
var checked = $(this).prop('checked');
localStorage.setItem("wakelock", checked);
setStorage("wakelock", checked);
if (platform_type == "cordova") {
if (localStorage.getItem("wakelock") == "true") {
window.powerManagement.acquire(function () {
console.log("Info", 'Wakelock acquired');
}, function () {
console.log("Warn", 'Failed to acquire wakelock');
});
} else {
window.powerManagement.release(function () {
console.log("Info", 'Wakelock released');
}, function () {
console.log("Warn", 'Failed to release wakelock');
});
}
loadSettings();
} else {
app.toast.show({
text: "This setting won't do anything on your device.",
@ -59,30 +81,37 @@ $('.item-content[data-setting=wakelock] .toggle input').on("change", function ()
$('.item-content[data-setting=alertvolume] .range-slider').on('range:changed', function (e, range) {
var val = app.range.get(".item-content[data-setting=alertvolume] .range-slider").getValue();
localStorage.setItem("alertvolume", val);
setStorage("alertvolume", val);
setVolume("alert", val);
playSound("alert");
});
$('.item-content[data-setting=alertradius] .range-slider').on('range:changed', function (e, range) {
var val = app.range.get(".item-content[data-setting=alertradius] .range-slider").getValue();
localStorage.setItem("alertradius", val);
setStorage("alertradius", val);
});
$('.item-content[data-setting=alertinterval] .range-slider').on('range:changed', function (e, range) {
var val = app.range.get(".item-content[data-setting=alertinterval] .range-slider").getValue();
localStorage.setItem("alertinterval", val);
setStorage("alertinterval", val);
});
$('.item-link[data-setting=mapsource] select').on("change", function () {
localStorage.setItem("mapsource", $('.item-link[data-setting=mapsource] select').val());
setStorage("mapsource", $('.item-link[data-setting=mapsource] select').val());
reloadMap();
});
$('.item-content[data-setting=mapscale] .toggle input').on("change", function () {
var checked = $(this).prop('checked');
setStorage("mapscale", checked ? "true" : "false");
reloadMap();
});
$('.item-content[data-setting=maptype] .toggle input').on("change", function () {
var checked = $(this).prop('checked');
localStorage.setItem("maptype", checked ? "leaflet" : "mapbox");
setStorage("maptype", checked ? "leaflet" : "mapbox");
maptype = checked ? "leaflet" : "mapbox";
@ -90,7 +119,7 @@ $('.item-content[data-setting=maptype] .toggle input').on("change", function ()
});
$('.item-link[data-setting=alertsound] select').on("change", function () {
localStorage.setItem("alertsound", $('.item-link[data-setting=alertsound] select').val());
setStorage("alertsound", $('.item-link[data-setting=alertsound] select').val());
// Reload sound effect stuff to apply new sound
initSFX();
// Play the selected sound

58
www/assets/js/storage.js Normal file
View File

@ -0,0 +1,58 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/**
* Save something to persistent storage.
* @param {string} key
* @param {string} value non-string values are converted to strings.
* @param {bool} nochangeupdate If true, the lastchange setting won't be updated.
* @returns {undefined}
*/
function setStorage(key, value, nochangeupdate) {
if (typeof nochangeupdate == 'undefined') {
nochangeupdate = false;
}
localStorage.setItem(key, value);
if (!nochangeupdate && !SETTINGS.synckeyblacklist.includes(key)) {
localStorage.setItem("lastchange", Date.now() / 1000);
}
}
/**
* Get an item from persistent storage.
* @param {type} key
* @returns {DOMString}
*/
function getStorage(key) {
return localStorage.getItem(key);
}
/**
* Check if an item is in the persistent storage.
* @param {string} key
* @returns {Boolean}
*/
function inStorage(key) {
return localStorage.getItem(key) != null;
}
/**
* Get all item from persistent storage.
* @returns {Array} [{key: "", value: ""},...]
*/
function getAllStorage() {
var all = [];
for (var key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
all.push({
key: key,
value: getStorage(key)
});
}
}
return all;
}

115
www/assets/js/sync.js Normal file
View File

@ -0,0 +1,115 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
function gatherSyncData() {
var data = {
localStorage: {},
changed: getStorage("lastchange") == null ? 0 : getStorage("lastchange"),
};
if (!inStorage("lastsync")) {
// first time syncing to the server, let's make sure
// the server settings take precedence
data.changed = 1;
}
var allitems = getAllStorage();
for (var i = 0; i < allitems.length; i++) {
var key = allitems[i].key;
var value = allitems[i].value;
if (SETTINGS.synckeyblacklist.includes(key)) {
continue;
}
data.localStorage[key] = value;
}
return data;
}
function syncDataToLocalStorage(data) {
for (var key in data.localStorage) {
if (data.localStorage.hasOwnProperty(key)) {
setStorage(key, data.localStorage[key], true);
}
}
}
function resolveSync(remotedata) {
var localchangetime = getStorage("lastchange");
if (remotedata.changed == null) {
// The server has nothing, this is the first sync
return true;
}
if (localchangetime == null) {
// No local setting changes but since we've gotten this far,
// the server has stuff for us
syncDataToLocalStorage(remotedata);
return true;
}
if (localchangetime < remotedata.changed) {
// The server has newer stuff for us
syncDataToLocalStorage(remotedata);
return true;
}
if (localchangetime >= remotedata.changed) {
// Our local data is newer or the same as the server copy
return true;
}
return false;
}
function syncNow(callback) {
var username = getStorage("username");
var password = getStorage("password");
if (username == null || password == null) {
return false;
}
var data = gatherSyncData();
$.post(SETTINGS.syncapi, {
username: username,
password: password,
data: JSON.stringify(data)
}, function (resp) {
if (resp.status == "OK") {
resolveSync(resp.data);
setStorage("lastsync", Date.now() / 1000);
if (typeof callback == "function") {
callback();
}
}
}, "json");
return true;
}
function loadSettings() {
if (getStorage("darktheme") == "true") {
$("#app").addClass("theme-dark");
} else {
$("#app").removeClass("theme-dark");
}
if (platform_type == "cordova") {
if (getStorage("wakelock") == "true") {
window.powerManagement.acquire(function () {
console.log("Info", 'Wakelock acquired');
}, function () {
console.log("Warn", 'Failed to acquire wakelock');
});
} else {
window.powerManagement.release(function () {
console.log("Info", 'Wakelock released');
}, function () {
console.log("Warn", 'Failed to release wakelock');
});
}
}
}
syncNow(loadSettings);
// Sync every two minutes
setInterval(function () {
syncNow(loadSettings);
}, 1000 * 60 * 2);

View File

@ -189,12 +189,12 @@ function openEventTypePopup(eventname) {
}
function saveScanCode(code) {
if (localStorage.getItem("scanevents") == null) {
localStorage.setItem("scanevents", "[]");
if (getStorage("scanevents") == null) {
setStorage("scanevents", "[]");
}
var events = JSON.parse(localStorage.getItem("scanevents"));
var events = JSON.parse(getStorage("scanevents"));
events.push(code);
localStorage.setItem("scanevents", JSON.stringify(events));
setStorage("scanevents", JSON.stringify(events));
}
$(".view-main").off("click", "#codelist li.codelist-entry");

View File

@ -11,7 +11,7 @@ function confirmDeleteScanEntries() {
"Clear Entries",
function () {
// clear
localStorage.setItem("scanevents", "[]");
setStorage("scanevents", "[]");
router.navigate("/toolbox/scanner/entries", {
reloadCurrent: true
});

View File

@ -88,7 +88,7 @@ function openTrackingHistory(code) {
}
// Keep last five tracking codes in history
var history = localStorage.getItem("trackingcodehistory");
var history = getStorage("trackingcodehistory");
if (history == null) {
history = [];
} else {
@ -100,7 +100,7 @@ function openTrackingHistory(code) {
while (history.length > 5) {
history.shift();
}
localStorage.setItem("trackingcodehistory", JSON.stringify(history));
setStorage("trackingcodehistory", JSON.stringify(history));
if (refresh) {
router.navigate("/toolbox/track/info", {

View File

@ -40,12 +40,12 @@ function loadWeather(reload) {
}
requestfinished = true;
if (resp.status == "OK") {
var mintemp = (localStorage.getItem("units") == "metric" ? Math.round(ftoc(resp.temp.min)) + " &deg;C" : Math.round(resp.temp.min) + " &deg;F");
var maxtemp = (localStorage.getItem("units") == "metric" ? Math.round(ftoc(resp.temp.max)) + " &deg;C" : Math.round(resp.temp.max) + " &deg;F");
var mintemp = (getStorage("units") == "metric" ? Math.round(ftoc(resp.temp.min)) + " &deg;C" : Math.round(resp.temp.min) + " &deg;F");
var maxtemp = (getStorage("units") == "metric" ? Math.round(ftoc(resp.temp.max)) + " &deg;C" : Math.round(resp.temp.max) + " &deg;F");
$("#lowtemp").html(mintemp);
$("#hightemp").html(maxtemp);
$("#precipchance").text(Math.round(resp.precipitation.chance * 100.0) + "% chance");
if (localStorage.getItem("units") == "metric") {
if (getStorage("units") == "metric") {
$("#windspeed").text(Math.round(resp.windspeed * 1.609344) + " km/h");
} else {
$("#windspeed").text(Math.round(resp.windspeed) + " mph");

View File

@ -4,6 +4,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/**
* Generate a UUID.
* From https://stackoverflow.com/a/2117523
* @returns {String}
*/
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function timestampToDateTimeString(timestamp) {
var date = new Date(timestamp * 1000);
@ -27,4 +38,21 @@ function timestampToTimeString(timestamp) {
var time = hours + ":" + (minutes < 10 ? "0" + minutes : minutes) + " " + (pm ? "PM" : "AM");
return time;
}
/**
* Get the current UNIX timestamp in seconds.
* @returns {Number}
*/
function time() {
return Date.now() / 1000;
}
/**
* Get the number of seconds between now and the given timestamp.
* @param {Number} compareto
* @returns {Number}
*/
function timeDiff(compareto) {
return time() - compareto;
}

167
www/cache.js Normal file
View File

@ -0,0 +1,167 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
var cachedurls = [
'package.json',
'settings.js',
'index.html',
'.',
'routes.js',
'cache.js',
'assets/audio/error.mp3',
'assets/audio/ok.mp3',
'assets/audio/scan.mp3',
'assets/images/icons/logo.svg',
'assets/images/barcode-dashed.svg',
'assets/images/boxes.png',
'assets/images/box-open-dashed.svg',
'assets/images/box.png',
'assets/images/check.png',
'assets/images/envelope.png',
'assets/images/envelopes.png',
'assets/images/express.png',
'assets/images/history-dashed.svg',
'assets/images/largeboxes.png',
'assets/images/largebox.png',
'assets/images/multiple-items.png',
'assets/images/note-dashed.svg',
'assets/images/signature.png',
'assets/images/signatures.png',
'assets/images/smallpackage.png',
'assets/images/smallpackages.png',
'assets/css/app.css',
'assets/css/backdrop.css',
'assets/css/oled.css',
'assets/css/web-barcode.css',
'assets/js/audio.js',
'assets/js/autofill.js',
'assets/js/list.js',
'assets/js/location.js',
'assets/js/login.js',
'assets/js/main.js',
'assets/js/manage.js',
'assets/js/map.js',
'assets/js/map_leaflet.js',
'assets/js/map_mapbox.js',
'assets/js/notes.js',
'assets/js/packages.js',
'assets/js/platform.js',
'assets/js/settings.js',
'assets/js/storage.js',
'assets/js/sync.js',
'assets/js/toolbox_scannerentries.js',
'assets/js/toolbox_scanner.js',
'assets/js/toolbox_sharelist.js',
'assets/js/toolbox_track.js',
'assets/js/toolbox_weather.js',
'assets/js/util.js',
'pages/credits.html',
'pages/home.html',
'pages/list.html',
'pages/login.html',
'pages/manage.html',
'pages/map.html',
'pages/myroute.html',
'pages/settings.html',
'pages/toolbox.html',
'pages/help/list.html',
'pages/help/map.html',
'pages/myroute/editnote.html',
'pages/toolbox/scanner.html',
'pages/toolbox/sharelist.html',
'pages/toolbox/track.html',
'pages/toolbox/trackinginfo.html',
'pages/toolbox/weather.html',
'pages/toolbox/scanner/entries.html',
'pages/toolbox/scanner/scanner.html',
'node_modules/framework7/css/framework7.bundle.min.css',
'node_modules/@fortawesome/fontawesome-free/css/all.min.css',
'node_modules/material-design-icons/iconfont/material-icons.css',
'node_modules/leaflet/dist/leaflet.css',
'node_modules/leaflet.markercluster/dist/MarkerCluster.css',
'node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css',
'node_modules/leaflet.locatecontrol/dist/L.Control.Locate.min.css',
'node_modules/mapbox-gl/dist/mapbox-gl.css',
'node_modules/framework7/js/framework7.bundle.min.js',
'node_modules/jquery/dist/jquery.min.js',
'node_modules/leaflet/dist/leaflet.js',
'node_modules/leaflet.markercluster/dist/leaflet.markercluster.js',
'node_modules/mapbox-gl/dist/mapbox-gl.js',
'node_modules/jsbarcode/dist/JsBarcode.all.min.js',
'node_modules/leaflet.locatecontrol/dist/L.Control.Locate.min.js',
'node_modules/@zxing/library/umd/index.min.js',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.ttf',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff2',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.ttf',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff',
'node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2',
'node_modules/material-design-icons/iconfont/MaterialIcons-Regular.ttf',
'node_modules/material-design-icons/iconfont/MaterialIcons-Regular.woff',
'node_modules/material-design-icons/iconfont/MaterialIcons-Regular.woff2'
];
for (i in SETTINGS.maptileurls) {
cachedurls.push(SETTINGS.maptileurls[i].json);
}
for (i in SETTINGS.alertsounds) {
cachedurls.push("assets/audio/" + SETTINGS.alertsounds[i].file);
}
for (i in SETTINGS.weathericons) {
cachedurls.push("assets/images/weather-" + SETTINGS.weathericons[i] + ".svg");
}
var cacheFilled = false;
function fillCache(cachename) {
if ('caches' in window) {
// delete old caches
caches.keys().then(function (cacheNames) {
return Promise.all(cacheNames.map(function (oldCacheName) {
if (oldCacheName != cachename) {
return caches.delete(oldCacheName);
}
}));
});
cacheFilled = true; // assume good
caches.open(cachename).then(function (cache) {
for (i in cachedurls) {
try {
cache.add(cachedurls[i]);
} catch (ex) {
cacheFilled = false;
}
}
});
// Do it again in a little while
if (!cacheFilled) {
setTimeout(function () {
fillCache(cachename);
}, 1000 * 30);
}
}
}
async function clearAllCaches() {
const keys = await caches.keys();
for (const key of keys) {
caches.delete(key);
}
}

View File

@ -5,6 +5,8 @@
<title>PackageHelper</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="assets/images/icons/logo.svg" />
<link rel="manifest" href="manifest.json">
<link rel="stylesheet" href="node_modules/framework7/css/framework7.bundle.min.css" />
<link rel="stylesheet" href="node_modules/@fortawesome/fontawesome-free/css/all.min.css" />
<link rel="stylesheet" href="node_modules/material-design-icons/iconfont/material-icons.css" />
@ -17,6 +19,7 @@
<link rel="stylesheet" href="assets/css/backdrop.css" />
<link rel="stylesheet" href="assets/css/oled.css" />
<link rel="stylesheet" href="assets/css/web-barcode.css" />
<link rel="stylesheet" href="assets/fonts/roboto/Roboto.css" />
<script src="cordova.js"></script>
@ -43,6 +46,8 @@
<script src="settings.js"></script>
<script src="assets/js/storage.js"></script>
<script src="assets/js/platform.js"></script>
<script src="assets/js/util.js"></script>
@ -55,6 +60,25 @@
<script src="assets/js/map_mapbox.js"></script>
<script src="assets/js/map.js"></script>
<script src="assets/js/manage.js"></script>
<script src="assets/js/sync.js"></script>
<script src="assets/js/notes.js"></script>
<script src="routes.js"></script>
<script src="assets/js/main.js"></script>
<script src="assets/js/main.js"></script>
<script src="cache.js"></script>
<script>
if (platform_type == "browser") {
fillCache(SETTINGS.cacheversion);
}
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js').then(function (registration) {
console.log('Service worker registration succeeded:', registration);
}, /*catch*/ function (error) {
console.log('Service worker registration failed:', error);
});
} else {
console.log('Service workers are not supported.');
}
</script>

57
www/manifest.json Normal file
View File

@ -0,0 +1,57 @@
{
"name": "PackageHelper",
"short_name": "PackageHelper",
"icons": [
{
"src": "assets/images/icons/32x32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "assets/images/icons/128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "assets/images/icons/144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "assets/images/icons/152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "assets/images/icons/192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "assets/images/icons/256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "assets/images/icons/512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"scope": "/",
"lang": "en-US",
"categories": ["navigation", "productivity", "utilities", "weather"],
"prefer_related_applications": true,
"related_applications": [
{
"platform": "play",
"url": "https://play.google.com/store/apps/details?id=com.netsyms.PackageHelper",
"id": "com.netsyms.PackageHelper"
}
],
"start_url": "index.html",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#2196f3",
"iarc_rating_id": "0e146f9e-8f83-4981-8d7d-30b857ac8db6"
}

View File

@ -1,6 +1,6 @@
{
"name": "PackageHelper",
"version": "1.3.0",
"version": "1.4.0",
"main": "index.html",
"license": "MPL-2.0",
"dependencies": {

View File

@ -2,7 +2,7 @@
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="page" data-name="manage">
<div class="page" data-name="add">
<div class="navbar">
<div class="navbar-bg"></div>
@ -14,12 +14,21 @@
</a>
</div>
<div class="title">Add Items</div>
<!--<div class="right">
<a class="link" id="addpackagebtn">
<i class="icon fas fa-plus"></i>
<span>Add</span>
<div class="right">
<a class="link popover-open" data-popover="#popover-add-options">
<i class="icon material-icons">more_vert</i>
</a>
</div>-->
</div>
</div>
</div>
<div class="popover" id="popover-add-options">
<div class="popover-inner">
<div class="list">
<ul>
<li><a class="list-button item-link popover-close" href="" onclick="toggleLettersInAddressNumber()"><label class="checkbox"><input type="checkbox" id="address-has-letters-checkbox"><i class="icon-checkbox"></i></label> Address contains letters</a></li>
</ul>
</div>
</div>
</div>
@ -37,12 +46,23 @@
<div class="list no-margin-top">
<ul>
<li class="item-divider">Address</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-title item-label">Address Number</div>
<div class="item-input-wrap">
<input type="number" name="number" placeholder="1234" autocomplete="off" autocorrect="off" autocapitalize="off">
<span class="input-clear-button"></span>
<li>
<div class="row justify-content-stretch">
<div class="col-70 item-content item-input">
<div class="item-inner">
<div class="item-title item-label">Number</div>
<div class="item-input-wrap">
<input type="number" name="number" id="housenumberinput" placeholder="1234" value="" autocomplete="off" autocorrect="off">
</div>
</div>
</div>
<div class="col-30 item-content item-input no-padding-left">
<div class="item-inner no-padding-right">
<div class="item-title item-label">Unit</div>
<div class="item-input-wrap">
<input type="text" name="unit" id="unitinput" placeholder="" value="" autocomplete="off" autocorrect="off">
</div>
</div>
</div>
</div>
</li>
@ -50,16 +70,28 @@
<div class="item-inner">
<div class="item-title item-label">Street</div>
<div class="item-input-wrap">
<input type="text" name="street" id="streetInput" placeholder="Road Drive" autocomplete="off" autocorrect="off">
<input type="text" name="street" id="streetInput" placeholder="Road Dr" autocomplete="off" autocorrect="off">
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-title item-label">City, State, ZIP</div>
<div class="item-input-wrap">
<input type="text" name="citystate" placeholder="City, ST, 12345" value="" autocomplete="off" autocorrect="off">
<li>
<div class="row justify-content-stretch">
<div class="col-70 item-content item-input">
<div class="item-inner">
<div class="item-title item-label">City, State</div>
<div class="item-input-wrap">
<input type="text" name="citystate" placeholder="City, ST" value="" autocomplete="off" autocorrect="off">
</div>
</div>
</div>
<div class="col-30 item-content item-input no-padding-left">
<div class="item-inner no-padding-right">
<div class="item-title item-label">ZIP</div>
<div class="item-input-wrap">
<input type="text" name="zipcode" placeholder="12345" value="" autocomplete="off" autocorrect="off">
</div>
</div>
</div>
</div>
</li>
@ -68,7 +100,7 @@
<li>
<label class="item-radio item-content">
<input type="radio" name="itemtype" value="{{id}}" {{#if selected}}data-default="1" checked{{/if}} />
<i class="icon icon-radio"></i>
<i class="icon icon-radio"></i>
<div class="item-inner">
<div class="item-title"><i class="{{icon}}"></i> {{name}}</div>
</div>
@ -98,6 +130,6 @@
</div>
</div>
<script src="assets/js/manage.js"></script>
<script src="assets/js/add.js"></script>
</div>

View File

@ -20,14 +20,11 @@
<div class="page-content">
<div class="block-title">Manage Items</div>
<div class="block">
Swipe <i class="fas fa-arrow-right"></i> left-to-right on a list entry
Tap or click on a list entry
to show the actions you can take.
These actions are marking the item as delivered/undelivered, or
navigating to its location with your device's default maps app.
<br />
Swipe <i class="fas fa-arrow-left"></i> right-to-left on a list entry
and tap
<span class="button button-small display-inline button-fill color-red text-color-white"><i class="material-icons">delete</i> Delete</span> to remove it.
These actions are marking the item as delivered/undelivered,
navigating to its location with your device's default maps app,
and deleting the item.
</div>
<div class="block-title">Clear the List</div>

View File

@ -30,7 +30,7 @@
<div class="list no-hairlines tablet-inset no-margin-top">
<ul>
<li>
<a href="/manage" class="item-link item-content">
<a href="/add" class="item-link item-content">
<div class="item-media"><i class="icon fas fa-truck-loading"></i></div>
<div class="item-inner">
<div class="item-title">Add Items</div>
@ -53,6 +53,14 @@
</div>
</a>
</li>
<li>
<a href="/myroute" class="item-link item-content">
<div class="item-media"><i class="icon fas fa-sticky-note"></i></div>
<div class="item-inner">
<div class="item-title">Route Notes</div>
</div>
</a>
</li>
<li>
<a href="/toolbox" class="item-link item-content">
<div class="item-media"><i class="icon fas fa-tools"></i></div>

View File

@ -49,11 +49,13 @@
<div id="no-packages-display" class="block display-none text-align-center">
<img style="width: 60%; max-width: 300px; max-height: 40vh;" src="assets/images/box-open-dashed.svg" class="margin-vertical" />
<div class="margin-top"><a href="/manage" class="button button-round button-fill margin-horizontal">Add a Package</a></div>
<div class="margin-top"><a href="/add" class="button button-round button-fill margin-horizontal">Add a Package</a></div>
</div>
<div class="list no-hairlines tablet-inset no-margin-top accordion-list" id="addresslist">
<ul>
<!-- Packages go here -->
</ul>
</div>
<ul class="list no-hairlines tablet-inset no-margin-top" id="addresslist">
<!-- Packages go here -->
</ul>
</div>

24
www/pages/login.html Normal file
View File

@ -0,0 +1,24 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="page" data-name="login">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="left">
<a href="#" class="link icon-only back">
<i class="icon icon-back"></i>
</a>
</div>
<div class="title">Log In</div>
</div>
</div>
<div class="page-content" style="overflow-y: hidden;"> <!-- overflow-y:hidden removes an annoying pointless scrollbar that wiggles a few pixels up and down -->
<iframe src="{{loginurl}}" id="loginframe" style="border: 0; width: 100%; height: calc(100vh - var(--f7-navbar-height));"></iframe>
</div>
<script src="assets/js/login.js"></script>
</div>

81
www/pages/myroute.html Normal file
View File

@ -0,0 +1,81 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="page" data-name="myroute">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="left">
<a href="#" class="link icon-only back">
<i class="icon icon-back"></i>
</a>
</div>
<div class="title">Route Notes</div>
<div class="right">
<a class="link icon-only searchbar-enable" data-searchbar=".notes-list-searchbar">
<i class="icon material-icons">search</i>
</a>
</div>
<form class="searchbar searchbar-expandable notes-list-searchbar">
<div class="searchbar-inner">
<div class="searchbar-input-wrap">
<input type="search" placeholder="Search"/>
<i class="searchbar-icon"></i>
<span class="input-clear-button"></span>
</div>
<span class="searchbar-disable-button">Cancel</span>
</div>
</form>
</div>
</div>
<div class="fab fab-right-bottom">
<a href="/myroute/addnote">
<i class="icon material-icons">add</i>
</a>
</div>
<div class="page-content page-content-fab-pad">
<div class="block-title">Notes</div>
{{#if notes}}
<div class="list accordion-list" id="noteslist">
<ul>
{{#each notes}}
<li class="accordion-item route-note" data-noteid="{{id}}">
<a href="#" class="item-content item-link">
<div class="item-inner">
<div class="item-title">{{number}} {{street}}</div>
</div>
</a>
<div class="accordion-item-content">
<div class="block">
<a class="button button-small button-outline editnotebtn display-inline-block" data-noteid="{{id}}" href="#"><i class="fas fa-pencil-alt fa-fw"></i> Edit</a>
<a class="button button-small button-outline deletenotebtn display-inline-block color-red" data-noteid="{{id}}" href="#"><i class="fas fa-trash fa-fw"></i> Delete</a>
{{#if notes}}
<p>{{newlinestobr notes}}</p>
{{/if}}
<div class="margin-vertical-half">
<ul>
{{#each toggles}}
{{#if this}}
<li><i class="{{notetoggleicon @key}}"></i> {{notetogglename @key}}</li>
{{/if}}
{{/each}}
</ul>
</div>
</div>
</div>
</li>
{{/each}}
</ul>
</div>
{{else}}
<div class="block text-align-center">
<img style="width: 60%; max-width: 300px; max-height: 40vh;" src="assets/images/note-dashed.svg" class="margin-vertical" />
<div class="margin-top">Press <i class="material-icons">add</i> to add a note.</div>
</div>
{{/if}}
</div>
</div>

View File

@ -0,0 +1,99 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="page" data-name="editnote">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="left">
<a href="#" class="link icon-only" onclick="router.back({force: true, ignoreCache: true})">
<i class="icon icon-back"></i>
</a>
</div>
<div class="title">{{title}}</div>
</div>
</div>
<div class="fab fab-extended fab-right-bottom">
<a id="savenotebtn" data-noteid="{{noteid}}">
<i class="icon material-icons">save</i>
<div class="fab-text">Save</div>
</a>
</div>
<div class="page-content page-content-fab-pad">
<div class="list no-margin-top no-hairlines">
<ul>
<li class="item-divider">Address</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-title item-label">Number</div>
<div class="item-input-wrap">
<input type="number" value="{{note.number}}" name="number" id="notenumberinput" placeholder="1234" autocomplete="off" autocorrect="off" autocapitalize="off">
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-title item-label">Street</div>
<div class="item-input-wrap">
<input type="text" value="{{note.street}}" name="street" id="notestreetinput" placeholder="Road Dr" autocomplete="off" autocorrect="off">
<span class="input-clear-button"></span>
</div>
</div>
</li>
<li>
<div class="row justify-content-stretch">
<div class="col-50 item-content item-input">
<div class="item-inner">
<div class="item-title item-label">ZIP</div>
<div class="item-input-wrap">
<input type="number" value="{{note.zipcode}}" name="zipcode" id="zipcode" placeholder="12345" value="" autocomplete="off" autocorrect="off">
</div>
</div>
</div>
<div class="col-50 item-content item-input no-padding-left">
<div class="item-inner no-padding-right">
<div class="item-title item-label">Route</div>
<div class="item-input-wrap">
<input type="text" value="{{note.route}}" name="route" id="route" placeholder="C999" value="" autocomplete="off" autocorrect="off" maxlength="4" pattern="^[CRHG][0-9]{3}$" >
</div>
</div>
</div>
</div>
</li>
<li class="item-divider">Options</li>
{{#each toggles}}
<li class="item-content">
<div class="item-inner">
<div class="item-title">
{{name}}
</div>
<div class="item-after">
<label class="toggle toggle-init">
<input class="note-toggle" type="checkbox" data-id="{{id}}" {{#if checked}}checked="checked"{{/if}}>
<span class="toggle-icon"></span>
</label>
</div>
</div>
</li>
{{/each}}
<li class="item-divider">Notes</li>
<li class="item-content item-input">
<div class="item-inner">
<div class="item-input-wrap">
<textarea id="notes">{{note.notes}}</textarea>
<span class="input-clear-button"></span>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>

View File

@ -30,9 +30,9 @@ var routes = [
}
},
{
path: '/manage',
name: 'manage',
templateUrl: './pages/manage.html',
path: '/add',
name: 'add',
templateUrl: './pages/add.html',
options: {
context: {
show_help: show_help,
@ -41,21 +41,7 @@ var routes = [
},
on: {
pageAfterIn: function () {
app.autocomplete.create({
inputEl: '#streetInput',
openIn: 'dropdown',
/* If we set valueProperty to "id" then input value on select will be set according to this property */
valueProperty: 'name', //object's "value" property name
textProperty: 'name', //object's "text" property name
limit: 10, //limit to 10 results
typeahead: true,
dropdownPlaceholderText: '',
source: function (query, render) {
var streets = searchAutofill(query, $("input[name=number]").val());
render(streets);
}
});
setupStreetAutofill("#streetInput", "input[name=number]");
}
}
},
@ -71,7 +57,6 @@ var routes = [
on: {
pageAfterIn: function () {
loadPackageList();
searchbar = app.searchbar.create({
el: '.package-list-searchbar',
searchContainer: '#addresslist',
@ -101,6 +86,97 @@ var routes = [
}
}
},
{
path: '/myroute',
name: 'myroute',
async: function (routeTo, routeFrom, resolve, reject) {
var notes = false;
if (inStorage("notes")) {
notes = JSON.parse(getStorage("notes"));
if (notes.length == 0) {
notes = false;
}
}
resolve({
templateUrl: './pages/myroute.html'
}, {
context: {
notes: notes
}
});
},
on: {
pageAfterIn: function () {
notessearchbar = app.searchbar.create({
el: '.notes-list-searchbar',
searchContainer: '#noteslist',
searchIn: '.item-title',
backdrop: false,
on: {
search(sb, query, previousQuery) {
console.log(query, previousQuery);
}
}
});
}
},
routes: [
{
path: '/addnote',
on: {
pageAfterIn: function () {
setupStreetAutofill("input[name=street]", "input[name=number]");
}
},
async: function (routeTo, routeFrom, resolve, reject) {
var uuid = uuidv4();
resolve({
templateUrl: './pages/myroute/editnote.html'
}, {
context: {
noteid: uuid,
title: "Add Note",
toggles: SETTINGS.routenotetoggles,
note: {
id: uuid,
number: "",
street: "",
zipcode: inStorage("zipcode") ? getStorage("zipcode") : "",
route: inStorage("lastrouteid") ? getStorage("lastrouteid") : "",
notes: "",
toggles: {}
}
}
});
},
},
{
path: '/editnote',
templateUrl: './pages/myroute/editnote.html',
on: {
pageAfterIn: function () {
setupStreetAutofill("input[name=street]", "input[name=number]");
}
},
options: {
context: {
title: "Edit Note",
toggles: SETTINGS.routenotetoggles
}
}
}
]
},
{
path: '/login',
templateUrl: './pages/login.html',
name: 'login',
options: {
context: {
loginurl: SETTINGS.loginurl
}
}
},
{
path: '/toolbox',
url: './pages/toolbox.html',
@ -121,7 +197,6 @@ var routes = [
async: function (routeTo, routeFrom, resolve, reject) {
if (localStorage.getItem("scanevents") != null && localStorage.getItem("scanevents") != "[]") {
var entries = JSON.parse(localStorage.getItem("scanevents"));
for (i in entries) {
entries[i].event = entries[i].event.join(' <i class="fas fa-chevron-right"></i> ');
}
@ -222,23 +297,62 @@ var routes = [
path: '/settings',
name: 'settings',
async: function (routeTo, routeFrom, resolve, reject) {
var settings = [
{
setting: "alerts",
title: "Package Alerts",
text: "Change the alert sound, volume, and distance.",
onclick: "router.navigate('/settings/alerts')",
link: true
},
{
setting: "maps",
title: "Map and Navigation",
text: "Change map settings and units.",
onclick: "router.navigate('/settings/maps')",
link: true
var settings = [];
if (localStorage.getItem("username") != null && localStorage.getItem("password") != null) {
var lastsync = localStorage.getItem("lastsync");
if (lastsync == null) {
lastsync = "never";
} else {
lastsync = timestampToDateTimeString(lastsync);
}
];
settings.push(
{
setting: "account",
title: "Account",
text: "Logged in as " + localStorage.getItem("username") + "<br>" + "Last sync: " + lastsync
},
{
setting: "syncnow",
title: "",
text: "Sync now",
link: true,
onclick: "resyncAndRestart()"
},
{
setting: "logout",
title: "",
text: "Log out",
link: true,
onclick: "logout()"
},
);
} else {
settings.push(
{
setting: "login",
title: "Account",
text: "Log in to backup and sync your settings and data.",
onclick: "router.navigate('/login')",
link: true
}
);
}
settings.push(
{
setting: "alerts",
title: "Package Alerts",
text: "Change the alert sound, volume, and distance.",
onclick: "router.navigate('/settings/alerts')",
link: true
},
{
setting: "maps",
title: "Map and Navigation",
text: "Change map settings and units.",
onclick: "router.navigate('/settings/maps')",
link: true
}
);
if (platform_type == "cordova" && cordova.platformId != "browser") {
settings.push({
setting: "wakelock",
@ -286,8 +400,14 @@ var routes = [
text: "",
onclick: "openBrowser('https://netsyms.com/legal?pk_campaign=PackageHelperApp')",
link: true
},
{
setting: "clearcache",
title: "Clear Cache",
text: "Delete saved maps and other temporary data",
link: true,
onclick: "clearCaches()"
});
resolve({
templateUrl: './pages/settings.html'
}, {
@ -349,7 +469,6 @@ var routes = [
slider: true
}
];
resolve({
templateUrl: './pages/settings.html'
}, {
@ -422,6 +541,14 @@ var routes = [
}
]
},
{
setting: "mapscale",
title: "Map Scale Ruler",
text: "Show a scale in the corner of the map.",
toggle: true,
checked: localStorage.getItem("mapscale") !== "false",
onclick: ""
},
{
setting: "maptype",
title: "Alternative map",
@ -431,7 +558,6 @@ var routes = [
onclick: ""
}
];
resolve({
templateUrl: './pages/settings.html'
}, {

View File

@ -5,6 +5,7 @@
*/
var SETTINGS = {
cacheversion: "v1.4.0_5",
maptileurls: {
liberty: {
url: "https://maps.netsyms.net/styles/osm-liberty/{z}/{x}/{y}.png",
@ -49,6 +50,15 @@ var SETTINGS = {
file: "alert.sonar.mp3"
}
},
/*
* id = key: internal type ID
* name: human-readable type label
* icon: CSS classes for displaying an icon with <i class="[icon]"></i>
* mapicon: assets/images/[mapicon].png
* pluralmapicon: same as mapicon but when there's more than one of the type
* selected: true for the default type, not present for other entries
* allowedlocationtypes: Pipe-seperated list of location types this item can be delivered to. Should include "address" somewhere to keep the server happy.
*/
itemtypes: {
package: {
id: "package",
@ -56,42 +66,48 @@ var SETTINGS = {
icon: "fas fa-box fa-fw",
mapicon: "box",
pluralmapicon: "boxes",
selected: true
selected: true,
allowedlocationtypes: "locker|address"
},
large: {
id: "large",
name: "Large Package",
icon: "fas fa-pallet fa-fw",
mapicon: "largebox",
pluralmapicon: "largeboxes"
pluralmapicon: "largeboxes",
allowedlocationtypes: "address"
},
small: {
id: "small",
name: "Small Package",
icon: "fas fa-envelope-square fa-fw",
mapicon: "smallpackage",
pluralmapicon: "smallpackages"
pluralmapicon: "smallpackages",
allowedlocationtypes: "mailbox|locker|address"
},
letter: {
id: "letter",
name: "Letter",
icon: "fas fa-envelope fa-fw",
mapicon: "envelope",
pluralmapicon: "envelopes"
pluralmapicon: "envelopes",
allowedlocationtypes: "mailbox|address"
},
signature: {
id: "signature",
name: "Signature Item",
icon: "fas fa-file-signature fa-fw",
mapicon: "signature",
pluralmapicon: "signatures"
pluralmapicon: "signatures",
allowedlocationtypes: "address"
},
express: {
id: "express",
name: "Express Item",
icon: "fas fa-shipping-fast fa-fw",
mapicon: "express",
pluralmapicon: "multiple-items"
pluralmapicon: "multiple-items",
allowedlocationtypes: "address"
}
},
weathericons: [
@ -301,10 +317,48 @@ var SETTINGS = {
]
}
],
routenotetoggles: [
{
name: "Vacant",
id: "vacant",
preventsDelivery: true,
reason: "is vacant",
icon: "far fa-circle"
},
{
name: "No Mail Receptacle",
id: "nmr",
preventsDelivery: true,
reason: "has no mailbox",
icon: "far fa-times-circle"
},
{
name: "Outside delivery radius",
id: "undeliverable",
preventsDelivery: true,
reason: "is too far from the route",
icon: "fas fa-route"
},
{
name: "On hold",
id: "hold",
preventsDelivery: true,
reason: "is on hold",
icon: "fas fa-pause-circle"
}
],
synckeyblacklist: [
"username", "password", "trackingcodehistory", "packages",
"user_latitude", "user_longitude", "geocode_cache", "scanevents",
"lastsync", "lastchange"
],
geocodecacheexpiry: 604800, // One week
geocodeapi: "https://apis.netsyms.net/packagehelper/geocode.php",
trackingapi: "https://apis.netsyms.net/packagehelper/track.php",
weatherapi: "https://apis.netsyms.net/packagehelper/weather.php",
geoipapi: "https://apis.netsyms.net/packagehelper/geoip.php",
sharelistapi: "https://apis.netsyms.net/packagehelper/sharepackagelist.php"
sharelistapi: "https://apis.netsyms.net/packagehelper/sharepackagelist.php",
loginurl: "https://apis.netsyms.net/packagehelper/login/",
syncapi: "https://apis.netsyms.net/packagehelper/sync.php",
mapfixapi: "https://apis.netsyms.net/packagehelper/contribgeocode.php",
}

39
www/sw.js Normal file
View File

@ -0,0 +1,39 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
var cachename = "v1.4.0_5";
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((resp) => {
return resp || fetch(event.request).then((response) => {
return caches.open(cachename).then((cache) => {
try {
// only cache map data
if (event.request.url.includes("maps.netsyms.net")) {
cache.put(event.request, response.clone());
}
} catch (ex) {
}
return response;
});
});
})
);
});
//
//self.addEventListener('install', function (event) {
// event.waitUntil(
// caches.open(cachename).then((cache) => {
// return cache.addAll([
// "https://maps.netsyms.net/styles/osm-liberty/style.json",
// "https://maps.netsyms.net/styles/klokantech-terrain/style.json",
// "https://maps.netsyms.net/styles/fiord-color/style.json",
// "https://maps.netsyms.net/styles/oled-black/style.json"
// ]);
// }));
//});