diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..226734f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,24 @@
+# EditorConfig is awesome: http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 4
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.html]
+indent_size = 2
+
+[*.mustache]
+indent_size = 2
+
+[*.css]
+indent_size = 2
+
+[Makefile]
+indent_style = tab
diff --git a/.gitignore b/.gitignore
index f5645f8..b206c98 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,11 @@
/bower_components/
+/vendor/
config.inc.php
owntracks.db3
+# Composer binary (get it from https://getcomposer.org/download/)
+/composer.phar
+
# Windows image file caches
Thumbs.db
ehthumbs.db
@@ -9,43 +13,8 @@ ehthumbs.db
# Folder config file
Desktop.ini
-# Recycle Bin used on file shares
-$RECYCLE.BIN/
-
-# Windows Installer files
-*.cab
-*.msi
-*.msm
-*.msp
-
-# Windows shortcuts
-*.lnk
-
-# =========================
-# Operating System Files
-# =========================
-
-# OSX
-# =========================
-
+# macOS metadata files
.DS_Store
.AppleDouble
.LSOverride
-
-# Thumbnails
._*
-
-# Files that might appear in the root of a volume
-.DocumentRevisions-V100
-.fseventsd
-.Spotlight-V100
-.TemporaryItems
-.Trashes
-.VolumeIcon.icns
-
-# Directories potentially created on remote AFP share
-.AppleDB
-.AppleDesktop
-Network Trash Folder
-Temporary Items
-.apdisk
diff --git a/assets/index.js b/assets/index.js
new file mode 100644
index 0000000..a538ae7
--- /dev/null
+++ b/assets/index.js
@@ -0,0 +1,647 @@
+//datetimepicker setup
+var dateFrom;
+var dateTo;
+var accuracy;
+var datePrevFrom;
+var datePrevTo;
+var dateNextFrom;
+var dateNextTo;
+var trackerID;
+var trackerIDs = [];
+
+var i;
+var map_drawn = false;
+var show_markers;
+var mymap;
+var tid_markers; // markers collected from json
+var my_marker;
+var my_markers = [];
+var my_latlngs = [];
+var polylines = [];
+var default_zoom;
+var default_center;
+var live_view = false;
+var live_view_timer;
+
+var marker_start_icons = [];
+var marker_finish_icons = [];
+var marker_icons = [];
+
+function updateDateNav(_dateFrom, _dateTo){
+ console.log("updateDateNav : INIT");
+
+
+ if(typeof _dateFrom == "undefined") { _dateFrom = dateFrom; }
+ if(typeof _dateTo == "undefined") { _dateTo = dateTo; }
+
+
+ diff = _dateTo.diff(_dateFrom, 'days');
+ //if(dateTo.isSame(dateFrom)){ diff = diff+1; }
+
+ datePrevTo = moment(_dateFrom).subtract(1, 'days');;
+ datePrevFrom = moment(datePrevTo).subtract(diff, 'days');
+
+ dateNextFrom = moment(_dateTo).add(1, 'days');
+ dateNextTo = moment(dateNextFrom).add(diff, 'days');
+
+ //disable Next button
+ if(dateNextFrom.isAfter(moment())){
+ $('#nextButton').addClass('disabled');
+ }else{
+ $('#nextButton').removeClass('disabled');
+ }
+
+ //disable today button
+ if(dateNextFrom.isSame(moment())){
+ $('#todayButton').addClass('disabled');
+ $('#livemap_on').removeClass('disabled');
+ }else{
+ $('#todayButton').removeClass('disabled');
+ $('#livemap_on').addClass('disabled');
+ }
+}
+
+function gotoDate(_dateFrom, _dateTo, pushState){
+ console.log("gotoDate : INIT");
+
+ var _dateFrom = (typeof _dateFrom !== 'undefined') ? moment(_dateFrom) : moment();
+ var _dateTo = (typeof _dateTo !== 'undefined') ? moment(_dateTo) : moment();
+ var pushState = (typeof pushState !== 'undefined') ? pushState : true;
+
+ dateFrom = _dateFrom;
+ dateTo = _dateTo;
+
+ $('#dateFrom').val(moment(dateFrom).format('YYYY-MM-DD'));
+ $('#dateTo').val(moment(dateTo).format('YYYY-MM-DD'));
+
+
+ //push selected dates in window.history stack
+ if(pushState) { window.history.pushState(
+ {dateFrom: moment(dateFrom).format('YYYY-MM-DD'), dateTo: moment(dateTo).format('YYYY-MM-DD')},
+ '',
+ window.location.pathname + '?dateFrom=' + moment(dateFrom).format('YYYY-MM-DD') + '&dateTo=' + moment(dateTo).format('YYYY-MM-DD')
+ );
+ }
+
+ updateDateNav();
+
+ mapMarkers();
+ return false;
+}
+
+function gotoAccuracy(){
+ console.log("gotoAccuracy : INIT");
+
+ var _accuracy = parseInt($('#accuracy').val());
+
+ if(_accuracy != accuracy){
+
+ Cookies.set('accuracy', _accuracy);
+ console.log("Accuracy cookie = " + Cookies.get('accuracy'));
+
+ //location.href='./?dateFrom='+moment(dateFrom).format('YYYY-MM-DD') + '&dateTo=' + moment(dateTo).format('YYYY-MM-DD') + '&accuracy=' + _accuracy + '&trackerID=' + trackerID;
+
+ accuracy = _accuracy;
+
+ mapMarkers();
+
+ }else{
+ $('#configCollapse').collapse('hide');
+ }
+ return false;
+}
+
+function changeTrackerID(){
+ console.log("changeTrackerID : INIT");
+
+ var _trackerID = $('#trackerID_selector').val();
+
+ if(_trackerID != trackerID){
+
+ Cookies.set('trackerID', _trackerID);
+ console.log("changeTrackerID : INFO trackerID cookie = " + Cookies.get('trackerID'));
+
+ trackerID = _trackerID;
+ drawMap();
+
+ }else{
+ $('#configCollapse').collapse('hide');
+ }
+ return false;
+}
+
+function handlePopState(event){
+ console.log("handlePopState : INIT");
+ console.log(event);
+
+ return gotoDate(event.state.dateFrom, event.state.dateTo, false);
+}
+
+function initUI(dateFromStr, dateToStr, accuracy, trackerID, language) {
+ console.log("initUI : INIT");
+
+ dateFrom = moment(dateFromStr);
+ dateTo = moment(dateToStr);
+
+ //date params event handlers
+ updateDateNav();
+
+ $('.input-daterange').datepicker({
+ format: 'yyyy-mm-dd',
+ language: language,
+ endDate: '0d',
+ });
+
+ $('.input-daterange').datepicker().on('hide', function(e) {
+ return gotoDate($('#dateFrom').val(), $('#dateTo').val());
+ });
+
+ //accuracy event handlers
+ $('#accuracy').change(function(){
+ gotoAccuracy();
+ });
+ $('#accuracySubmit').click(function(){
+ gotoAccuracy();
+ });
+
+
+ $('#trackerID_selector').change(function(){
+ changeTrackerID();
+ });
+
+ $('#configCollapse').on('show.bs.collapse', function (e) {
+ $('#configButton').removeClass( "btn-default" ).addClass( "btn-primary" ).addClass( "active" );
+ })
+ $('#configCollapse').on('hide.bs.collapse', function (e) {
+ $('#configButton').addClass( "btn-default" ).removeClass( "btn-primary" ).removeClass( "active" );
+ })
+
+ //setup history popupstate event handler
+ window.onpopstate = handlePopState;
+}
+
+function initMap()
+{
+ console.log("initMap : INIT");
+
+ show_markers = Cookies.get('show_markers');
+ console.log("initMap : INFO show_markers = " + show_markers);
+
+ marker_start_icons[0] = L.AwesomeMarkers.icon({icon: 'play', markerColor: 'blue', iconColor: 'green' });
+ marker_start_icons[1] = L.AwesomeMarkers.icon({icon: 'play', markerColor: 'red', iconColor: 'green' });
+ marker_start_icons[2] = L.AwesomeMarkers.icon({icon: 'play', markerColor: 'orange', iconColor: 'green' });
+ marker_start_icons[3] = L.AwesomeMarkers.icon({icon: 'play', markerColor: 'green', iconColor: 'darkgreen' });
+ marker_start_icons[4] = L.AwesomeMarkers.icon({icon: 'play', markerColor: 'purple', iconColor: 'green' });
+ marker_start_icons[5] = L.AwesomeMarkers.icon({icon: 'play', markerColor: 'cadetblue', iconColor: 'green' });
+ marker_start_icons[6] = L.AwesomeMarkers.icon({icon: 'play', markerColor: 'darkred', iconColor: 'green' });
+ marker_start_icons[7] = L.AwesomeMarkers.icon({icon: 'play', markerColor: 'darkgreen', iconColor: 'green' });
+ marker_start_icons[8] = L.AwesomeMarkers.icon({icon: 'play', markerColor: 'darkpuple', iconColor: 'green' });
+
+ marker_finish_icons[0] = L.AwesomeMarkers.icon({icon: 'stop', markerColor: 'blue', iconColor: 'red' });
+ marker_finish_icons[1] = L.AwesomeMarkers.icon({icon: 'stop', markerColor: 'red', iconColor: 'darkred' });
+ marker_finish_icons[2] = L.AwesomeMarkers.icon({icon: 'stop', markerColor: 'orange', iconColor: 'red' });
+ marker_finish_icons[3] = L.AwesomeMarkers.icon({icon: 'stop', markerColor: 'green', iconColor: 'red' });
+ marker_finish_icons[4] = L.AwesomeMarkers.icon({icon: 'stop', markerColor: 'purple', iconColor: 'red' });
+ marker_finish_icons[5] = L.AwesomeMarkers.icon({icon: 'stop', markerColor: 'cadetblue', iconColor: 'red' });
+ marker_finish_icons[6] = L.AwesomeMarkers.icon({icon: 'stop', markerColor: 'darkred', iconColor: 'red' });
+ marker_finish_icons[7] = L.AwesomeMarkers.icon({icon: 'stop', markerColor: 'darkgreen', iconColor: 'red' });
+ marker_finish_icons[8] = L.AwesomeMarkers.icon({icon: 'stop', markerColor: 'darkpuple', iconColor: 'red' });
+
+ marker_icons[0] = L.AwesomeMarkers.icon({icon: 'user', markerColor: 'blue' });
+ marker_icons[1] = L.AwesomeMarkers.icon({icon: 'user', markerColor: 'red' });
+ marker_icons[2] = L.AwesomeMarkers.icon({icon: 'user', markerColor: 'orange' });
+ marker_icons[3] = L.AwesomeMarkers.icon({icon: 'user', markerColor: 'green' });
+ marker_icons[4] = L.AwesomeMarkers.icon({icon: 'user', markerColor: 'purple' });
+ marker_icons[5] = L.AwesomeMarkers.icon({icon: 'user', markerColor: 'cadetblue' });
+ marker_icons[6] = L.AwesomeMarkers.icon({icon: 'user', markerColor: 'darkred' });
+ marker_icons[7] = L.AwesomeMarkers.icon({icon: 'user', markerColor: 'darkgreen' });
+ marker_icons[8] = L.AwesomeMarkers.icon({icon: 'user', markerColor: 'darkpuple' });
+
+ //set checkbox
+ if (show_markers == '1') {
+ //hideMarkers();
+ //$('#show_markers').prop('checked',false);
+ $('#show_markers').removeClass( "btn-default" ).addClass( "btn-primary" ).addClass( "active" );
+ }
+
+ mymap = L.map('mapid').setView([52.52, 13.44], 11);
+
+ L.tileLayer( 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ attribution: '© OpenStreetMap',
+ subdomains: ['a','b','c']
+ }).addTo( mymap );
+ mapMarkers();
+}
+
+function setDefaultZoom()
+{
+ console.log("setDefaultZoom : INIT");
+ setTimeout(function() {
+ default_zoom = mymap.getZoom();
+ default_center = mymap.getCenter();
+ }, 2000);
+}
+
+function mapMarkers()
+{
+ console.log("mapMarkers : INIT");
+ getMarkers();
+}
+
+function getMarkers()
+{
+ console.log("getMarkers : INIT");
+ //ajax call to get list of markers
+ $.ajax({
+ url: 'rpc.php',
+ data: {
+ 'dateFrom': dateFrom.format('YYYY-MM-DD'),
+ 'dateTo': dateTo.format('YYYY-MM-DD'),
+ 'accuracy': accuracy,
+ //'trackerID' : trackerID,
+ //'epoc': time(),
+ 'action': 'getMarkers'
+ },
+ type: 'GET',
+ dataType: 'json',
+ beforeSend: function(xhr)
+ {
+ $('#mapid').css('filter','blur(5px)');
+ },
+ success: function(data, status)
+ {
+ if (data.status) {
+ jsonMarkers = JSON.parse(data.markers);
+ updateTrackerIDs(jsonMarkers);
+ if (drawMap(jsonMarkers)) { $('#mapid').css('filter','blur(0px)'); }
+ } else {
+ console.log("getMarkers : ERROR Status : " + status);
+ console.log("getMarkers : ERROR Data : ");
+ console.log(data);
+ }
+ },
+ error: function(xhr, desc, err) {
+ console.log(xhr);
+ console.log("getMarkers : ERROR Details: " + desc + "\nError:" + err);
+ }
+ });
+}
+
+function updateTrackerIDs(_tid_markers)
+{
+ console.log("updateTrackerIDs : INIT");
+ try {
+ $("#trackerID_selector option[value!='" + window.default_trackerID + "']").each(function() {
+ $(this).remove();
+ });
+ if (typeof _tid_markers != "undefined" && _tid_markers != null) {
+ trackerIDs = Object.keys(_tid_markers);
+ $.each(trackerIDs, function( index, value ) {
+ $('#trackerID_selector').append($('