Compare commits

...

43 Commits

Author SHA1 Message Date
424c6b3987 Update copyright year 2020-01-01 20:31:12 -07:00
4d9b4abd19 Add geocode cache (close #30) 2020-01-01 20:29:58 -07:00
771fd271c2 Move mapbox-gl controls to the left side of the screen 2020-01-01 20:08:05 -07:00
e1ded04c9b Autofill: number-only results are now subject to user input filtering 2020-01-01 20:06:38 -07:00
261b797a85 Send separated address parts to the geocoding server 2019-12-31 01:16:00 -07:00
bbdf21f8a2 Broken scanner tool: detect signature items and warn user 2019-12-29 22:01:29 -07:00
7c25b8c3b3 Fix max zoom bug 2019-12-29 20:42:53 -07:00
acd0c86447 Fix bug with receiving shared item list 2019-12-29 20:42:35 -07:00
5f28ee8a7e Fix icon sizing 2019-12-29 19:55:43 -07:00
d5e6d7eaee Improve map help text 2019-12-29 19:48:11 -07:00
7d39c12fce Add help menus (close #29) 2019-12-29 19:36:00 -07:00
a0cbb0dbe5 Remove extra console logging and improve what's left behind, fix map reload errors 2019-12-29 19:10:45 -07:00
91e31c449e Add setting to change the map zoom level when location is being tracked 2019-12-29 18:03:01 -07:00
b50524903d Make location dot "transparent" to taps/clicks 2019-12-29 17:28:40 -07:00
1f942ecafe Remove redundant alertsettings.html, move map settings to sub-page 2019-12-29 17:17:45 -07:00
5306f25a97 Increase package grouping fuzziness by rounding to 4 decimal places 2019-12-29 16:57:10 -07:00
78f9c5f31f Fix build errors, reduce APK size 2019-12-29 16:39:11 -07:00
c69b8933ab Fix build error 2019-12-25 21:14:27 -07:00
00600ffee9 Fix npm error 2019-12-25 21:00:40 -07:00
1796e45f92 Fix npm error 2019-12-25 20:54:26 -07:00
5d9944a9c4 Run scripts with bash 2019-12-22 22:15:31 -07:00
b25a292f34 Cleanup script broken 2019-12-22 22:07:56 -07:00
13021af8ca Fix package.json and add more to remove_bloat.sh 2019-12-22 21:56:28 -07:00
1c8c7cc2e7 Add mapbox-gl-js map client 2019-12-22 21:07:45 -07:00
90aeb804ae Minor text changes 2019-12-22 17:52:51 -07:00
31dee00cb1 Group multiple addresses with identical coordinates 2019-12-22 17:50:46 -07:00
8514582bcb Add help panel to list page 2019-12-20 00:50:08 -07:00
403708572e Cordova: Close sheet modal when back button pressed 2019-12-18 11:17:17 -07:00
09b51d95f0 Reverse order of tracking history so most recent is on top 2019-12-06 15:28:53 -07:00
faa13a1588 Bump Framework7 version 5.0.2 -> 5.1.3 2019-12-06 15:28:31 -07:00
f3f3653d2c Don't insert duplicate codes in tracking history 2019-11-30 19:43:10 -07:00
976f44a4f8 Add history (last five codes) and better help text to tracking page 2019-11-30 19:29:58 -07:00
008ab56ce8 Handle pressing Enter in tracking code box 2019-11-30 19:05:47 -07:00
4c24d8fba8 Remove some papercuts from broken scanner feature 2019-11-30 18:59:23 -07:00
9f5caa2593 Add list sharing feature (no sync, simply transfer+merge). 2019-11-30 18:44:08 -07:00
c4900d2505 Bump app version from 1.2.0 to 1.3.0 2019-11-30 17:27:40 -07:00
efd39a2349 Save packages list to localStorage after deadline alarm triggers 2019-11-30 17:26:55 -07:00
df06534d72 Add OLED Black map theme 2019-11-30 17:25:42 -07:00
c1f74e43fe Add delivery alarm for express packages 2019-11-30 16:40:44 -07:00
384dac7b85 Fix color bugs with scanner and dark theme 2019-11-27 17:11:04 -07:00
d0c3988566 Add tool for scanning items delivered (close #14) 2019-11-27 02:53:01 -07:00
70fa409015 Fix #27 2019-11-26 22:22:30 -07:00
3602f77343 Make barcode scanner work on Cordova browser platform (close #28) 2019-11-26 21:45:46 -07:00
42 changed files with 4431 additions and 525 deletions

View File

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.netsyms.PackageHelper" version="1.2.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<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">
<name>PackageHelper</name>
<description>
Assistant app for door-to-door package delivery.
@ -32,7 +32,5 @@
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
</platform>
<hook src="scripts/www_npm_install.sh" type="before_prepare" />
<hook src="scripts/generate_credits.sh" type="before_prepare" />
<hook src="scripts/remove_bloat.sh" type="before_prepare" />
<hook src="scripts/npm_prepare.sh" type="before_prepare" />
</widget>

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "com.netsyms.packagehelper",
"displayName": "PackageHelper",
"version": "1.2.0",
"version": "1.3.0",
"description": "Assistant app for door-to-door package delivery.",
"main": "www/index.html",
"scripts": {
@ -18,7 +18,8 @@
"cordova-plugin-inappbrowser": {},
"cordova-plugin-powermanagement-netsyms": {},
"cordova-plugin-whitelist": {},
"phonegap-plugin-barcodescanner": {}
"phonegap-plugin-barcodescanner": {},
"cordova-plugin-device": {}
},
"platforms": [
"android",
@ -28,6 +29,7 @@
"dependencies": {
"cordova-android": "^8.1.0",
"cordova-browser": "^6.0.0",
"cordova-plugin-device": "^2.0.3",
"cordova-plugin-inappbrowser": "^3.1.0",
"cordova-plugin-powermanagement-netsyms": "git+https://source.netsyms.com/Netsyms/cordova-plugin-powermanagement",
"cordova-plugin-whitelist": "^1.3.4"

View File

@ -1,3 +1,4 @@
#!/bin/bash
cd www
yarn licenses generate-disclaimer > ../license-credits.md
cd ..

5
scripts/npm_prepare.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
./scripts/www_npm_install.sh
./scripts/generate_credits.sh
./scripts/remove_bloat.sh

View File

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# This script removes some stuff in `www/node_modules` that the app doesn't need to run.
# It removes about 6MB from the build size.
@ -7,60 +7,80 @@ echo "Removing bloat in node_modules..."
pwd
cd www/node_modules
rm -rf dom7
rm -rf path-to-regexp
rm -rf ssr-window
rm -rf template7
rm -rf {ansicolors,buffer-from,cardinal,concat-stream,core-util-is,csscolorparser}
rm -rf {dom7,earcut,esprima,geojson-vt,gl-matrix,grid-index,ieee754,inherits,isarray}
rm -rf {kdbush,leaflet-geometryutil,@mapbox,minimist,murmurhash-js}
rm -rf {path-to-regexp,pbf,potpack,process-nextick-args}
rm -rf {protocol-buffers-schema,quickselect,readable-stream,redeyed,resolve-protobuf-schema}
rm -rf {rw,safe-buffer,sharkdown,split,ssr-window,string_decoder,supercluster}
rm -rf {template7,text-encoding,through,tinyqueue,ts-custom-error,typedarray}
rm -rf {util-deprecate,vt-pbf,wgs84}
# Make npm stop complaining that these don't exist by actually removing them
rm -rf .bin/*
cd @fortawesome/fontawesome-free
rm -rf js
rm -rf less
rm -rf scss
rm -rf sprites
rm -rf svgs
rm -rf {js,less,metadata,scss,sprites,svgs}
find css -type f -not -name 'all.min.css' -delete
cd ../..
cd jquery
rm -rf src
rm -rf external
cd ..
cd jsbarcode
rm -rf .dockerignore .eslintignore .eslintrcautomation bower.json CONTRIBUTING.md docker-compose.yml Dockerfile example gulpfile.js jsbarcode.d.ts README.md src test .travis.yml
rm -rf dist/barcodes
rm -rf dist/JsBarcode.all.js
cd framework7
rm -rf components
rm -rf lazy-components
rm -rf less
rm -rf modules
rm -rf utils
rm -f framework7.*
rm -f framework7-lite.*
find css -type f -not -name 'framework7.bundle.min.css' -delete
find js -type f -not -name 'framework7.bundle.min.js' -delete
cd ..
cd jquery
rm -rf src
rm -rf external
find dist -type f -not -name 'jquery.min.js' -delete
cd ..
cd jsbarcode
rm -rf .dockerignore .eslintignore .eslintrcautomation bower.json CONTRIBUTING.md docker-compose.yml Dockerfile example gulpfile.js jsbarcode.d.ts README.md src test .travis.yml
rm -rf {automation,bin}
rm -rf dist/barcodes
rm -rf dist/JsBarcode.all.js
cd ..
cd leaflet
rm -rf {CHANGELOG.md,src}
find dist -type f -not -name 'leaflet.css' -not -name 'leaflet.js' -delete
cd ..
cd leaflet.locatecontrol
rm -rf {CHANGELOG.md,README.md,src}
find dist -type f -not -name 'L.Control.Locate.min.css' -not -name 'L.Control.Locate.min.js' -delete
cd ..
cd leaflet.markercluster
rm -rf {build,example,spec,src,CHANGELOG.md}
find dist -type f -not -name 'leaflet.markercluster.js' -not -name 'MarkerCluster.css' -not -name 'MarkerCluster.Default.css' -delete
cd ..
cd material-design-icons
rm -rf action
rm -rf alert
rm -rf av
rm -rf bower.json
rm -rf communication
rm -rf content
rm -rf device
rm -rf editor
rm -rf file
rm -rf gulpfile.babel.js
rm -rf hardware
rm -rf image
rm -rf index.js
rm -rf maps
rm -rf navigation
rm -rf notification
rm -rf package.json
rm -rf places
rm -rf README.md
rm -rf social
rm -rf sprites
rm -rf toggle
# Remove everything except the icon font and license, but also exclude the
# current and parent folder (. , ..) so rm won't complain about refusing
# to delete the whole filesystem
find . -maxdepth 1 -not -name '.' -not -name 'LICENSE' -not -name 'iconfont' -exec rm -rf {} \;
cd ..
cd mapbox-gl
rm -rf {build,flow-typed,src}
find dist -type f -not -name 'mapbox-gl.css' -not -name 'mapbox-gl.js' -delete
rm -rf dist/style-spec
cd ..
cd @zxing/library
rm -rf {esm,esm5}
rm -f umd/index.min.js.map
cd ../..
echo "Cleanup finished"

View File

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
cd www
npm install

View File

@ -18,3 +18,32 @@ Framework7 and FontAwesome both have a .fab class
font-size: var(--f7-font-size);
line-height: var(--f7-line-height);
}
/*
* Material icons are too big and disrupt the flow of text
*/
.block .material-icons {
font-size: var(--f7-block-font-size);
}
.material-icons.material-icons-24px {
font-size: 24px;
}
.material-icons-intext .material-icons {
font-size: var(--f7-block-font-size);
}
#mapbox .package-marker {
width: 25px;
height: 25px;
background-image: url(../images/box.png);
background-size: contain;
}
/* Allow tapping/clicking on package markers
* when the location dot is overlapping them
*/
#mapbox .mapboxgl-user-location-dot {
pointer-events: none;
}

View File

@ -0,0 +1,60 @@
<?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-history fa-w-16"
aria-hidden="true"
data-icon="history"
data-prefix="far"
focusable="false"
role="img"
version="1.1"
viewBox="0 0 512 512"
id="svg877"
sodipodi:docname="barcode-dashed.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata883">
<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>
<defs
id="defs881" />
<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="namedview879"
showgrid="false"
inkscape:zoom="1.4980469"
inkscape:cx="256"
inkscape:cy="256"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg877" />
<path
id="path952"
d="M 25.36636,428.97523 V 83.02477 h 16.216428 v 345.95046 z m 24.195811,-0.24595 V 83.02477 h 8.237044 v 345.70451 z m 24.453472,0 V 83.02477 h 7.979383 v 345.70451 z m 40.412237,0 V 83.02477 h 7.97938 v 345.70451 z m 32.43286,0 V 83.02477 h 15.95876 v 345.70451 z m 40.41224,0 V 83.02477 h 7.97938 v 345.70451 z m 16.21642,0 V 83.02477 h 7.97939 v 345.70451 z m 16.21643,0 V 83.02477 h 7.97938 v 345.70451 z m 32.1761,0 V 83.02477 h 16.21642 v 345.70451 z m 40.41224,0 V 83.02477 h 16.21642 v 345.70451 z m 32.43195,0 V 83.02477 h 16.21733 v 345.70451 z m 32.43376,0 V 83.02477 h 16.21732 v 345.70451 z m 24.19581,0 V 83.02477 h 16.21642 v 345.70451 z m 40.6699,0 V 83.02477 h 24.19581 v 345.70451 z m 32.17519,0 V 83.02477 h 8.23705 v 345.70451 z m 16.21643,0.24595 V 83.02477 h 16.21643 v 345.95046 z"
inkscape:connector-curvature="0"
style="fill:#9e9e9e;fill-opacity:1;stroke:none;stroke-width:10.00063038;stroke-miterlimit:4;stroke-dasharray:5.00031521, 5.00031521;stroke-dashoffset:0;stroke-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -76,7 +76,7 @@ function addAutofillEntry(address) {
function searchAutofill(q, number) {
var byNumber = [];
if (typeof number != 'undefined') {
byNumber = searchAutofillByNumber(number);
byNumber = searchAutofillByNumber(number, q);
}
var byStreet = [];
@ -87,7 +87,7 @@ function searchAutofill(q, number) {
return byNumber.concat(byStreet.filter((item) => byNumber.indexOf(item) < 0));
}
function searchAutofillByNumber(number) {
function searchAutofillByNumber(number, q) {
if (typeof autofillDB[number] == 'undefined') {
return [];
}
@ -96,11 +96,19 @@ function searchAutofillByNumber(number) {
return b[1] - a[1];
});
var query = false;
if (typeof q != 'undefined' && q != "") {
query = true;
}
var streets = [];
for (var i = 0; i < sorted.length; i++) {
// if there's no search query OR if the query matches the current item
if (!query || (query && sorted[i][0].toLowerCase().includes(q))) {
streets.push(sorted[i][0]);
}
}
return streets;
}
@ -112,12 +120,12 @@ function searchAutofillByStreet(q) {
return b[1] - a[1];
});
console.log(sortedDB);
//console.log(sortedDB);
q = q.toLowerCase();
for (var i = 0; i < sortedDB.length; i++) {
console.log(sortedDB[i][0].toLowerCase().indexOf(q));
//console.log(sortedDB[i][0].toLowerCase().indexOf(q));
if (sortedDB[i][0].toLowerCase().includes(q)) {
streets.push(sortedDB[i][0]);
}

View File

@ -16,7 +16,7 @@ $(".view-main").on("click", "#addresslist .package-list-item .directions-btn", f
});
$(".view-main").on("swipeout:delete", "#addresslist .package-list-item", function () {
console.log("Deleting package", $(this).data("packageid"));
console.log("Info", "Deleting package", $(this).data("packageid"));
deletePackage($(this).data("packageid"));
});
@ -62,8 +62,8 @@ function loadPackageList(sortType) {
var anum = parseInt(a.value.address.split(" ", 1)[0], 10);
var bnum = parseInt(b.value.address.split(" ", 1)[0], 10);
console.log("aalpha", aalpha);
console.log("balpha", balpha);
//console.log("aalpha", aalpha);
//console.log("balpha", balpha);
switch (sortType) {
case "alpha_desc":
if (aalpha > balpha) {

View File

@ -45,14 +45,10 @@ if ("geolocation" in navigator) {
localStorage.setItem("user_latitude", userPosition.coords.latitude);
localStorage.setItem("user_longitude", userPosition.coords.longitude);
if (mapLocationControlStarted) {
//setMapLocation(position.coords.latitude, position.coords.longitude);
// 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);
if (map != null) {
//map.updatePackageLayer(packages);
}
var alertinterval = localStorage.getItem("alertinterval");
if (alertinterval == null) {
@ -89,7 +85,7 @@ if ("geolocation" in navigator) {
}
} else {
if (map != null) {
map.locateControl.start();
map.startLocateControl();
mapLocationControlStarted = true;
}
}
@ -104,7 +100,7 @@ if ("geolocation" in navigator) {
});
} else {
geoerrorcount++;
console.log("Geolocation error #" + geoerrorcount + ": ", error);
console.log("Warn", "Geolocation error #" + geoerrorcount + ": ", error);
// Stop showing error toasts if they're happening a lot
if (geoerrorcount <= 3) {
app.toast.show({

View File

@ -63,10 +63,7 @@ router.on("pageInit", function (pagedata) {
});
router.on("routeChange", function (newRoute) {
console.log(newRoute);
if (newRoute == "home") {
router.refreshPage();
}
console.log("Info", "Navigating to ", newRoute.path);
});
// Set alert radius to 100 meters by default

View File

@ -43,7 +43,12 @@ $("#addpackagebtn").click(function () {
var address = ($("input[name=number]").val() + " " + $("input[name=street]").val()).toUpperCase();
$("#no-history").addClass("display-none");
addPackageByAddress(address, $("input[name=citystate]").val().toUpperCase(), $("input[name=itemtype]:checked").val(), function (ids) {
addPackageByAddress(
$("input[name=number]").val().toUpperCase(),
$("input[name=street]").val().toUpperCase(),
$("input[name=citystate]").val().toUpperCase(),
$("input[name=itemtype]:checked").val(),
function (ids) {
var packageObj = getPackage(ids.packageID);
// Reset item type to default
$("input[name=itemtype][data-default=1]").prop("checked", true);
@ -66,9 +71,9 @@ $("#addpackagebtn").click(function () {
$(".view-main").off("click", "#historylist .history-list-item");
$(".view-main").on("click", "#historylist .history-list-item", function () {
console.log("Asking to delete ", $(this).data("package"));
console.log("Info", "Asking to delete ", $(this).data("package"));
confirmDeletePackage(getPackage($(this).data("package")), function (id) {
console.log("Removing history item", id);
console.log("Info", "Removing history item", id);
$('#historylist .history-list-item[data-package="' + id + '"]').remove();
if ($('#historylist .history-list-item').length == 0) {
$("#no-history").removeClass("display-none");

View File

@ -6,8 +6,24 @@
var map = null;
var maptype = "mapbox";
function createMap() {
if (localStorage.getItem("maptype") == null) {
localStorage.setItem("maptype", "mapbox");
}
maptype = localStorage.getItem("maptype");
if (maptype == "mapbox") {
if (mapboxgl.supported()) {
map = mapboxMap();
} else {
console.log("Warn", "mapbox-gl not supported, falling back to Leaflet");
maptype = "leaflet";
map = leafletMap();
}
} else {
map = leafletMap();
}
map.updatePackageLayer(packages);
}
@ -20,16 +36,37 @@ function reloadMap() {
if (map != null && typeof map != 'undefined') {
var mapcenter = map.getCenter();
var mapzoom = map.getZoom();
if (map.maptype == "mapbox") {
var mapbearing = map.getBearing();
var mappitch = map.getPitch();
}
map.off();
map.remove();
map = null;
if (document.getElementById("mapbox") != null) {
createMap();
if (map.maptype == "mapbox") {
map.jumpTo({
center: mapcenter,
zoom: mapzoom,
bearing: mapbearing,
pitch: mappitch
});
} else {
map.setView(mapcenter, mapzoom);
}
} else {
console.log("Info", "Not re-creating map because #mapbox is not in DOM. Creation will be automatically triggered when map page is loaded.");
}
} else {
createMap();
}
} catch (ex) {
// oh well ¯\(°_o)/¯
console.log(ex);
}
}
@ -48,12 +85,12 @@ function openPackageInfoSheet(coordid, refreshOnly) {
refreshOnly = false;
}
console.log("Packages array: ", packages);
//console.log("Packages array: ", packages);
for (var i = 0; i < packages.length; i++) {
if (packages[i].id == coordid) {
package = packages[i];
console.log("Single Address:", package);
//console.log("Single Address:", package);
$("#package-info-get-directions").attr("href", "geo:" + package.coords[0] + "," + package.coords[1]);
$("#package-info-sheet-inner .list ul").html("");

View File

@ -14,6 +14,8 @@ function leafletMap() {
attributionControl: false
});
map.maptype = "leaflet";
if (localStorage.getItem("mapsource") == null) {
localStorage.setItem("mapsource", "liberty");
}
@ -30,7 +32,7 @@ function leafletMap() {
showPopup: false,
locateOptions: {
enableHighAccuracy: true,
maxZoom: 16
maxZoom: localStorage.getItem("trackzoom") == null ? 16 : localStorage.getItem("trackzoom") * 1
},
setView: "untilPanOrZoom",
icon: "far fa-compass",
@ -43,6 +45,14 @@ function leafletMap() {
map.setView({lat: userPosition.coords.latitude, lng: userPosition.coords.longitude}, 2);
map.startLocateControl = function () {
map.locateControl.start();
}
map.stopLocateControl = function () {
}
map.setMapHeading = function (heading) {
}
@ -57,7 +67,7 @@ function leafletMap() {
map.updatePackageLayer = function (data) {
map.packagelayer.clearLayers();
console.log(data);
//console.log(data);
for (var i = 0; i < data.length; i++) {
// JavaScript variable scope and anonymous functions are dumb
@ -65,7 +75,7 @@ function leafletMap() {
// of the loop, or something like that
(function (datai) {
var iconName = getMapIconForItems(datai.items);
console.log(iconName);
//console.log(iconName);
var icon = L.icon({
iconUrl: "assets/images/" + iconName + ".png",

134
www/assets/js/map_mapbox.js Normal file
View File

@ -0,0 +1,134 @@
/*
* 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 true, we'll do a fancy zoom/pan in
// Otherwise we'll just jump to the correct location
var firstload = true;
function mapboxMap() {
if (localStorage.getItem("mapsource") == null) {
localStorage.setItem("mapsource", "liberty");
}
$("#mapbox").css("background-color", SETTINGS.maptileurls[localStorage.getItem("mapsource")].bgcolor);
mapboxgl.accessToken = '';
var map = new mapboxgl.Map({
container: 'mapbox',
style: SETTINGS.maptileurls[localStorage.getItem("mapsource")].json,
attributionControl: false,
dragPan: true,
pitch: 0,
zoom: 2,
maxZoom: 19
});
map.maptype = "mapbox";
map.addControl(new mapboxgl.NavigationControl({
visualizePitch: true
}), 'top-left');
map.addControl(
new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true,
timeout: 10 * 1000
},
fitBoundsOptions: {
maxZoom: localStorage.getItem("trackzoom") == null ? 16 : localStorage.getItem("trackzoom") * 1
},
trackUserLocation: true
}), 'top-left'
);
map.startLocateControl = function () {
// stub
}
map.stopLocateControl = function () {
// stub
}
map.mapEasing = function (t) {
return t * (2 - t);
}
map.setMapHeading = function (heading) {
if (typeof heading == 'number') {
map.easeTo({
bearing: heading,
easing: map.mapEasing
});
}
}
map.setMapLocation = function (latitude, longitude) {
map.easeTo({
center: [
longitude,
latitude
]
});
}
map.updatePackageLayer = function (data) {
var oldmarkers = document.getElementsByClassName("package-marker");
if (oldmarkers.length > 0) {
markerparent = oldmarkers[0].parentNode;
while (oldmarkers.length > 0) {
markerparent.removeChild(oldmarkers[0]);
}
}
for (var i = 0; i < data.length; i++) {
// JavaScript variable scope and anonymous functions are dumb
// This is necessary, otherwise all the on(click)s will fire for the last iteration
// of the loop, or something like that
(function (datai) {
var iconName = getMapIconForItems(datai.items);
//console.log(iconName);
var el = document.createElement("div");
el.className = "package-marker";
el.style = "background-image: url(assets/images/" + iconName + ".png);";
el.addEventListener('click', function () {
openPackageInfoSheet(datai.id);
});
new mapboxgl.Marker(el)
.setLngLat([datai.coords[1], datai.coords[0]])
.addTo(map);
})(data[i]);
}
}
map.animateMapIn = function (latitude, longitude, zoom, heading) {
if (typeof zoom == 'undefined') {
zoom = 16;
}
if (typeof heading == 'undefined') {
heading = 0;
}
map.flyTo({
center: [
longitude,
latitude
],
speed: 1,
zoom: zoom,
heading: heading,
pitch: 0
});
}
map.animateMapIn(userPosition.coords.latitude, userPosition.coords.longitude, 12);
return map;
}

View File

@ -11,14 +11,14 @@ if (localStorage.getItem("packages") != null) {
}
/**
* Count how many items are still undelivered for an address.
* @param {type} address An item in the packages array.
* Count how many items are still undelivered for a location.
* @param {type} location An item in the packages array.
* @returns {Number}
*/
function getUndeliveredCount(address) {
function getUndeliveredCount(location) {
var undelivered = 0;
for (var i = 0; i < address.items.length; i++) {
if (!address.items[i].delivered) {
for (var i = 0; i < location.items.length; i++) {
if (!location.items[i].delivered) {
undelivered++;
}
}
@ -65,16 +65,16 @@ function getMapIconForItems(items) {
// Count how many types we have, and set/overwrite the icon assuming we
// only have that type. If we end up with multiple types, we return that
// icon instead of a specific one.
console.log(types);
//console.log(types);
for (var type in types) {
console.log(type);
//console.log(type);
item_types++;
if (types[type] == 1) {
icon = SETTINGS.itemtypes[type].mapicon;
} else {
icon = SETTINGS.itemtypes[type].pluralmapicon;
}
console.log(icon);
//console.log(icon);
}
if (item_types > 1) {
return "multiple-items";
@ -82,22 +82,31 @@ function getMapIconForItems(items) {
return icon;
}
function addPackage(address, latitude, longitude, type, callback) {
function addPackage(address, latitude, longitude, type, callback, deadline) {
var added = false;
if (typeof type == 'undefined') {
type = "package";
type = SETTINGS.itemtypes[0].id;
}
if (typeof deadline == 'undefined') {
deadline = false;
}
// Extra precision makes the map stupider,
// and doesn't really increase accuracy since four decimal places ~= 11m
latitude = +(parseFloat("" + latitude).toFixed(4));
longitude = +(parseFloat("" + longitude).toFixed(4));
var packageID = uuidv4();
var coordsID = "";
for (var i = 0; i < packages.length; i++) {
if (packages[i].coords[0] == latitude && packages[i].coords[1] == longitude && packages[i].address == address) {
if (packages[i].coords[0] == latitude && packages[i].coords[1] == longitude) {
coordsID = packages[i].id;
packages[i].items.push({
address: address,
delivered: false,
type: type,
deadline: deadline,
id: packageID
});
added = true;
@ -118,6 +127,7 @@ function addPackage(address, latitude, longitude, type, callback) {
address: address,
delivered: false,
type: type,
deadline: deadline,
id: packageID
}
]
@ -128,7 +138,7 @@ function addPackage(address, latitude, longitude, type, callback) {
playSound("ok");
app.toast.show({
text: 'Package Added!<br><span style="font-size: 80%;">' + address + "</span>",
text: SETTINGS.itemtypes[type].name + ' Added!<br><span style="font-size: 80%;">' + address + "</span>",
position: "bottom",
destroyOnClose: true,
closeTimeout: 1000 * 3
@ -148,6 +158,51 @@ function addPackage(address, latitude, longitude, type, callback) {
addAutofillEntry(address);
}
/**
* Import a second package list and merge it with the existing one.
* @param {type} newlist
* @return {number} The number of packages that were skipped because they already exist locally.
*/
function importPackageList(newlist) {
skipped = 0;
for (latlng in newlist) {
var latitude = newlist[latlng].coords[0];
var longitude = newlist[latlng].coords[1];
latitude = +(parseFloat("" + latitude).toFixed(4));
longitude = +(parseFloat("" + longitude).toFixed(4));
for (pkg in newlist[latlng].items) {
var added = false;
for (var i = 0; i < packages.length; i++) {
if (+(parseFloat("" + packages[i].coords[0]).toFixed(4)) == latitude && +(parseFloat("" + packages[i].coords[1]).toFixed(4)) == longitude) {
var newpackage = newlist[latlng].items[pkg];
for (var j in packages[i].items) {
if (packages[i].items[j].id == newpackage.id) {
// This package already exists in the local database.
added = true;
skipped++;
}
}
if (!added) {
packages[i].items.push(package);
added = true;
}
break;
}
}
if (!added) {
packages.push(newlist[latlng]);
}
}
}
localStorage.setItem("packages", JSON.stringify(packages));
if (map != null) {
reloadMap();
}
return skipped;
}
function markDelivered(id, delivered) {
for (var i = 0; i < packages.length; i++) {
for (var j = 0; j < packages[i].items.length; j++) {
@ -173,7 +228,7 @@ function markDelivered(id, delivered) {
function confirmDeletePackage(package, callback) {
app.dialog.confirm(
"Delete item at " + package.address + "?",
"Delete " + SETTINGS.itemtypes[package.type].name.toLowerCase() + " at " + package.address + "?",
"Confirm",
function () {
// delete
@ -229,14 +284,41 @@ function countPackages() {
return count;
}
function addPackageByAddress(address, citystate, type, callback) {
function addPackageByAddress(number, street, citystate, type, callback) {
var requestfinished = false;
var searchingdialogopen = false;
var deadline = false;
var ajaxlookup = function () {
var geocodecache = localStorage.getItem("geocode_cache");
if (geocodecache == null) {
geocodecache = "{}";
localStorage.setItem("geocode_cache", "{}");
}
geocodecache = JSON.parse(geocodecache);
var cachekey = number + " || " + street + " || " + citystate;
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));
} else {
console.log("Info", "Using cached geocode result", cacheitem);
addPackage(cacheitem.address, cacheitem.latitude, cacheitem.longitude, type, callback, deadline);
return;
}
}
$.ajax({
url: SETTINGS.geocodeapi,
dataType: 'json',
data: {
address: address + " " + citystate
number: number,
street: street,
citystate: citystate
},
timeout: 15 * 1000,
success: function (resp) {
@ -247,7 +329,14 @@ function addPackageByAddress(address, 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);
addPackage(resp.address.street, resp.coords[0], resp.coords[1], type, callback, deadline);
geocodecache[cachekey] = {
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));
} else {
playSound("error");
app.dialog.confirm(
@ -255,9 +344,9 @@ function addPackageByAddress(address, citystate, type, callback) {
"Accuracy Warning",
function (ok) {
if (resp.address.street == "") {
addPackage(address, resp.coords[0], resp.coords[1], type, callback);
addPackage(address, resp.coords[0], resp.coords[1], type, callback, deadline);
} else {
addPackage(resp.address.street, resp.coords[0], resp.coords[1], type, callback);
addPackage(resp.address.street, resp.coords[0], resp.coords[1], type, callback, deadline);
}
}
);
@ -286,3 +375,93 @@ function addPackageByAddress(address, 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();
}
}).open();
} else {
ajaxlookup();
}
}
function checkDeadlines() {
if (localStorage.getItem("deadlinealarm_minutes") == null) {
localStorage.setItem("deadlinealarm_minutes", 20);
}
var minutes = localStorage.getItem("deadlinealarm_minutes");
var currentTime = new Date().getTime() / 1000;
var deadlineTime = currentTime + (minutes * 60);
for (i in packages) {
for (j in packages[i].items) {
var item = packages[i].items[j];
if (typeof item.deadline != 'undefined' && item.deadline != false && item.delivered != true) {
if ((typeof item.deadlinealarmed == 'undefined' || item.deadlinealarmed != true) && item.deadline <= deadlineTime) {
playSound("alert");
app.dialog.alert(
"Item at " + item.address + " needs to be delivered by " + timestampToTimeString(item.deadline) + " (" + Math.floor((item.deadline - currentTime) / 60) + " minutes from now).",
"Delivery Alarm",
function () {
}
);
packages[i].items[j].deadlinealarmed = true;
localStorage.setItem("packages", JSON.stringify(packages));
}
}
}
}
}
setInterval(checkDeadlines, 15 * 1000);

View File

@ -64,30 +64,76 @@ var watchLocation = function (success, error) {
}
}
function setupHTML5BarcodeScanner() {
$("body").append('<script src="node_modules/@zxing/library/umd/index.min.js"></script>');
scanBarcode = function (success, error) {
$("#web-barcode-ui").removeClass("hidden");
// Stolen from https://zxing-js.github.io/library/examples/multi-camera/
const codeReader = new ZXing.BrowserMultiFormatReader();
console.log("Info", 'ZXing code reader initialized');
codeReader.getVideoInputDevices()
.then((videoInputDevices) => {
if (videoInputDevices.length == 0) {
codeReader.reset();
$("#web-barcode-ui").addClass("hidden");
error("A camera is required to scan barcodes.");
return;
}
selectedDeviceId = videoInputDevices[0].deviceId;
codeReader.decodeFromInputVideoDeviceContinuously(selectedDeviceId, 'barcode-viewer', (result, err) => {
if (result) {
codeReader.reset();
$("#web-barcode-ui").addClass("hidden");
success(result.text);
return;
}
if (err && !(err instanceof ZXing.NotFoundException)) {
console.error(err);
codeReader.reset();
$("#web-barcode-ui").addClass("hidden");
error(err);
return;
}
});
})
.catch((err) => {
console.error(err);
});
$("#web-barcode-ui").on("click", function () {
codeReader.reset();
$("#web-barcode-ui").addClass("hidden");
});
};
}
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("deviceready", function () {
if (localStorage.getItem("wakelock") == "true") {
window.powerManagement.acquire(function () {
console.log('Wakelock acquired');
console.log("Info", 'Wakelock acquired');
}, function () {
console.log('Failed to acquire wakelock');
console.log("Warn", 'Failed to acquire wakelock');
});
} else {
window.powerManagement.release(function () {
console.log('Wakelock released');
console.log("Info", 'Wakelock released');
}, function () {
console.log('Failed to release wakelock');
console.log("Warn", 'Failed to release wakelock');
});
}
}, false);
openBrowser = function (url) {
cordova.InAppBrowser.open(url, '_blank', 'location=yes');
}
@ -96,6 +142,7 @@ function initCordova() {
window.open(url, '_system', '');
}
if (typeof device != "undefined" && device.platform != "browser") {
scanBarcode = function (success, error) {
cordova.plugins.barcodeScanner.scan(
function (result) {
@ -117,13 +164,15 @@ function initCordova() {
formats: "QR_CODE,DATA_MATRIX,CODE_39,CODE_93,CODE_128,CODABAR,PDF_417,AZTEC,MAXICODE"
}
);
};
} else {
setupHTML5BarcodeScanner();
}
}
function initNW() {
platform_type = "nw";
platform_theme = "md";
openBrowser = function (url) {
nw.Window.open(url, {
id: url
@ -156,57 +205,22 @@ function initNW() {
require('nw.gui').Shell.openExternal(url);
}
$("body").append('<script src="node_modules/@zxing/library/umd/index.min.js"></script>');
scanBarcode = function (success, error) {
$("#web-barcode-ui").removeClass("hidden");
// Stolen from https://zxing-js.github.io/library/examples/multi-camera/
const codeReader = new ZXing.BrowserMultiFormatReader();
console.log('ZXing code reader initialized');
codeReader.getVideoInputDevices()
.then((videoInputDevices) => {
if (videoInputDevices.length == 0) {
codeReader.reset();
$("#web-barcode-ui").addClass("hidden");
error("A camera is required to scan barcodes.");
return;
}
selectedDeviceId = videoInputDevices[0].deviceId;
codeReader.decodeFromInputVideoDeviceContinuously(selectedDeviceId, 'barcode-viewer', (result, err) => {
if (result) {
console.log(result);
codeReader.reset();
$("#web-barcode-ui").addClass("hidden");
success(result.text);
return;
}
if (err && !(err instanceof ZXing.NotFoundException)) {
console.error(err);
codeReader.reset();
$("#web-barcode-ui").addClass("hidden");
error(err);
return;
}
});
})
.catch((err) => {
console.error(err);
});
$("#web-barcode-ui").on("click", function () {
codeReader.reset();
$("#web-barcode-ui").addClass("hidden");
});
};
setupHTML5BarcodeScanner();
}
function initBrowser() {
platform_type = "browser";
platform_theme = "md";
openBrowser = function (url) {
window.open(url);
}
openExternalBrowser = function (url) {
window.open(url);
}
setupHTML5BarcodeScanner();
}
function initPlatform() {

View File

@ -7,7 +7,6 @@
$('.item-content[data-setting=darktheme] .toggle input').on("change", function () {
var checked = $(this).prop('checked');
console.log(checked);
localStorage.setItem("darktheme", checked);
if (localStorage.getItem("darktheme") == "true") {
@ -17,27 +16,35 @@ $('.item-content[data-setting=darktheme] .toggle input').on("change", function (
}
});
$('.item-content[data-setting=showhelp] .toggle input').on("change", function () {
var checked = $(this).prop('checked');
localStorage.setItem("show_help", checked);
});
$('.item-link[data-setting=units] select').on("change", function () {
localStorage.setItem("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());
});
$('.item-content[data-setting=wakelock] .toggle input').on("change", function () {
var checked = $(this).prop('checked');
console.log(checked);
localStorage.setItem("wakelock", checked);
if (platform_type == "cordova") {
if (localStorage.getItem("wakelock") == "true") {
window.powerManagement.acquire(function () {
console.log('Wakelock acquired');
console.log("Info", 'Wakelock acquired');
}, function () {
console.log('Failed to acquire wakelock');
console.log("Warn", 'Failed to acquire wakelock');
});
} else {
window.powerManagement.release(function () {
console.log('Wakelock released');
console.log("Info", 'Wakelock released');
}, function () {
console.log('Failed to release wakelock');
console.log("Warn", 'Failed to release wakelock');
});
}
} else {
@ -69,7 +76,16 @@ $('.item-content[data-setting=alertinterval] .range-slider').on('range:changed',
$('.item-link[data-setting=mapsource] select').on("change", function () {
localStorage.setItem("mapsource", $('.item-link[data-setting=mapsource] select').val());
// Re-init map to load new style
reloadMap();
});
$('.item-content[data-setting=maptype] .toggle input').on("change", function () {
var checked = $(this).prop('checked');
localStorage.setItem("maptype", checked ? "leaflet" : "mapbox");
maptype = checked ? "leaflet" : "mapbox";
reloadMap();
});

View File

@ -0,0 +1,271 @@
/*
* 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 scannerCodes = [];
function resetScanner() {
scannerCodes = [];
app.popup.close("#scanEventTypePopup");
app.popup.close("#scanEventPopup");
router.refreshPage();
}
function brokenScannerEsc() {
app.dialog.confirm(
"Clear list?",
"Confirm",
function () {
resetScanner();
},
function () {
// cancel
}
);
}
function brokenScannerScan() {
scanBarcode(function (code) {
playSound("scan");
if (code != "" && code.length > 5 && code.match(/^[0-9A-Z]+$/i)) {
addCodeToScannerList(code);
} else {
app.dialog.alert("That's not a valid tracking code.", "Error");
}
}, function (error) {
app.dialog.alert(error, "Error");
});
}
function brokenScannerAddTextEntry() {
var code = $("#brokenscannerinput").val();
if (code != "" && code.length > 5 && code.match(/^[0-9A-Z]+$/i)) {
addCodeToScannerList(code);
$("#brokenscannerinput").val("");
} else {
app.dialog.alert("That's not a valid tracking code.", "Error");
}
}
function addCodeToScannerList(code) {
code = code.toUpperCase();
var signatureregexes = [
/^E[A-Z][0-9]{9}US$/, // Priority Mail Express
/^[RV][A-Z][0-9]{9}[A-Z]{2}$/, // Registered mail
/^(420[0-9]{5})?7[0-9]{19}$/ // Certified Mail
];
/**
* Regex of IMpb codes that don't need anything special
* @type {RegExp}
*/
var stcregex = /^(420[0-9]{5})?[0-9]{2}(001|023|055|056|112|113|134|135|138|140|141|142|164|209|211|259|265|269|346|361|389|390|419|431|490|502|551|563|612|624|671|701|702|703|704|723|746|748|790|791|793|794|905|906|907|909|971|972)[0-9]+$/;
var signatureRequired = false;
for (var i = 0; i < signatureregexes.length; i++) {
if (code.match(signatureregexes[i])) {
signatureRequired = true;
break;
}
}
if (!code.match(stcregex)) {
signatureRequired = true;
}
var codeEntryTemplate = Template7.compile('<li class="codelist-entry" data-code="{{code}}">'
+ ' <div class="item-content">'
+ ' <div class="item-inner">'
+ ' <div class="item-title">{{code}}</div>'
+ ' </div>'
+ ' </div>'
+ '</li>');
if (signatureRequired) {
if (code.match(/^E[A-Z][0-9]{9}US$/)) {
app.dialog.confirm(
"Does this item contain a waiver of signature endorsement?",
"Express Item",
function () {
$("#codelist").append(codeEntryTemplate({
code: code
}));
},
function () {
// cancel
}
);
} else {
app.dialog.confirm(
"It looks like this item might require a signature or other special procedures. Add it anyways?",
"Signature Item",
function () {
$("#codelist").append(codeEntryTemplate({
code: code
}));
},
function () {
// cancel
}
);
}
} else {
$("#codelist").append(codeEntryTemplate({
code: code
}));
}
}
function chooseScanEvent() {
if ($("#codelist li.codelist-entry").length <= 0) {
app.dialog.alert("Nothing was entered!");
return;
}
$("#codelist li.codelist-entry").each(function () {
scannerCodes.push($(this).data("code"));
});
openEventPopup();
}
function openEventPopup() {
var eventItemTemplate = Template7.compile('<li data-button="{{button}}" data-title="{{title}}" class="eventbutton" onclick=\'openEventTypePopup("{{title}}");\'>'
+ ' <div class="item-link item-content">'
+ ' <div class="item-media">{{button}}</div>'
+ ' <div class="item-inner">'
+ ' <div class="item-title">{{title}}</div>'
+ ' </div>'
+ ' </div>'
+ '</li>');
$("#scanEventPopup ul.eventlist").html("");
for (i in SETTINGS.scannerevents) {
var event = SETTINGS.scannerevents[i];
$("#scanEventPopup ul.eventlist").append(eventItemTemplate({
button: event.button,
title: event.title
}));
}
app.popup.open("#scanEventPopup");
}
function openEventTypePopup(eventname) {
var eventItemTemplate = Template7.compile('<li data-button="{{button}}" data-title="{{title}}" data-parenttitle="{{parenttitle}}" data-form3849="{{form3849}}" class="eventbutton eventtypebutton">'
+ ' <div class="item-link item-content">'
+ ' <div class="item-media">{{button}}</div>'
+ ' <div class="item-inner">'
+ ' <div class="item-title">{{title}}</div>'
+ ' </div>'
+ ' </div>'
+ '</li>');
var eventafter = false;
for (i in SETTINGS.scannerevents) {
var event = SETTINGS.scannerevents[i];
if (event.title == eventname) {
eventafter = event.after;
break;
}
}
$("#scanEventTypePopup ul.eventlist").html("");
for (i in eventafter) {
var event = eventafter[i];
$("#scanEventTypePopup ul.eventlist").append(eventItemTemplate({
button: event.button,
title: event.title,
parenttitle: eventname,
form3849: event.after == "3849" ? "1" : "0"
}));
}
app.popup.open("#scanEventTypePopup");
}
function saveScanCode(code) {
if (localStorage.getItem("scanevents") == null) {
localStorage.setItem("scanevents", "[]");
}
var events = JSON.parse(localStorage.getItem("scanevents"));
events.push(code);
localStorage.setItem("scanevents", JSON.stringify(events));
}
$(".view-main").off("click", "#codelist li.codelist-entry");
$(".view-main").on("click", "#codelist li.codelist-entry", function () {
var entry = $(this);
var code = entry.data("code");
app.dialog.confirm(
"Remove " + code + " from list?",
"Confirm",
function () {
// delete
entry.remove();
},
function () {
// cancel
}
);
});
$("#app").off("click", "ul li.eventtypebutton");
$("#app").on("click", "ul li.eventtypebutton", function () {
var eventname = $(this).data("parenttitle");
var eventtypename = $(this).data("title");
var scanEvent = [];
scanEvent.push(eventname);
scanEvent.push(eventtypename);
if ($(this).data("form3849") == "1") {
// TODO: make this not a hack
app.dialog.prompt("Key in 3849 form", "3849 Form", function (formcode) {
for (i in scannerCodes) {
saveScanCode({
code: scannerCodes[i],
event: scanEvent,
form3849: formcode,
date: timestampToDateTimeString((new Date).getTime() / 1000)
});
}
app.toast.show({
text: 'Information recorded successfully!',
position: "center",
destroyOnClose: true,
closeTimeout: 1000 * 3
});
resetScanner();
}, function () {
}, "");
} else {
for (i in scannerCodes) {
saveScanCode({
code: scannerCodes[i],
event: scanEvent,
form3849: "",
date: timestampToDateTimeString((new Date).getTime() / 1000)
});
}
app.toast.show({
text: 'Information recorded successfully!',
position: "center",
destroyOnClose: true,
closeTimeout: 1000 * 3
});
resetScanner();
}
});
$("#brokenscannerinput").on('keypress', function (e) {
if (event.key === "Enter") {
brokenScannerAddTextEntry();
}
});

View File

@ -0,0 +1,23 @@
/*
* 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 confirmDeleteScanEntries() {
app.dialog.confirm(
"Really delete all entries from scan list?",
"Clear Entries",
function () {
// clear
localStorage.setItem("scanevents", "[]");
router.navigate("/toolbox/scanner/entries", {
reloadCurrent: true
});
},
function () {
// cancel
}
);
}

View File

@ -0,0 +1,101 @@
/*
* 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 uploadList() {
if (packages.length == 0) {
app.dialog.alert("Your list doesn't have anything to send.", "Empty List");
return;
}
app.dialog.preloader("Uploading...");
var uploadlistdialogopen = true;
$.ajax({
url: SETTINGS.sharelistapi,
dataType: 'json',
method: 'post',
data: {
packages: JSON.stringify(packages)
},
timeout: 15 * 1000,
success: function (resp) {
if (uploadlistdialogopen) {
app.dialog.close();
uploadlistdialogopen = false;
}
if (resp.status == "OK") {
JsBarcode("#listidbarcode", resp.uuid, {
format: "code128",
ean128: false,
width: 2,
height: 40
});
$("#listidbarcodeli").css("display", "");
} else {
app.dialog.alert(resp.message, "Error");
}
},
error: function (jqXHR, status, errorThrown) {
if (uploadlistdialogopen) {
app.dialog.close();
uploadlistdialogopen = false;
}
app.dialog.alert("There was a network or server issue while uploading the list. Please try again.", "Error");
}
});
}
function downloadItemList(code) {
if (typeof code == "undefined") {
code = $("#getlistidbox").val();
}
if (code.match(/^[a-f0-9]{10}$/i)) {
app.dialog.preloader("Downloading...");
var downloadlistdialogopen = true;
$.ajax({
url: SETTINGS.sharelistapi,
dataType: 'json',
method: 'get',
data: {
uuid: code
},
timeout: 15 * 1000,
success: function (resp) {
if (downloadlistdialogopen) {
app.dialog.close();
downloadlistdialogopen = false;
}
if (resp.status == "OK") {
var skipped = importPackageList(resp.packages);
if (skipped > 0) {
app.dialog.alert("List imported and merged with the existing one. " + skipped + " items already existed locally and were skipped. Verify their delivery status manually.", "Import Complete");
} else {
app.dialog.alert("List imported and merged with the existing one.", "Import Complete");
}
} else {
app.dialog.alert(resp.message, "Error");
}
},
error: function (jqXHR, status, errorThrown) {
if (downloadlistdialogopen) {
app.dialog.close();
downloadlistdialogopen = false;
}
app.dialog.alert("There was a network or server issue while downloading the list. Please try again.", "Error");
}
});
} else {
app.dialog.alert("That's not a valid list ID.", "Error");
}
}
function scanListIDBarcode() {
scanBarcode(function (code) {
playSound("scan");
downloadItemList(code);
}, function (error) {
app.dialog.alert(error, "Error");
});
}

View File

@ -86,7 +86,21 @@ function openTrackingHistory(code) {
infocontext.history[i].date = timestampToDateTimeString(infocontext.history[i].date);
infocontext.history[i].status = trackingStatusToNiceString(infocontext.history[i].status, true);
}
// TODO: format timestamps as local time
// Keep last five tracking codes in history
var history = localStorage.getItem("trackingcodehistory");
if (history == null) {
history = [];
} else {
history = JSON.parse(history);
}
if (infocontext.code != "" && !history.includes(infocontext.code)) {
history.push(infocontext.code);
}
while (history.length > 5) {
history.shift();
}
localStorage.setItem("trackingcodehistory", JSON.stringify(history));
if (refresh) {
router.navigate("/toolbox/track/info", {
@ -136,7 +150,7 @@ function scanTrackingBarcode() {
}
}, function (error) {
app.dialog.alert(error, "Error");
})
});
}
$("#trackbtn").click(function () {
@ -148,3 +162,9 @@ $("#trackbtn").click(function () {
app.dialog.alert("That's not a valid tracking code.", "Error");
}
});
$("input[name=trackingcode]").on('keypress', function (e) {
if (event.key === "Enter") {
$("#trackbtn").click();
}
});

View File

@ -12,6 +12,7 @@
<link rel="stylesheet" href="node_modules/leaflet.markercluster/dist/MarkerCluster.css" />
<link rel="stylesheet" href="node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css" />
<link rel="stylesheet" href="node_modules/leaflet.locatecontrol/dist/L.Control.Locate.min.css" />
<link rel="stylesheet" href="node_modules/mapbox-gl/dist/mapbox-gl.css">
<link rel="stylesheet" href="assets/css/app.css" />
<link rel="stylesheet" href="assets/css/backdrop.css" />
<link rel="stylesheet" href="assets/css/oled.css" />
@ -36,6 +37,7 @@
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/leaflet/dist/leaflet.js"></script>
<script src="node_modules/leaflet.markercluster/dist/leaflet.markercluster.js"></script>
<script src="node_modules/mapbox-gl/dist/mapbox-gl.js"></script>
<script src="node_modules/jsbarcode/dist/JsBarcode.all.min.js"></script>
<script src="node_modules/leaflet.locatecontrol/dist/L.Control.Locate.min.js"></script>
@ -50,6 +52,7 @@
<script src="assets/js/location.js"></script>
<script src="assets/js/list.js"></script>
<script src="assets/js/map_leaflet.js"></script>
<script src="assets/js/map_mapbox.js"></script>
<script src="assets/js/map.js"></script>
<script src="assets/js/manage.js"></script>

View File

@ -1,18 +1,19 @@
{
"name": "PackageHelper",
"version": "1.2.0",
"version": "1.3.0",
"main": "index.html",
"license": "MPL-2.0",
"dependencies": {
"@fortawesome/fontawesome-free": "^5.10.2",
"@zxing/library": "^0.15.2",
"framework7": "^5.0.2",
"framework7": "^5.1.3",
"jquery": "^3.4.1",
"jsbarcode": "^3.11.0",
"leaflet": "^1.5.1",
"leaflet-geometryutil": "^0.9.1",
"leaflet.locatecontrol": "^0.67.0",
"leaflet.markercluster": "^1.4.1",
"mapbox-gl": "^1.6.1",
"material-design-icons": "^3.0.1"
},
"devDependencies": {}

View File

@ -1,90 +0,0 @@
<!-- 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="alertsettings">
<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">Alert Settings</div>
</div>
</div>
<div class="page-content">
<div class="list media-list no-hairlines tablet-inset" style="margin-top: 0;">
<ul>
{{#each settings}}
<li>
{{#if toggle}}
<div class="item-content" data-setting="{{setting}}">
<div class="item-inner">
<div style="display: flex; justify-content: between;">
<div class="item-title">
{{title}}
</div>
<div class="item-after" onclick="{{onclick}}">
<label class="toggle toggle-init">
<input type="checkbox" {{#if checked}}checked{{/if}}>
<span class="toggle-icon"></span>
</label>
</div>
</div>
<div class="item-text">{{text}}</div>
</div>
</div>
{{else}}
{{#if slider}}
<div class="item-content" data-setting="{{setting}}">
<div class="item-inner">
<div class="item-title" style="background-color: rgba(0,0,0,0);">
{{title}}
</div>
<div class="item-subtitle padding-horizontal padding-top">
<div class="range-slider range-slider-init padding-top margin-top" data-label="true">
<input type="range" min="{{min}}" max="{{max}}" step="{{step}}" value="{{value}}">
</div>
</div>
</div>
</div>
{{else}}
{{#if select}}
<a class="item-link smart-select smart-select-init" data-open-in="popover" data-setting="{{setting}}">
<select>
{{#each options}}
<option value="{{value}}"{{#if selected}} selected{{/if}}>{{label}}</option>
{{/each}}
</select>
<div class="item-content">
<div class="item-inner">
<div class="item-title">{{title}}</div>
</div>
</div>
</a>
{{else}}
<div class="item-content" data-setting="{{setting}}" onclick="{{onclick}}">
<div class="item-inner">
<div class="item-title-row">
<div class="item-title">{{title}}</div>
</div>
<div class="item-text">{{text}}</div>
</div>
</div>
{{/if}}
{{/if}}
{{/if}}
</li>
{{/each}}
</ul>
</div>
</div>
<script src="assets/js/settings.js"></script>
</div>

File diff suppressed because it is too large Load Diff

58
www/pages/help/list.html 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/. -->
<div class="panel panel-right panel-cover">
<div class="view">
<div class="page">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="title">Help</div>
<div class="right">
<a class="link panel-close">
<span>Close</span>
</a>
</div>
</div>
</div>
<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
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.
</div>
<div class="block-title">Clear the List</div>
<div class="block">
Tap the <span class="color-red text-color-primary"><i class="material-icons">delete</i></span>
button to remove all items from the list. This cannot be undone, so it's
a good idea to do it at the end of the day or in the morning before you start.
Note that clearing the list does not affect the address autofill.
</div>
<div class="block-title">Search</div>
<div class="block">
Tap the <i class="material-icons text-color-primary">search</i> button to open a search
box. Type in this box to hide any list entries that don't contain your
search query.
</div>
<div class="block-title">Sort Items</div>
<div class="block">
Tap the <i class="material-icons text-color-primary">sort</i> button to sort
the list. You can sort by distance from your current
location, alphabetically by street name, and numerically by
address number.
</div>
</div>
</div>
</div>
</div>

77
www/pages/help/map.html Normal file
View File

@ -0,0 +1,77 @@
<!-- 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="panel panel-right panel-cover">
<div class="view">
<div class="page">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="title">Help</div>
<div class="right">
<a class="link panel-close">
<span>Close</span>
</a>
</div>
</div>
</div>
<div class="page-content">
<div class="block-title">Manage Items</div>
<div class="block">
Tap on an icon to see the item(s) at that location.
Tap on an item to toggle it between delivered and
undelivered. If two or more addresses are very close
together, their items will be grouped under one icon.
</div>
<div class="block-title">Move the Map</div>
<div class="block">
Pinch with two fingers or scroll with a mouse to zoom
in and out. You can also double-tap to zoom in.
Drag with one finger or a mouse to move (pan) the map.
<br />
If you prefer, tap or click
<span class="button button-small display-inline button-fill color-white text-color-black"><i class="fas fa-plus"></i></span>
and
<span class="button button-small display-inline button-fill color-white text-color-black"><i class="fas fa-minus"></i></span>
to zoom in and out.
<br />
Drag on
<span class="button button-small display-inline button-fill color-white text-color-black"><i class="fas fa-sort"></i></span>
to rotate and tilt the map. Dragging left and right rotates, up and down tilts.
You can also rotate with two fingers by twisting them around each other,
or with a keyboard by holding Ctrl and dragging the mouse.
</div>
<div class="block-title">Follow Location</div>
<div class="block">
Tap
<span class="button button-small display-inline button-fill color-white text-color-black"><i class='material-icons'>my_location</i></span>
or
<span class="button button-small display-inline button-fill color-white text-color-black"><i class='far fa-compass'></i></span>
to show your current
location on the map as a blue dot. The map will follow
the dot until you zoom or move the map. If the dot goes
off the edge of the map, press the button again to
re-center the map. Pressing the button when the dot is
visible will hide the dot and stop the map from
following your location.
</div>
<div class="block-title">Get Directions</div>
<div class="block">
<span class="text-color-primary"><i class="material-icons">directions</i></span> will open your
default map/navigation app with the item location set as
the destination. This is useful if you're having
trouble finding the location.
</div>
</div>
</div>
</div>
</div>

View File

@ -24,6 +24,12 @@
<a class="link text-color-red" onclick="confirmDeleteAllPackages()">
<i class="icon material-icons">delete</i>
</a>
{{#if show_help}}
<a class="link" href="/help/list">
<i class="icon material-icons">help</i>
</a>
{{/if}}
</div>
<form class="searchbar searchbar-expandable package-list-searchbar">
<div class="searchbar-inner">

View File

@ -15,9 +15,11 @@
</div>
<div class="title">Map</div>
<div class="right">
<a class="link" onclick="reloadMap()">
<i class="material-icons">refresh</i>
{{#if show_help}}
<a class="link" href="/help/map">
<i class="icon material-icons">help</i>
</a>
{{/if}}
</div>
</div>
</div>

View File

@ -12,13 +12,13 @@
<i class="icon icon-back"></i>
</a>
</div>
<div class="title">Settings</div>
<div class="title">{{page_title}}</div>
</div>
</div>
<div class="page-content">
<div class="list media-list no-hairlines tablet-inset" style="margin-top: 0;">
<div class="list media-list no-hairlines no-margin-top tablet-inset">
<ul>
{{#each settings}}
<li>

View File

@ -19,6 +19,14 @@
<div class="page-content">
<div class="list no-hairlines tablet-inset" style="margin-top: 0;">
<ul>
<li>
<a href="/toolbox/scanner" class="item-link item-content">
<div class="item-media"><i class="icon fas fa-barcode"></i></div>
<div class="item-inner">
<div class="item-title">Broken Scanner</div>
</div>
</a>
</li>
<li>
<a href="/toolbox/track" class="item-link item-content">
<div class="item-media"><i class="icon fas fa-search-location"></i></div>
@ -35,14 +43,14 @@
</div>
</a>
</li>
<!--<li>
<a href="/toolbox/scanner" class="item-link item-content">
<div class="item-media"><i class="icon fas fa-barcode"></i></div>
<li>
<a href="/toolbox/sharelist" class="item-link item-content">
<div class="item-media"><i class="icon fas fa-share-alt"></i></div>
<div class="item-inner">
<div class="item-title">Scan for Delivery</div>
<div class="item-title">Share Item List</div>
</div>
</a>
</li>-->
</li>
</ul>
</div>
</div>

View File

@ -0,0 +1,50 @@
<!-- 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">
<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">Broken Scanner</div>
</div>
</div>
<div class="page-content">
<div class="list no-hairlines tablet-inset" style="margin-top: 0;">
<ul>
<li>
<a href="/toolbox/scanner/scanner" class="item-link item-content">
<div class="item-media">1</div>
<div class="item-inner">
<div class="item-title">Scan Barcode</div>
</div>
</a>
</li>
<li>
<a href="/toolbox/scanner/entries" class="item-link item-content">
<div class="item-media">2</div>
<div class="item-inner">
<div class="item-title">Review Entries</div>
</div>
</a>
</li>
</ul>
</div>
<div class="block text-align-center">
<i class="material-icons material-icons-24px">info</i>
<br />
Scan barcodes while your postal scanner is crashed or restarting.
When the scanner is working again, you can scan all the saved
barcodes from your phone screen using the scanner's Manual Input or
Scan Barcode feature.
</div>
</div>
</div>

View File

@ -0,0 +1,60 @@
<!-- 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="scanner">
<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">Review Entries</div>
<div class="right">
<a class="link text-color-red" onclick="confirmDeleteScanEntries()">
<i class="icon material-icons">delete</i>
</a>
</div>
</div>
</div>
<div class="page-content">
{{#if entries}}
<div class="list media-list no-margin-top no-hairlines">
<ul>
{{#each entries}}
<li style="padding-top: 2rem; padding-bottom: 2rem;">
<div class="item-content">
<div class="item-inner">
<div style="background-color: white; display: flex; justify-content: center; padding-left: 1rem; padding-right: 1rem;">
<svg class="barcode_entry" id="barcode_{{code}}" data-barcode="{{code}}"></svg>
</div>
<div class="item-text text-align-center">{{event}}</div>
<div class="item-text text-align-center">{{date}}</div>
{{#if form3849}}
<h2 class="item-text text-align-center">Form 3849:</h2>
<div style="background-color: white; display: flex; justify-content: center; padding-left: 1rem; padding-right: 1rem;">
<svg class="barcode_entry" id="barcode_{{form3849}}" data-barcode="{{form3849}}"></svg>
</div>
{{/if}}
</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/barcode-dashed.svg" class="margin-vertical" />
<h2 class="margin-top">
No scan entries.
</h2>
</div>
{{/if}}
</div>
<script src="assets/js/toolbox_scannerentries.js"></script>
</div>

View File

@ -0,0 +1,107 @@
<!-- 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="scanner">
<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">Scan Barcode</div>
<div class="subnavbar" style="background-color: var(--f7-searchbar-bg-color,var(--f7-bars-bg-color));">
<div class="subnavbar-inner">
<div class="searchbar">
<div class="searchbar-inner">
<div class="searchbar-input-wrap">
<input type="text" id="brokenscannerinput" placeholder="Enter Code" autocomplete="off" autocorrect="off" autocapitalize="off" style="padding-left: 1em; padding-right: 1em;" />
</div>
<a id="brokenscannercodeadd" onclick="brokenScannerAddTextEntry()" style="padding-right: 0.5em; padding-top: 0.3em;">
<i class="material-icons">keyboard_return</i>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="fab fab-extended fab-left-bottom">
<a href="#" onclick="brokenScannerEsc()">
<div class="fab-text">ESC</div>
</a>
</div>
<div class="fab fab-extended fab-center-bottom">
<a href="#" onclick="brokenScannerScan()">
<div class="fab-text">Scan</div>
</a>
</div>
<div class="fab fab-extended fab-right-bottom">
<a href="#" onclick="chooseScanEvent()">
<div class="fab-text">Enter</div>
</a>
</div>
<div class="popup" id="scanEventPopup" style="background-color: var(--f7-page-bg-color);">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="title">Select Event</div>
</div>
</div>
<div class="block no-margin-top padding-bottom" style="height: calc(100% - var(--f7-navbar-height)); overflow-y: auto;">
<div class="list tablet-inset no-hairlines no-margin-top">
<ul class="eventlist">
</ul>
</div>
</div>
<div class="fab fab-extended fab-left-bottom">
<a href="#" onclick="app.popup.close('#scanEventPopup');">
<div class="fab-text">ESC</div>
</a>
</div>
</div>
<div class="popup" id="scanEventTypePopup" style="background-color: var(--f7-page-bg-color);">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="title">Event Type</div>
</div>
</div>
<div class="block no-margin-top padding-bottom" style="height: calc(100% - var(--f7-navbar-height)); overflow-y: auto;">
<div class="list tablet-inset no-hairlines no-margin-top">
<ul class="eventlist">
</ul>
</div>
</div>
<div class="fab fab-extended fab-left-bottom">
<a href="#" onclick="app.popup.close('#scanEventTypePopup');">
<div class="fab-text">ESC</div>
</a>
</div>
</div>
<div class="page-content">
<div class="list no-hairlines tablet-inset">
<ul id="codelist">
</ul>
</div>
<div style="height: 3rem;"></div>
</div>
<script src="assets/js/toolbox_scanner.js"></script>
</div>

View File

@ -0,0 +1,68 @@
<!-- 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="sharelist">
<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">Share Item List</div>
</div>
</div>
<div class="page-content">
<div class="list media-list no-hairlines tablet-inset no-margin-top">
<ul>
<li class="item-divider">Send List</li>
<li>
<div class="item-content">
<a class="button button-fill" id="sendlistbtn" onclick="uploadList();"><i class="fas fa-upload"></i> Upload List</a>
</div>
</li>
<li style="display: none;" id="listidbarcodeli">
<div class="item-content" style="background-color: white; display: flex; justify-content: center; padding-left: 1rem; padding-right: 1rem;">
<svg class="barcode" id="listidbarcode"></svg>
</div>
</li>
<li class="item-divider">Receive List</li>
<li>
<div class="item-content">
<div class="item-inner">
<div class="item-title item-label">List ID</div>
<div class="item-input-wrap">
<input type="text" id="getlistidbox" placeholder="" autocomplete="off" autocorrect="off" autocapitalize="off" />
<span class="input-clear-button"></span>
</div>
</div>
</div>
</li>
<li class="item-content">
<a class="button button-fill" onclick="downloadItemList();"><i class="fas fa-download"></i> Download List</a>
</li>
<li>
<div class="item-content">
<a class="button button-fill" id="scanlistbarcodebtn" onclick="scanListIDBarcode();"><i class="fas fa-barcode"></i> Scan List Barcode</a>
</div>
</li>
</ul>
</div>
<div class="block text-align-center">
<i class="material-icons material-icons-24px">info</i>
<br />
Share your delivery list to another device. The sender uploads the list,
and the receiver(s) either scan the barcode on the sender's device, or
type in the code beneath the barcode.
</div>
</div>
<script src="assets/js/toolbox_sharelist.js"></script>
</div>

View File

@ -40,9 +40,32 @@
</ul>
</div>
<div class="block">
<div class="block text-align-center">
<i class="material-icons material-icons-24px">info</i>
<br />
Compatible with USPS, UPS, FedEx, and DHL tracking codes.
Can extract full delivery point address (and sometimes customer phone number)
from 2D FedEx and UPS Mail Innovations barcodes.
</div>
{{#if trackingcodehistory}}
<div class="list no-hairlines tablet-inset">
<ul>
<li class="item-divider">
History
</li>
{{#each trackingcodehistory}}
<li>
<div class="item-link item-content" onclick="openTrackingHistory('{{this}}')">
<div class="item-inner">
<div class="item-title">{{this}}</div>
</div>
</div>
</li>
{{/each}}
</ul>
</div>
{{/if}}
</div>
<script src="assets/js/toolbox_track.js"></script>

View File

@ -52,12 +52,7 @@
</li>
<li>
<div class="item-content" style="background-color: white; display: flex; justify-content: center; padding-left: 1rem; padding-right: 1rem;">
<svg class="barcode"
jsbarcode-format="code128"
jsbarcode-value="{{#if barcode}}{{barcode}}{{else}}{{code}}{{/if}}"
jsbarcode-height="40"
jsbarcode-width="2">
</svg>
<svg class="barcode" id="trackingbarcode"></svg>
</div>
</li>
<li class="item-divider">History</li>
@ -84,6 +79,11 @@
</div>
<script>
JsBarcode(".barcode").init();
JsBarcode("#trackingbarcode", "{{#if barcode}}{{escape barcode}}{{else}}{{escape code}}{{/if}}", {
format: "code128",
ean128: true,
width: 2,
height: 40
});
</script>
</div>

View File

@ -4,6 +4,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
var show_help = function () {
return localStorage.getItem("show_help") != "false";
}
var routes = [
{
path: '/home',
@ -27,13 +31,14 @@ var routes = [
},
{
path: '/manage',
name: 'manage',
templateUrl: './pages/manage.html',
options: {
context: {
show_help: show_help,
itemtypes: SETTINGS.itemtypes
}
},
name: 'manage',
on: {
pageAfterIn: function () {
app.autocomplete.create({
@ -56,8 +61,13 @@ var routes = [
},
{
path: '/list',
templateUrl: './pages/list.html',
name: 'list',
templateUrl: './pages/list.html',
options: {
context: {
show_help: show_help
}
},
on: {
pageAfterIn: function () {
loadPackageList();
@ -74,14 +84,17 @@ var routes = [
}
});
}
},
keepAlive: true
}
},
{
path: '/map',
url: './pages/map.html',
templateUrl: './pages/map.html',
name: 'map',
keepAlive: true,
options: {
context: {
show_help: show_help
}
},
on: {
pageAfterIn: function () {
reloadMap();
@ -91,15 +104,83 @@ var routes = [
{
path: '/toolbox',
url: './pages/toolbox.html',
name: 'toolbox'
name: 'toolbox',
routes: [
{
path: '/scanner',
templateUrl: './pages/toolbox/scanner.html',
routes: [
{
path: '/scanner',
templateUrl: './pages/toolbox/scanner/scanner.html',
name: 'scanner'
},
{
path: '/toolbox/track',
url: './pages/toolbox/track.html',
name: 'track'
path: '/entries',
name: 'entries',
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> ');
}
} else {
var entries = false;
}
resolve({
templateUrl: './pages/toolbox/scanner/entries.html'
}, {
context: {
entries: entries
}
});
},
on: {
pageAfterIn: function () {
$(".barcode_entry").each(function () {
var code = $(this).data("barcode");
JsBarcode("#barcode_" + code, code, {
format: "code128",
ean128: true,
width: 2,
height: 40
});
});
}
}
}
]
},
{
path: '/toolbox/weather',
path: '/track',
name: 'track',
async: function (routeTo, routeFrom, resolve, reject) {
var history = localStorage.getItem("trackingcodehistory");
if (history == null) {
history = false;
} else {
history = JSON.parse(history).reverse(); // Most recent on top
}
resolve({
templateUrl: './pages/toolbox/track.html'
}, {
context: {
trackingcodehistory: history
}
});
},
routes: [
{
path: '/info',
templateUrl: './pages/toolbox/trackinginfo.html',
name: 'trackinginfo'
}
]
},
{
path: '/weather',
url: './pages/toolbox/weather.html',
name: 'weather',
on: {
@ -109,9 +190,28 @@ var routes = [
}
},
{
path: '/toolbox/track/info',
templateUrl: './pages/toolbox/trackinginfo.html',
name: 'trackinginfo'
path: '/sharelist',
url: './pages/toolbox/sharelist.html',
name: 'sharelist'
}
]
},
{
path: '/help',
routes: [
{
path: '/list',
panel: {
url: './pages/help/list.html'
}
},
{
path: '/map',
panel: {
url: './pages/help/map.html'
}
}
]
},
{
path: '/credits',
@ -122,17 +222,6 @@ var routes = [
path: '/settings',
name: 'settings',
async: function (routeTo, routeFrom, resolve, reject) {
var mapstyles = [];
for (var id in SETTINGS.maptileurls) {
if (SETTINGS.maptileurls.hasOwnProperty(id)) {
mapstyles.push({
value: id,
label: SETTINGS.maptileurls[id].name,
selected: localStorage.getItem("mapsource") == id
});
}
}
var settings = [
{
setting: "alerts",
@ -140,6 +229,13 @@ var routes = [
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
}
];
@ -164,33 +260,17 @@ var routes = [
onclick: ""
},
{
setting: "units",
title: "Measurement units",
select: true,
options: [
{
value: "metric",
label: "Meters/Kilometers",
selected: localStorage.getItem("units") == "metric"
},
{
value: "imperial",
label: "Feet/Miles",
selected: localStorage.getItem("units") == "imperial"
}
]
},
{
setting: "mapsource",
title: "Map style",
select: true,
options: mapstyles,
text: "Choose which map style to use."
setting: "showhelp",
title: "Show help",
text: "Show the <span class=material-icons-intext><i class=material-icons>help</i></span> icons",
toggle: true,
checked: localStorage.getItem("show_help") != "false",
onclick: ""
},
{
setting: "versions",
title: "PackageHelper app v" + app_version,
text: "Copyright &copy; 2019 Netsyms Technologies. Licensed under the Mozilla Public License 2.0.",
text: "Copyright &copy; 2019-2020 Netsyms Technologies. Licensed under the Mozilla Public License 2.0.",
onclick: ""
},
{
@ -204,7 +284,7 @@ var routes = [
setting: "privacy",
title: "Privacy policy and legal",
text: "",
onclick: "openBrowser('https://netsyms.com/legal?pk_campaign=PackageHelpterApp')",
onclick: "openBrowser('https://netsyms.com/legal?pk_campaign=PackageHelperApp')",
link: true
});
@ -212,14 +292,15 @@ var routes = [
templateUrl: './pages/settings.html'
}, {
context: {
page_title: "Settings",
settings: settings
}
});
}
},
routes: [
{
path: '/settings/alerts',
name: 'alertsettings',
path: '/alerts',
name: 'settings',
async: function (routeTo, routeFrom, resolve, reject) {
var alertsounds = [];
for (var id in SETTINGS.alertsounds) {
@ -232,7 +313,8 @@ var routes = [
}
}
var settings = [{
var settings = [
{
setting: "alertsound",
title: "Alert sound",
text: "Select which sound to play when a package is nearby.",
@ -265,15 +347,101 @@ var routes = [
step: 15,
value: localStorage.getItem("alertinterval") == null ? 30 : localStorage.getItem("alertinterval"),
slider: true
}];
}
];
resolve({
templateUrl: './pages/alertsettings.html'
templateUrl: './pages/settings.html'
}, {
context: {
page_title: "Alert Settings",
settings: settings
}
});
}
},
{
path: '/maps',
name: 'settings',
async: function (routeTo, routeFrom, resolve, reject) {
var mapstyles = [];
for (var id in SETTINGS.maptileurls) {
if (SETTINGS.maptileurls.hasOwnProperty(id)) {
mapstyles.push({
value: id,
label: SETTINGS.maptileurls[id].name,
selected: localStorage.getItem("mapsource") == id
});
}
}
var settings = [
{
setting: "mapsource",
title: "Map style",
select: true,
options: mapstyles,
text: "Choose which map style to use."
},
{
setting: "units",
title: "Measurement units",
select: true,
options: [
{
value: "metric",
label: "Meters/Kilometers",
selected: localStorage.getItem("units") == "metric"
},
{
value: "imperial",
label: "Feet/Miles",
selected: localStorage.getItem("units") == "imperial"
}
]
},
{
setting: "trackzoom",
title: "Zoom when tracking location",
select: true,
options: [
{
value: 15,
label: "Low",
selected: localStorage.getItem("trackzoom") == 15
},
{
value: 16,
label: "Normal",
selected: localStorage.getItem("trackzoom") == null || localStorage.getItem("trackzoom") == 16
},
{
value: 17,
label: "High",
selected: localStorage.getItem("trackzoom") == 17
}
]
},
{
setting: "maptype",
title: "Alternative map",
text: "Turn this on if you have problems with the map.",
toggle: true,
checked: localStorage.getItem("maptype") == "leaflet",
onclick: ""
}
];
resolve({
templateUrl: './pages/settings.html'
}, {
context: {
page_title: "Map Settings",
settings: settings
}
});
}
}
]
}
];

View File

@ -8,18 +8,27 @@ var SETTINGS = {
maptileurls: {
liberty: {
url: "https://maps.netsyms.net/styles/osm-liberty/{z}/{x}/{y}.png",
json: "https://maps.netsyms.net/styles/osm-liberty/style.json",
name: "Liberty",
bgcolor: "#EFEFEF"
},
terrain: {
url: "https://maps.netsyms.net/styles/klokantech-terrain/{z}/{x}/{y}.png",
json: "https://maps.netsyms.net/styles/klokantech-terrain/style.json",
name: "Terrain",
bgcolor: "#EDF5F3"
},
fiord: {
url: "https://maps.netsyms.net/styles/fiord-color/{z}/{x}/{y}.png",
json: "https://maps.netsyms.net/styles/fiord-color/style.json",
name: "Dark Fiord",
bgcolor: "#45516E"
},
oledblack: {
url: "https://maps.netsyms.net/styles/oled-black/{z}/{x}/{y}.png",
json: "https://maps.netsyms.net/styles/oled-black/style.json",
name: "OLED Black",
bgcolor: "#000000"
}
},
alertsounds: {
@ -72,14 +81,14 @@ var SETTINGS = {
},
signature: {
id: "signature",
name: "Signature",
name: "Signature Item",
icon: "fas fa-file-signature fa-fw",
mapicon: "signature",
pluralmapicon: "signatures"
},
express: {
id: "express",
name: "Express",
name: "Express Item",
icon: "fas fa-shipping-fast fa-fw",
mapicon: "express",
pluralmapicon: "multiple-items"
@ -94,8 +103,208 @@ var SETTINGS = {
"windy",
"none"
],
scannerevents: [
{
button: 1,
title: "Delivered",
type: "event",
after: [
{
button: 1,
title: "In/At Mailbox",
type: "type",
after: false
},
{
button: 2,
title: "Front Door/Porch",
type: "type",
after: false
},
{
button: 3,
title: "Left with Individual at Address",
type: "type",
after: false
},
{
button: 4,
title: "Front Desk/Reception/Mail Room",
type: "type",
after: false
},
{
button: 5,
title: "Parcel Locker",
type: "type",
after: false
},
{
button: 6,
title: "Garage or Other Location at Address",
type: "type",
after: false
},
{
button: 7,
title: "Left with Neighbor",
type: "type",
after: false
},
{
button: 'G',
title: "Authorized Agent",
type: "type",
after: false
},
{
button: 'U',
title: "Tendered to Authorized Agent",
type: "type",
after: false
}
]
},
{
button: 2,
title: "Attempted",
type: "event",
after: [
{
button: 2,
title: "No Secure Location Available",
type: "type",
after: "3849"
},
{
button: 3,
title: "No Access or Business Closed",
type: "type",
after: false
},
{
button: 4,
title: "Receptacle Full/Item Oversized",
type: "type",
after: "3849"
},
{
button: 5,
title: "Customer Hold",
type: "type",
after: false
},
{
button: 6,
title: "Animal Interference",
type: "type",
after: false
}
]
},
{
button: 3,
title: "Return to Sender or Forward",
type: "event",
after: [
{
button: 1,
title: "Forwarded",
type: "type",
after: false
},
{
button: 2,
title: "Insufficient Address",
type: "type",
after: false
},
{
button: 3,
title: "No Such Number",
type: "type",
after: false
},
{
button: 4,
title: "Addressee Unknown",
type: "type",
after: false
},
{
button: 5,
title: "Vacant",
type: "type",
after: false
},
{
button: 6,
title: "Unable to Forward / Forward Order Expired",
type: "type",
after: false
},
{
button: 7,
title: "Deceased",
type: "type",
after: false
},
{
button: 8,
title: "Refused",
type: "type",
after: false
},
{
button: 9,
title: "Returned for Other Reason",
type: "type",
after: false
}
]
},
{
button: 4,
title: "Other",
type: "event",
after: [
{
button: 'X',
title: "Dispatch to Military/Diplomatic PO",
type: "type",
after: false
},
{
button: 1,
title: "Return to Post Office Not Attempted / Delivery Delay",
type: "type",
after: false
},
{
button: 2,
title: "Local Weather Delay",
type: "type",
after: false
},
{
button: 3,
title: "Visible Damage",
type: "type",
after: false
},
{
button: 4,
title: "Out for Delivery",
type: "type",
after: false
}
]
}
],
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"
geoipapi: "https://apis.netsyms.net/packagehelper/geoip.php",
sharelistapi: "https://apis.netsyms.net/packagehelper/sharepackagelist.php"
}