aboutsummaryrefslogtreecommitdiff
path: root/web/app
diff options
context:
space:
mode:
Diffstat (limited to 'web/app')
-rw-r--r--web/app/Application.js17
-rw-r--r--web/app/GeofenceConverter.js9
-rw-r--r--web/app/Style.js3
-rw-r--r--web/app/controller/Root.js8
-rw-r--r--web/app/model/ReportStop.js4
-rw-r--r--web/app/model/ReportSummary.js8
-rw-r--r--web/app/model/ReportTrip.js8
-rw-r--r--web/app/store/AlarmTypes.js34
-rw-r--r--web/app/store/AllMaintenances.js2
-rw-r--r--web/app/store/CommonUserAttributes.js4
-rw-r--r--web/app/store/KnownCommands.js9
-rw-r--r--web/app/store/Maintenances.js2
-rw-r--r--web/app/store/MapTypes.js3
-rw-r--r--web/app/view/DeviceMenu.js2
-rw-r--r--web/app/view/DeviceMenuController.js4
-rw-r--r--web/app/view/EventsController.js15
-rw-r--r--web/app/view/Report.js5
-rw-r--r--web/app/view/ReportController.js53
-rw-r--r--web/app/view/SettingsMenu.js2
-rw-r--r--web/app/view/SettingsMenuController.js13
-rw-r--r--web/app/view/StateController.js5
-rw-r--r--web/app/view/Statistics.js4
-rw-r--r--web/app/view/TouchFix62.js48
-rw-r--r--web/app/view/UnescapedTextAreaField.js29
-rw-r--r--web/app/view/UnescapedTextField.js29
-rw-r--r--web/app/view/dialog/Attribute.js5
-rw-r--r--web/app/view/dialog/Calendar.js5
-rw-r--r--web/app/view/dialog/ComputedAttribute.js9
-rw-r--r--web/app/view/dialog/Device.js13
-rw-r--r--web/app/view/dialog/Driver.js8
-rw-r--r--web/app/view/dialog/Geofence.js7
-rw-r--r--web/app/view/dialog/Group.js5
-rw-r--r--web/app/view/dialog/Maintenance.js5
-rw-r--r--web/app/view/dialog/Notification.js21
-rw-r--r--web/app/view/dialog/NotificationController.js26
-rw-r--r--web/app/view/dialog/SavedCommand.js7
-rw-r--r--web/app/view/dialog/SendCommand.js2
-rw-r--r--web/app/view/dialog/Server.js12
-rw-r--r--web/app/view/dialog/User.js13
-rw-r--r--web/app/view/edit/DevicesController.js17
-rw-r--r--web/app/view/edit/Drivers.js2
-rw-r--r--web/app/view/edit/Groups.js2
-rw-r--r--web/app/view/edit/GroupsController.js4
-rw-r--r--web/app/view/edit/Notifications.js17
-rw-r--r--web/app/view/edit/SavedCommands.js2
-rw-r--r--web/app/view/edit/Users.js2
-rw-r--r--web/app/view/edit/UsersController.js4
-rw-r--r--web/app/view/map/BaseMap.js40
-rw-r--r--web/app/view/map/GeofenceMap.js5
-rw-r--r--web/app/view/map/MapController.js3
-rw-r--r--web/app/view/map/MapMarkerController.js62
-rw-r--r--web/app/view/permissions/SavedCommands.js2
52 files changed, 484 insertions, 136 deletions
diff --git a/web/app/Application.js b/web/app/Application.js
index d0b6713..91bdc58 100644
--- a/web/app/Application.js
+++ b/web/app/Application.js
@@ -21,7 +21,8 @@ Ext.define('Traccar.Application', {
requires: [
'Traccar.Style',
- 'Traccar.AttributeFormatter'
+ 'Traccar.AttributeFormatter',
+ 'Traccar.view.TouchFix62'
],
models: [
@@ -51,6 +52,7 @@ Ext.define('Traccar.Application', {
'Devices',
'AllGroups',
'AllDevices',
+ 'AlarmTypes',
'Positions',
'LatestPositions',
'EventPositions',
@@ -148,6 +150,19 @@ Ext.define('Traccar.Application', {
}
},
+ updateNotificationToken: function (token) {
+ var attributes = Ext.clone(this.user.get('attributes'));
+ if (!attributes.notificationTokens || attributes.notificationTokens.indexOf(token) < 0) {
+ if (!attributes.notificationTokens) {
+ attributes.notificationTokens = token;
+ } else {
+ attributes.notificationTokens += ',' + token;
+ }
+ this.user.set('attributes', attributes);
+ this.user.save();
+ }
+ },
+
setUser: function (data) {
var reader = Ext.create('Ext.data.reader.Json', {
model: 'Traccar.model.User'
diff --git a/web/app/GeofenceConverter.js b/web/app/GeofenceConverter.js
index 4891e7b..688f223 100644
--- a/web/app/GeofenceConverter.js
+++ b/web/app/GeofenceConverter.js
@@ -45,7 +45,7 @@ Ext.define('Traccar.GeofenceConverter', {
resolutionAtEquator = mapView.getResolution();
pointResolution = ol.proj.getPointResolution(projection, resolutionAtEquator, center);
resolutionFactor = resolutionAtEquator / pointResolution;
- radius = Number(coordinates[2]) / ol.proj.METERS_PER_UNIT.m * resolutionFactor;
+ radius = Number(coordinates[2]) / ol.proj.Units.METERS_PER_UNIT.m * resolutionFactor;
geometry = new ol.geom.Circle(center, radius);
}
}
@@ -68,15 +68,14 @@ Ext.define('Traccar.GeofenceConverter', {
},
geometryToWkt: function (projection, geometry) {
- var result, i, center, radius, edgeCoordinate, earthSphere, groundRadius, points;
+ var result, i, center, radius, edgeCoordinate, groundRadius, points;
if (geometry instanceof ol.geom.Circle) {
center = geometry.getCenter();
radius = geometry.getRadius();
edgeCoordinate = [center[0] + radius, center[1]];
center = ol.proj.transform(center, projection, 'EPSG:4326');
- earthSphere = new ol.Sphere(6378137);
- groundRadius = earthSphere.haversineDistance(center,
- ol.proj.transform(edgeCoordinate, projection, 'EPSG:4326'));
+ groundRadius = ol.sphere.getDistance(
+ center, ol.proj.transform(edgeCoordinate, projection, 'EPSG:4326'), 6378137);
result = 'CIRCLE (';
result += center[1] + ' ' + center[0] + ', ';
result += groundRadius.toFixed(1) + ')';
diff --git a/web/app/Style.js b/web/app/Style.js
index 47c49a9..371e05a 100644
--- a/web/app/Style.js
+++ b/web/app/Style.js
@@ -81,6 +81,9 @@ Ext.define('Traccar.Style', {
mapGeofenceWidth: 5,
mapGeofenceRadius: 9,
+ mapAnimateMarkerDuration: 2000,
+ mapAnimateMarkerTimeout: 40,
+
coordinatePrecision: 6,
numberPrecision: 2,
diff --git a/web/app/controller/Root.js b/web/app/controller/Root.js
index 0edc049..7c0345a 100644
--- a/web/app/controller/Root.js
+++ b/web/app/controller/Root.js
@@ -109,6 +109,14 @@ Ext.define('Traccar.controller.Root', {
loadApp: function () {
var attribution, eventId;
+
+ if (window.webkit && window.webkit.messageHandlers.appInterface) {
+ window.webkit.messageHandlers.appInterface.postMessage('login');
+ }
+ if (window.appInterface) {
+ window.appInterface.postMessage('login');
+ }
+
Ext.getStore('Groups').load();
Ext.getStore('Drivers').load();
Ext.getStore('Geofences').load();
diff --git a/web/app/model/ReportStop.js b/web/app/model/ReportStop.js
index 1867f73..9aaa58b 100644
--- a/web/app/model/ReportStop.js
+++ b/web/app/model/ReportStop.js
@@ -34,6 +34,10 @@ Ext.define('Traccar.model.ReportStop', {
type: 'date',
dateFormat: 'c'
}, {
+ name: 'startOdometer',
+ type: 'float',
+ convert: Traccar.AttributeFormatter.getConverter('distance')
+ }, {
name: 'address',
type: 'string'
}, {
diff --git a/web/app/model/ReportSummary.js b/web/app/model/ReportSummary.js
index 4bc7873..dcc0365 100644
--- a/web/app/model/ReportSummary.js
+++ b/web/app/model/ReportSummary.js
@@ -39,6 +39,14 @@ Ext.define('Traccar.model.ReportSummary', {
type: 'float',
convert: Traccar.AttributeFormatter.getConverter('distance')
}, {
+ name: 'startOdometer',
+ type: 'float',
+ convert: Traccar.AttributeFormatter.getConverter('distance')
+ }, {
+ name: 'endOdometer',
+ type: 'float',
+ convert: Traccar.AttributeFormatter.getConverter('distance')
+ }, {
name: 'engineHours',
type: 'int'
}, {
diff --git a/web/app/model/ReportTrip.js b/web/app/model/ReportTrip.js
index 9004b86..9d45fc8 100644
--- a/web/app/model/ReportTrip.js
+++ b/web/app/model/ReportTrip.js
@@ -39,6 +39,14 @@ Ext.define('Traccar.model.ReportTrip', {
type: 'float',
convert: Traccar.AttributeFormatter.getConverter('distance')
}, {
+ name: 'startOdometer',
+ type: 'float',
+ convert: Traccar.AttributeFormatter.getConverter('distance')
+ }, {
+ name: 'endOdometer',
+ type: 'float',
+ convert: Traccar.AttributeFormatter.getConverter('distance')
+ }, {
name: 'duration',
type: 'int'
}, {
diff --git a/web/app/store/AlarmTypes.js b/web/app/store/AlarmTypes.js
new file mode 100644
index 0000000..1ee7ffe
--- /dev/null
+++ b/web/app/store/AlarmTypes.js
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+Ext.define('Traccar.store.AlarmTypes', {
+ extend: 'Ext.data.Store',
+ fields: ['key', 'name'],
+
+ data: (function () {
+ var key, items = [];
+ for (key in Strings) {
+ if (Strings.hasOwnProperty(key) && key.lastIndexOf('alarm', 0) === 0) {
+ items.push({
+ key: key.charAt(5).toLowerCase() + key.slice(6),
+ name: Strings[key]
+ });
+ }
+ }
+ return items;
+ })()
+});
diff --git a/web/app/store/AllMaintenances.js b/web/app/store/AllMaintenances.js
index 745b5d6..8435ad4 100644
--- a/web/app/store/AllMaintenances.js
+++ b/web/app/store/AllMaintenances.js
@@ -22,7 +22,7 @@ Ext.define('Traccar.store.AllMaintenances', {
proxy: {
type: 'rest',
- url: 'api/maintenances',
+ url: 'api/maintenance',
extraParams: {
all: true
}
diff --git a/web/app/store/CommonUserAttributes.js b/web/app/store/CommonUserAttributes.js
index 0ee9e16..a8e9282 100644
--- a/web/app/store/CommonUserAttributes.js
+++ b/web/app/store/CommonUserAttributes.js
@@ -63,8 +63,8 @@ Ext.define('Traccar.store.CommonUserAttributes', {
name: Strings.attributeUiDisableCalendars,
valueType: 'boolean'
}, {
- key: 'ui.disableMaintenances',
- name: Strings.attributeUiDisableMaintenances,
+ key: 'ui.disableMaintenance',
+ name: Strings.attributeUiDisableMaintenance,
valueType: 'boolean'
}, {
key: 'ui.hidePositionAttributes',
diff --git a/web/app/store/KnownCommands.js b/web/app/store/KnownCommands.js
index 8bf2e24..1e82f2b 100644
--- a/web/app/store/KnownCommands.js
+++ b/web/app/store/KnownCommands.js
@@ -39,7 +39,7 @@ Ext.define('Traccar.store.KnownCommands', {
}, {
type: 'setTimezone',
parameters: [{
- key: 'timezoneName',
+ key: 'timezone',
name: Strings.commandTimezone,
valueType: 'string',
dataType: 'timezone'
@@ -56,6 +56,13 @@ Ext.define('Traccar.store.KnownCommands', {
valueType: 'string'
}]
}, {
+ type: 'message',
+ parameters: [{
+ key: 'message',
+ name: Strings.commandMessage,
+ valueType: 'string'
+ }]
+ }, {
type: 'sendUssd',
parameters: [{
key: 'phone',
diff --git a/web/app/store/Maintenances.js b/web/app/store/Maintenances.js
index 43d6ba5..a7aa4a0 100644
--- a/web/app/store/Maintenances.js
+++ b/web/app/store/Maintenances.js
@@ -22,7 +22,7 @@ Ext.define('Traccar.store.Maintenances', {
proxy: {
type: 'rest',
- url: 'api/maintenances',
+ url: 'api/maintenance',
writer: {
writeAllFields: true
}
diff --git a/web/app/store/MapTypes.js b/web/app/store/MapTypes.js
index 3d32243..88d54bf 100644
--- a/web/app/store/MapTypes.js
+++ b/web/app/store/MapTypes.js
@@ -49,5 +49,8 @@ Ext.define('Traccar.store.MapTypes', {
}, {
key: 'custom',
name: Strings.mapCustom
+ }, {
+ key: 'customArcgis',
+ name: Strings.mapCustomArcgis
}]
});
diff --git a/web/app/view/DeviceMenu.js b/web/app/view/DeviceMenu.js
index e4623b9..06b272a 100644
--- a/web/app/view/DeviceMenu.js
+++ b/web/app/view/DeviceMenu.js
@@ -57,7 +57,7 @@ Ext.define('Traccar.view.DeviceMenu', {
handler: 'onCommandsClick',
reference: 'menuCommandsButton'
}, {
- text: Strings.sharedMaintenances,
+ text: Strings.sharedMaintenance,
glyph: 'xf0ad@FontAwesome',
handler: 'onMaintenancesClick',
reference: 'menuMaintenancesButton'
diff --git a/web/app/view/DeviceMenuController.js b/web/app/view/DeviceMenuController.js
index 9355571..830ea7e 100644
--- a/web/app/view/DeviceMenuController.js
+++ b/web/app/view/DeviceMenuController.js
@@ -40,7 +40,7 @@ Ext.define('Traccar.view.DeviceMenuController', {
this.lookupReference('menuDeviceAccumulatorsButton').setHidden(
!Traccar.app.getUser().get('administrator') && Traccar.app.getUser().get('userLimit') === 0 || Traccar.app.getVehicleFeaturesDisabled());
this.lookupReference('menuMaintenancesButton').setHidden(
- Traccar.app.getVehicleFeaturesDisabled() || Traccar.app.getBooleanAttributePreference('ui.disableMaintenances'));
+ Traccar.app.getVehicleFeaturesDisabled() || Traccar.app.getBooleanAttributePreference('ui.disableMaintenance'));
},
onGeofencesClick: function () {
@@ -110,7 +110,7 @@ Ext.define('Traccar.view.DeviceMenuController', {
onMaintenancesClick: function () {
Ext.create('Traccar.view.BaseWindow', {
- title: Strings.sharedMaintenances,
+ title: Strings.sharedMaintenance,
items: {
xtype: 'linkMaintenancesView',
baseObjectName: 'deviceId',
diff --git a/web/app/view/EventsController.js b/web/app/view/EventsController.js
index 8eb4363..e11b4ff 100644
--- a/web/app/view/EventsController.js
+++ b/web/app/view/EventsController.js
@@ -24,7 +24,11 @@ Ext.define('Traccar.view.EventsController', {
listen: {
controller: {
'*': {
- deselectevent: 'deselectEvent'
+ selectdevice: 'deselectEvent',
+ selectreport: 'deselectEvent'
+ },
+ 'map': {
+ deselectfeature: 'deselectFeature'
}
},
store: {
@@ -79,7 +83,14 @@ Ext.define('Traccar.view.EventsController', {
Traccar.app.showEvents(false);
},
- deselectEvent: function () {
+
+ deselectEvent: function (object) {
+ if (object) {
+ this.deselectFeature();
+ }
+ },
+
+ deselectFeature: function () {
this.getView().getSelectionModel().deselectAll();
},
diff --git a/web/app/view/Report.js b/web/app/view/Report.js
index f77cfca..deedb74 100644
--- a/web/app/view/Report.js
+++ b/web/app/view/Report.js
@@ -63,6 +63,11 @@ Ext.define('Traccar.view.Report', {
disabled: true,
handler: 'onReportClick'
}, {
+ text: Strings.reportEmail,
+ reference: 'emailButton',
+ disabled: true,
+ handler: 'onReportClick'
+ }, {
text: Strings.reportClear,
handler: 'onClearClick'
}]
diff --git a/web/app/view/ReportController.js b/web/app/view/ReportController.js
index 72e7e37..2f10cde 100644
--- a/web/app/view/ReportController.js
+++ b/web/app/view/ReportController.js
@@ -32,12 +32,13 @@ Ext.define('Traccar.view.ReportController', {
listen: {
controller: {
'*': {
- selectdevice: 'selectDevice',
- showsingleevent: 'showSingleEvent',
- deselectfeature: 'deselectFeature'
+ selectdevice: 'deselectReport',
+ selectevent: 'deselectReport',
+ showsingleevent: 'showSingleEvent'
},
'map': {
- selectreport: 'selectReport'
+ selectreport: 'selectReport',
+ deselectfeature: 'deselectFeature'
}
},
global: {
@@ -145,6 +146,7 @@ Ext.define('Traccar.view.ReportController', {
disabled = !reportType || !devices || !time || this.reportProgress;
this.lookupReference('showButton').setDisabled(disabled);
this.lookupReference('exportButton').setDisabled(reportType === 'chart' || disabled);
+ this.lookupReference('emailButton').setDisabled(reportType === 'chart' || disabled);
},
onReportClick: function (button) {
@@ -187,14 +189,15 @@ Ext.define('Traccar.view.ReportController', {
to: to.toISOString()
}
});
- } else if (button.reference === 'exportButton') {
+ } else {
url = this.getGrid().getStore().getProxy().url;
- this.downloadFile(url, {
+ this.excelReport(url, {
deviceId: this.deviceId,
groupId: this.groupId,
type: this.eventType,
from: Ext.Date.format(from, 'c'),
- to: Ext.Date.format(to, 'c')
+ to: Ext.Date.format(to, 'c'),
+ mail: button.reference === 'emailButton'
});
}
}
@@ -229,12 +232,6 @@ Ext.define('Traccar.view.ReportController', {
}
},
- selectDevice: function (device) {
- if (device) {
- this.getGrid().getSelectionModel().deselectAll();
- }
- },
-
selectReport: function (object) {
var positionRelated, reportType = this.lookupReference('reportTypeField').getValue();
if (object instanceof Traccar.model.Position) {
@@ -249,6 +246,12 @@ Ext.define('Traccar.view.ReportController', {
}
},
+ deselectReport: function (object) {
+ if (object) {
+ this.deselectFeature();
+ }
+ },
+
deselectFeature: function () {
if (this.lookupReference('reportTypeField').getValue() !== 'trips') {
this.getGrid().getSelectionModel().deselectAll();
@@ -371,7 +374,7 @@ Ext.define('Traccar.view.ReportController', {
});
},
- downloadFile: function (requestUrl, requestParams) {
+ excelReport: function (requestUrl, requestParams) {
Ext.Ajax.request({
url: requestUrl,
method: 'GET',
@@ -384,7 +387,7 @@ Ext.define('Traccar.view.ReportController', {
scope: this,
callback: function (options, success, response) {
var disposition, filename, type, blob, url, downloadUrl;
- if (success) {
+ if (success && !requestParams.mail) {
disposition = response.getResponseHeader('Content-Disposition');
filename = disposition.slice(disposition.indexOf('=') + 1, disposition.length);
type = response.getResponseHeader('Content-Type');
@@ -539,6 +542,14 @@ Ext.define('Traccar.view.ReportController', {
dataIndex: 'distance',
renderer: Traccar.AttributeFormatter.getFormatter('distance')
}, {
+ text: Strings.reportStartOdometer,
+ dataIndex: 'startOdometer',
+ renderer: Traccar.AttributeFormatter.getFormatter('distance')
+ }, {
+ text: Strings.reportEndOdometer,
+ dataIndex: 'endOdometer',
+ renderer: Traccar.AttributeFormatter.getFormatter('distance')
+ }, {
text: Strings.reportAverageSpeed,
dataIndex: 'averageSpeed',
renderer: Traccar.AttributeFormatter.getFormatter('speed')
@@ -566,6 +577,10 @@ Ext.define('Traccar.view.ReportController', {
xtype: 'datecolumn',
renderer: Traccar.AttributeFormatter.getFormatter('startTime')
}, {
+ text: Strings.reportStartOdometer,
+ dataIndex: 'startOdometer',
+ renderer: Traccar.AttributeFormatter.getFormatter('distance')
+ }, {
text: Strings.reportStartAddress,
dataIndex: 'startAddress',
renderer: Traccar.AttributeFormatter.getFormatter('address')
@@ -575,6 +590,10 @@ Ext.define('Traccar.view.ReportController', {
xtype: 'datecolumn',
renderer: Traccar.AttributeFormatter.getFormatter('endTime')
}, {
+ text: Strings.reportEndOdometer,
+ dataIndex: 'endOdometer',
+ renderer: Traccar.AttributeFormatter.getFormatter('distance')
+ }, {
text: Strings.reportEndAddress,
dataIndex: 'endAddress',
renderer: Traccar.AttributeFormatter.getFormatter('address')
@@ -614,6 +633,10 @@ Ext.define('Traccar.view.ReportController', {
xtype: 'datecolumn',
renderer: Traccar.AttributeFormatter.getFormatter('startTime')
}, {
+ text: Strings.positionOdometer,
+ dataIndex: 'startOdometer',
+ renderer: Traccar.AttributeFormatter.getFormatter('distance')
+ }, {
text: Strings.positionAddress,
dataIndex: 'address',
renderer: Traccar.AttributeFormatter.getFormatter('address')
diff --git a/web/app/view/SettingsMenu.js b/web/app/view/SettingsMenu.js
index 309133c..0a81d52 100644
--- a/web/app/view/SettingsMenu.js
+++ b/web/app/view/SettingsMenu.js
@@ -98,7 +98,7 @@ Ext.define('Traccar.view.SettingsMenu', {
reference: 'settingsCommandsButton'
}, {
hidden: true,
- text: Strings.sharedMaintenances,
+ text: Strings.sharedMaintenance,
glyph: 'xf0ad@FontAwesome',
handler: 'onMaintenancesClick',
reference: 'settingsMaintenancesButton'
diff --git a/web/app/view/SettingsMenuController.js b/web/app/view/SettingsMenuController.js
index 37e0634..c8018f6 100644
--- a/web/app/view/SettingsMenuController.js
+++ b/web/app/view/SettingsMenuController.js
@@ -37,14 +37,15 @@ Ext.define('Traccar.view.SettingsMenuController', {
],
init: function () {
- var admin, manager, readonly, deviceReadonly;
+ var admin, manager, readonly;
admin = Traccar.app.getUser().get('administrator');
manager = Traccar.app.getUser().get('userLimit') !== 0;
readonly = Traccar.app.getPreference('readonly', false);
- deviceReadonly = Traccar.app.getUser().get('deviceReadonly');
if (admin) {
this.lookupReference('settingsServerButton').setHidden(false);
this.lookupReference('settingsStatisticsButton').setHidden(false);
+ this.lookupReference('settingsComputedAttributesButton').setHidden(
+ Traccar.app.getBooleanAttributePreference('ui.disableComputedAttributes'));
}
if (admin || manager) {
this.lookupReference('settingsUsersButton').setHidden(false);
@@ -60,11 +61,7 @@ Ext.define('Traccar.view.SettingsMenuController', {
Traccar.app.getVehicleFeaturesDisabled() || Traccar.app.getBooleanAttributePreference('ui.disableDrivers'));
this.lookupReference('settingsCommandsButton').setHidden(Traccar.app.getPreference('limitCommands', false));
this.lookupReference('settingsMaintenancesButton').setHidden(
- Traccar.app.getVehicleFeaturesDisabled() || Traccar.app.getBooleanAttributePreference('ui.disableMaintenances'));
- }
- if (admin || !deviceReadonly && !readonly) {
- this.lookupReference('settingsComputedAttributesButton').setHidden(
- Traccar.app.getBooleanAttributePreference('ui.disableComputedAttributes'));
+ Traccar.app.getVehicleFeaturesDisabled() || Traccar.app.getBooleanAttributePreference('ui.disableMaintenance'));
}
},
@@ -166,7 +163,7 @@ Ext.define('Traccar.view.SettingsMenuController', {
onMaintenancesClick: function () {
Ext.create('Traccar.view.BaseWindow', {
- title: Strings.sharedMaintenances,
+ title: Strings.sharedMaintenance,
items: {
xtype: 'maintenancesView'
}
diff --git a/web/app/view/StateController.js b/web/app/view/StateController.js
index 6363fc9..1ab1aa2 100644
--- a/web/app/view/StateController.js
+++ b/web/app/view/StateController.js
@@ -32,7 +32,8 @@ Ext.define('Traccar.view.StateController', {
controller: {
'*': {
selectdevice: 'selectDevice',
- selectreport: 'selectReport',
+ selectreport: 'selectPosition',
+ selectevent: 'selectPosition',
deselectfeature: 'deselectFeature'
}
},
@@ -167,7 +168,7 @@ Ext.define('Traccar.view.StateController', {
}
},
- selectReport: function (position) {
+ selectPosition: function (position) {
if (position instanceof Traccar.model.Position) {
this.deviceId = null;
this.position = position;
diff --git a/web/app/view/Statistics.js b/web/app/view/Statistics.js
index f033e03..af199a0 100644
--- a/web/app/view/Statistics.js
+++ b/web/app/view/Statistics.js
@@ -77,10 +77,10 @@ Ext.define('Traccar.view.Statistics', {
text: Strings.statisticsMessagesStored,
dataIndex: 'messagesStored'
}, {
- text: Strings.notificationMail,
+ text: Strings.notificatorMail,
dataIndex: 'mailSent'
}, {
- text: Strings.notificationSms,
+ text: Strings.notificatorSms,
dataIndex: 'smsSent'
}, {
text: Strings.statisticsGeocoder,
diff --git a/web/app/view/TouchFix62.js b/web/app/view/TouchFix62.js
new file mode 100644
index 0000000..300d4f7
--- /dev/null
+++ b/web/app/view/TouchFix62.js
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Workaround for bug in ExtJs 6.2.0.
+ * Resolved in current yet unreleased version
+ */
+
+Ext.define('Traccar.view.TouchFix62', {
+ override: 'Ext.dom.Element'
+},
+function () {
+ var additiveEvents = this.prototype.additiveEvents,
+ eventMap = this.prototype.eventMap;
+ if (Ext.supports.TouchEvents && Ext.firefoxVersion >= 52 && Ext.os.is.Desktop) {
+ eventMap['touchstart'] = 'mousedown';
+ eventMap['touchmove'] = 'mousemove';
+ eventMap['touchend'] = 'mouseup';
+ eventMap['touchcancel'] = 'mouseup';
+ eventMap['click'] = 'click';
+ eventMap['dblclick'] = 'dblclick';
+ additiveEvents['mousedown'] = 'mousedown';
+ additiveEvents['mousemove'] = 'mousemove';
+ additiveEvents['mouseup'] = 'mouseup';
+ additiveEvents['touchstart'] = 'touchstart';
+ additiveEvents['touchmove'] = 'touchmove';
+ additiveEvents['touchend'] = 'touchend';
+ additiveEvents['touchcancel'] = 'touchcancel';
+ additiveEvents['pointerdown'] = 'mousedown';
+ additiveEvents['pointermove'] = 'mousemove';
+ additiveEvents['pointerup'] = 'mouseup';
+ additiveEvents['pointercancel'] = 'mouseup';
+ }
+});
diff --git a/web/app/view/UnescapedTextAreaField.js b/web/app/view/UnescapedTextAreaField.js
new file mode 100644
index 0000000..5de267c
--- /dev/null
+++ b/web/app/view/UnescapedTextAreaField.js
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+Ext.define('Traccar.view.UnescapedTextAreaField', {
+ extend: 'Ext.form.field.TextArea',
+ xtype: 'unescapedTextAreaField',
+
+ initComponent: function () {
+ this.callParent();
+ this.on('change', this.onValueChange);
+ },
+
+ onValueChange: function (field, newValue) {
+ field.setValue(Ext.String.htmlDecode(newValue));
+ }
+});
diff --git a/web/app/view/UnescapedTextField.js b/web/app/view/UnescapedTextField.js
new file mode 100644
index 0000000..3b1b279
--- /dev/null
+++ b/web/app/view/UnescapedTextField.js
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+Ext.define('Traccar.view.UnescapedTextField', {
+ extend: 'Ext.form.field.Text',
+ xtype: 'unescapedTextField',
+
+ initComponent: function () {
+ this.callParent();
+ this.on('change', this.onValueChange);
+ },
+
+ onValueChange: function (field, newValue) {
+ field.setValue(Ext.String.htmlDecode(newValue));
+ }
+});
diff --git a/web/app/view/dialog/Attribute.js b/web/app/view/dialog/Attribute.js
index 2a45897..a85cad0 100644
--- a/web/app/view/dialog/Attribute.js
+++ b/web/app/view/dialog/Attribute.js
@@ -21,7 +21,8 @@ Ext.define('Traccar.view.dialog.Attribute', {
requires: [
'Traccar.view.dialog.AttributeController',
'Traccar.view.ColorPicker',
- 'Traccar.view.CustomNumberField'
+ 'Traccar.view.CustomNumberField',
+ 'Traccar.view.UnescapedTextField'
],
controller: 'attribute',
@@ -33,7 +34,7 @@ Ext.define('Traccar.view.dialog.Attribute', {
validitychange: 'onValidityChange'
},
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
reference: 'nameTextField',
name: 'name',
allowBlank: false,
diff --git a/web/app/view/dialog/Calendar.js b/web/app/view/dialog/Calendar.js
index 9880d4e..5f00a8b 100644
--- a/web/app/view/dialog/Calendar.js
+++ b/web/app/view/dialog/Calendar.js
@@ -20,7 +20,8 @@ Ext.define('Traccar.view.dialog.Calendar', {
extend: 'Traccar.view.dialog.BaseEdit',
requires: [
- 'Traccar.view.dialog.CalendarController'
+ 'Traccar.view.dialog.CalendarController',
+ 'Traccar.view.UnescapedTextField'
],
controller: 'calendar',
@@ -32,7 +33,7 @@ Ext.define('Traccar.view.dialog.Calendar', {
xtype: 'fieldset',
title: Strings.sharedRequired,
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'name',
fieldLabel: Strings.sharedName,
allowBlank: false
diff --git a/web/app/view/dialog/ComputedAttribute.js b/web/app/view/dialog/ComputedAttribute.js
index ef6269b..adae7f7 100644
--- a/web/app/view/dialog/ComputedAttribute.js
+++ b/web/app/view/dialog/ComputedAttribute.js
@@ -20,7 +20,9 @@ Ext.define('Traccar.view.dialog.ComputedAttribute', {
extend: 'Traccar.view.dialog.BaseEdit',
requires: [
- 'Traccar.view.dialog.ComputedAttributeController'
+ 'Traccar.view.dialog.ComputedAttributeController',
+ 'Traccar.view.UnescapedTextField',
+ 'Traccar.view.UnescapedTextAreaField'
],
controller: 'computedAttribute',
@@ -29,7 +31,7 @@ Ext.define('Traccar.view.dialog.ComputedAttribute', {
items: {
xtype: 'form',
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'description',
fieldLabel: Strings.sharedDescription
}, {
@@ -43,7 +45,8 @@ Ext.define('Traccar.view.dialog.ComputedAttribute', {
change: 'onAttributeChange'
}
}, {
- xtype: 'textareafield',
+ xtype: 'unescapedTextAreaField',
+ reference: 'expressionField',
name: 'expression',
fieldLabel: Strings.sharedExpression,
allowBlank: false
diff --git a/web/app/view/dialog/Device.js b/web/app/view/dialog/Device.js
index 50a5e79..60a8f71 100644
--- a/web/app/view/dialog/Device.js
+++ b/web/app/view/dialog/Device.js
@@ -20,7 +20,8 @@ Ext.define('Traccar.view.dialog.Device', {
requires: [
'Traccar.view.ClearableComboBox',
- 'Traccar.view.dialog.DeviceController'
+ 'Traccar.view.dialog.DeviceController',
+ 'Traccar.view.UnescapedTextField'
],
controller: 'device',
@@ -32,12 +33,12 @@ Ext.define('Traccar.view.dialog.Device', {
xtype: 'fieldset',
title: Strings.sharedRequired,
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'name',
fieldLabel: Strings.sharedName,
allowBlank: false
}, {
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'uniqueId',
fieldLabel: Strings.deviceIdentifier,
allowBlank: false
@@ -56,15 +57,15 @@ Ext.define('Traccar.view.dialog.Device', {
displayField: 'name',
valueField: 'id'
}, {
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'phone',
fieldLabel: Strings.sharedPhone
}, {
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'model',
fieldLabel: Strings.deviceModel
}, {
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'contact',
fieldLabel: Strings.deviceContact
}, {
diff --git a/web/app/view/dialog/Driver.js b/web/app/view/dialog/Driver.js
index b67e182..9b1c17b 100644
--- a/web/app/view/dialog/Driver.js
+++ b/web/app/view/dialog/Driver.js
@@ -19,6 +19,10 @@
Ext.define('Traccar.view.dialog.Driver', {
extend: 'Traccar.view.dialog.BaseEdit',
+ requires: [
+ 'Traccar.view.UnescapedTextField'
+ ],
+
title: Strings.sharedDriver,
items: {
@@ -27,12 +31,12 @@ Ext.define('Traccar.view.dialog.Driver', {
xtype: 'fieldset',
title: Strings.sharedRequired,
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'name',
fieldLabel: Strings.sharedName,
allowBlank: false
}, {
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'uniqueId',
fieldLabel: Strings.deviceIdentifier,
allowBlank: false
diff --git a/web/app/view/dialog/Geofence.js b/web/app/view/dialog/Geofence.js
index 65c32c0..1e22cd7 100644
--- a/web/app/view/dialog/Geofence.js
+++ b/web/app/view/dialog/Geofence.js
@@ -20,7 +20,8 @@ Ext.define('Traccar.view.dialog.Geofence', {
requires: [
'Traccar.view.ClearableComboBox',
- 'Traccar.view.dialog.GeofenceController'
+ 'Traccar.view.dialog.GeofenceController',
+ 'Traccar.view.UnescapedTextField'
],
controller: 'geofence',
@@ -32,7 +33,7 @@ Ext.define('Traccar.view.dialog.Geofence', {
xtype: 'fieldset',
title: Strings.sharedRequired,
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'name',
fieldLabel: Strings.sharedName
}]
@@ -42,7 +43,7 @@ Ext.define('Traccar.view.dialog.Geofence', {
collapsible: true,
collapsed: true,
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'description',
fieldLabel: Strings.sharedDescription
}, {
diff --git a/web/app/view/dialog/Group.js b/web/app/view/dialog/Group.js
index 805b422..61ca193 100644
--- a/web/app/view/dialog/Group.js
+++ b/web/app/view/dialog/Group.js
@@ -19,7 +19,8 @@ Ext.define('Traccar.view.dialog.Group', {
extend: 'Traccar.view.dialog.BaseEdit',
requires: [
- 'Traccar.view.ClearableComboBox'
+ 'Traccar.view.ClearableComboBox',
+ 'Traccar.view.UnescapedTextField'
],
title: Strings.groupDialog,
@@ -30,7 +31,7 @@ Ext.define('Traccar.view.dialog.Group', {
xtype: 'fieldset',
title: Strings.sharedRequired,
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'name',
fieldLabel: Strings.sharedName,
allowBlank: false
diff --git a/web/app/view/dialog/Maintenance.js b/web/app/view/dialog/Maintenance.js
index 5705278..d844d25 100644
--- a/web/app/view/dialog/Maintenance.js
+++ b/web/app/view/dialog/Maintenance.js
@@ -21,7 +21,8 @@ Ext.define('Traccar.view.dialog.Maintenance', {
requires: [
'Traccar.view.dialog.MaintenanceController',
- 'Traccar.view.CustomNumberField'
+ 'Traccar.view.CustomNumberField',
+ 'Traccar.view.UnescapedTextField'
],
controller: 'maintenance',
@@ -37,7 +38,7 @@ Ext.define('Traccar.view.dialog.Maintenance', {
xtype: 'fieldset',
title: Strings.sharedRequired,
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'name',
fieldLabel: Strings.sharedName,
allowBlank: false
diff --git a/web/app/view/dialog/Notification.js b/web/app/view/dialog/Notification.js
index dc4362d..51af5b8 100644
--- a/web/app/view/dialog/Notification.js
+++ b/web/app/view/dialog/Notification.js
@@ -41,7 +41,10 @@ Ext.define('Traccar.view.dialog.Notification', {
displayField: 'name',
valueField: 'type',
editable: false,
- allowBlank: false
+ allowBlank: false,
+ listeners: {
+ change: 'onTypeChange'
+ }
}, {
xtype: 'checkboxfield',
inputValue: true,
@@ -49,8 +52,22 @@ Ext.define('Traccar.view.dialog.Notification', {
name: 'always',
fieldLabel: Strings.notificationAlways
}, {
- fieldLabel: Strings.notificationNotificators,
xtype: 'tagfield',
+ reference: 'alarmsField',
+ fieldLabel: Strings.sharedAlarms,
+ maxWidth: Traccar.Style.formFieldWidth,
+ store: 'AlarmTypes',
+ valueField: 'key',
+ displayField: 'name',
+ queryMode: 'local',
+ hidden: true,
+ listeners: {
+ beforerender: 'onAlarmsLoad',
+ change: 'onAlarmsChange'
+ }
+ }, {
+ xtype: 'tagfield',
+ fieldLabel: Strings.notificationNotificators,
name: 'notificators',
maxWidth: Traccar.Style.formFieldWidth,
store: 'AllNotificators',
diff --git a/web/app/view/dialog/NotificationController.js b/web/app/view/dialog/NotificationController.js
index ad65c33..5da669a 100644
--- a/web/app/view/dialog/NotificationController.js
+++ b/web/app/view/dialog/NotificationController.js
@@ -23,5 +23,31 @@ Ext.define('Traccar.view.dialog.NotificationController', {
init: function () {
this.lookupReference('calendarCombo').setHidden(
Traccar.app.getBooleanAttributePreference('ui.disableCalendars'));
+ },
+
+ onTypeChange: function (view, value) {
+ this.lookupReference('alarmsField').setHidden(value !== 'alarm');
+ },
+
+ onAlarmsLoad: function (view) {
+ var attributes, record = view.up('form').getRecord();
+ attributes = record.get('attributes') || {};
+ if (attributes['alarms']) {
+ view.suspendEvents(false);
+ view.setValue(attributes['alarms'].split(','));
+ view.resumeEvents();
+ }
+ },
+
+ onAlarmsChange: function (view, value) {
+ var attributes, record = view.up('window').down('form').getRecord();
+ attributes = record.get('attributes') || {};
+
+ value = value.join();
+ if (attributes['alarms'] !== value) {
+ attributes['alarms'] = value;
+ record.set('attributes', attributes);
+ record.dirty = true;
+ }
}
});
diff --git a/web/app/view/dialog/SavedCommand.js b/web/app/view/dialog/SavedCommand.js
index 4759143..b1aeae7 100644
--- a/web/app/view/dialog/SavedCommand.js
+++ b/web/app/view/dialog/SavedCommand.js
@@ -19,7 +19,8 @@ Ext.define('Traccar.view.dialog.SavedCommand', {
extend: 'Traccar.view.dialog.BaseEdit',
requires: [
- 'Traccar.view.dialog.SavedCommandController'
+ 'Traccar.view.dialog.SavedCommandController',
+ 'Traccar.view.UnescapedTextField'
],
controller: 'savedCommand',
@@ -34,7 +35,7 @@ Ext.define('Traccar.view.dialog.SavedCommand', {
xtype: 'fieldset',
title: Strings.sharedRequired,
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'description',
fieldLabel: Strings.sharedDescription
}, {
@@ -42,7 +43,7 @@ Ext.define('Traccar.view.dialog.SavedCommand', {
name: 'textChannel',
inputValue: true,
uncheckedValue: false,
- fieldLabel: Strings.notificationSms
+ fieldLabel: Strings.commandSendSms
}, {
xtype: 'combobox',
name: 'type',
diff --git a/web/app/view/dialog/SendCommand.js b/web/app/view/dialog/SendCommand.js
index 9e07cbf..7995473 100644
--- a/web/app/view/dialog/SendCommand.js
+++ b/web/app/view/dialog/SendCommand.js
@@ -54,7 +54,7 @@ Ext.define('Traccar.view.dialog.SendCommand', {
reference: 'textChannelCheckBox',
inputValue: true,
uncheckedValue: false,
- fieldLabel: Strings.notificationSms,
+ fieldLabel: Strings.commandSendSms,
listeners: {
change: 'onTextChannelChange'
}
diff --git a/web/app/view/dialog/Server.js b/web/app/view/dialog/Server.js
index fbfb3c6..b72dbb0 100644
--- a/web/app/view/dialog/Server.js
+++ b/web/app/view/dialog/Server.js
@@ -20,7 +20,8 @@ Ext.define('Traccar.view.dialog.Server', {
requires: [
'Traccar.view.ClearableComboBox',
- 'Traccar.view.dialog.MapPickerController'
+ 'Traccar.view.dialog.MapPickerController',
+ 'Traccar.view.UnescapedTextField'
],
controller: 'mapPicker',
@@ -39,13 +40,14 @@ Ext.define('Traccar.view.dialog.Server', {
displayField: 'name',
valueField: 'key'
}, {
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'bingKey',
fieldLabel: Strings.mapBingKey
}, {
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
+ reference: 'mapUrlField',
name: 'mapUrl',
- fieldLabel: Strings.mapCustom
+ fieldLabel: Strings.mapCustomLabel
}, {
xtype: 'numberfield',
reference: 'latitude',
@@ -83,7 +85,7 @@ Ext.define('Traccar.view.dialog.Server', {
displayField: 'name',
valueField: 'key'
}, {
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'poiLayer',
fieldLabel: Strings.mapPoiLayer
}]
diff --git a/web/app/view/dialog/User.js b/web/app/view/dialog/User.js
index 86f9167..917f110 100644
--- a/web/app/view/dialog/User.js
+++ b/web/app/view/dialog/User.js
@@ -20,7 +20,8 @@ Ext.define('Traccar.view.dialog.User', {
requires: [
'Traccar.view.ClearableComboBox',
- 'Traccar.view.dialog.UserController'
+ 'Traccar.view.dialog.UserController',
+ 'Traccar.view.UnescapedTextField'
],
controller: 'user',
@@ -32,11 +33,11 @@ Ext.define('Traccar.view.dialog.User', {
xtype: 'fieldset',
title: Strings.sharedRequired,
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'name',
fieldLabel: Strings.sharedName
}, {
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'email',
fieldLabel: Strings.userEmail,
allowBlank: false
@@ -53,7 +54,7 @@ Ext.define('Traccar.view.dialog.User', {
collapsible: true,
collapsed: true,
items: [{
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'phone',
fieldLabel: Strings.sharedPhone
}, {
@@ -94,7 +95,7 @@ Ext.define('Traccar.view.dialog.User', {
displayField: 'name',
valueField: 'key'
}, {
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'poiLayer',
fieldLabel: Strings.mapPoiLayer
}]
@@ -164,7 +165,7 @@ Ext.define('Traccar.view.dialog.User', {
disabled: true,
reference: 'userLimitField'
}, {
- xtype: 'textfield',
+ xtype: 'unescapedTextField',
name: 'token',
reference: 'tokenField',
fieldLabel: Strings.userToken,
diff --git a/web/app/view/edit/DevicesController.js b/web/app/view/edit/DevicesController.js
index 2b4ff14..16e54b2 100644
--- a/web/app/view/edit/DevicesController.js
+++ b/web/app/view/edit/DevicesController.js
@@ -35,7 +35,8 @@ Ext.define('Traccar.view.edit.DevicesController', {
listen: {
controller: {
'*': {
- selectreport: 'selectReport'
+ selectreport: 'deselectDevice',
+ selectevent: 'deselectDevice'
},
'root': {
selectdevice: 'selectDevice'
@@ -109,12 +110,10 @@ Ext.define('Traccar.view.edit.DevicesController', {
this.lookupReference('deviceCommandButton').setDisabled(empty || readonly);
},
- onSelectionChange: function (selection, selected) {
- this.updateButtons(selected);
- if (selected.length > 0) {
- this.fireEvent('selectdevice', selected[0], true);
- } else {
- this.fireEvent('deselectfeature');
+ onSelectionChange: function (el, records) {
+ if (records && records.length) {
+ this.updateButtons(records);
+ this.fireEvent('selectdevice', records[0], true);
}
},
@@ -124,8 +123,8 @@ Ext.define('Traccar.view.edit.DevicesController', {
this.getView().getView().focusRow(device);
},
- selectReport: function (position) {
- if (position !== undefined) {
+ deselectDevice: function (object) {
+ if (object) {
this.deselectFeature();
}
},
diff --git a/web/app/view/edit/Drivers.js b/web/app/view/edit/Drivers.js
index 9aac8cd..7bd10a6 100644
--- a/web/app/view/edit/Drivers.js
+++ b/web/app/view/edit/Drivers.js
@@ -46,7 +46,7 @@ Ext.define('Traccar.view.edit.Drivers', {
dataIndex: 'name',
filter: 'string'
}, {
- text: Strings.sharedDescription,
+ text: Strings.deviceIdentifier,
dataIndex: 'uniqueId',
filter: 'string'
}]
diff --git a/web/app/view/edit/Groups.js b/web/app/view/edit/Groups.js
index 0697420..8b09316 100644
--- a/web/app/view/edit/Groups.js
+++ b/web/app/view/edit/Groups.js
@@ -76,7 +76,7 @@ Ext.define('Traccar.view.edit.Groups', {
handler: 'onMaintenancesClick',
reference: 'toolbarMaintenancesButton',
glyph: 'xf0ad@FontAwesome',
- tooltip: Strings.sharedMaintenances,
+ tooltip: Strings.sharedMaintenance,
tooltipType: 'title'
}]
},
diff --git a/web/app/view/edit/GroupsController.js b/web/app/view/edit/GroupsController.js
index 2e62a28..ae96a24 100644
--- a/web/app/view/edit/GroupsController.js
+++ b/web/app/view/edit/GroupsController.js
@@ -41,7 +41,7 @@ Ext.define('Traccar.view.edit.GroupsController', {
Traccar.app.getBooleanAttributePreference('ui.disableComputedAttributes'));
this.lookupReference('toolbarCommandsButton').setHidden(Traccar.app.getPreference('limitCommands', false));
this.lookupReference('toolbarMaintenancesButton').setHidden(
- Traccar.app.getVehicleFeaturesDisabled() || Traccar.app.getBooleanAttributePreference('ui.disableMaintenances'));
+ Traccar.app.getVehicleFeaturesDisabled() || Traccar.app.getBooleanAttributePreference('ui.disableMaintenance'));
},
onGeofencesClick: function () {
@@ -117,7 +117,7 @@ Ext.define('Traccar.view.edit.GroupsController', {
onMaintenancesClick: function () {
var group = this.getView().getSelectionModel().getSelection()[0];
Ext.create('Traccar.view.BaseWindow', {
- title: Strings.sharedMaintenances,
+ title: Strings.sharedMaintenance,
items: {
xtype: 'linkMaintenancesView',
baseObjectName: 'groupId',
diff --git a/web/app/view/edit/Notifications.js b/web/app/view/edit/Notifications.js
index 9e24d3d..9cf97b1 100644
--- a/web/app/view/edit/Notifications.js
+++ b/web/app/view/edit/Notifications.js
@@ -60,6 +60,23 @@ Ext.define('Traccar.view.edit.Notifications', {
renderer: Traccar.AttributeFormatter.getFormatter('always'),
filter: 'boolean'
}, {
+ text: Strings.sharedAlarms,
+ dataIndex: 'attributes',
+ renderer: function (value) {
+ var i, key, result = '', alarms = value && value['alarms'];
+ if (alarms) {
+ alarms = alarms.split(',');
+ for (i = 0; i < alarms.length; i++) {
+ key = 'alarm' + alarms[i].charAt(0).toUpperCase() + alarms[i].slice(1);
+ if (result) {
+ result += ', ';
+ }
+ result += Strings[key] || key;
+ }
+ }
+ return result;
+ }
+ }, {
text: Strings.notificationNotificators,
dataIndex: 'notificators',
flex: 2,
diff --git a/web/app/view/edit/SavedCommands.js b/web/app/view/edit/SavedCommands.js
index 794e95e..9e5f486 100644
--- a/web/app/view/edit/SavedCommands.js
+++ b/web/app/view/edit/SavedCommands.js
@@ -56,7 +56,7 @@ Ext.define('Traccar.view.edit.SavedCommands', {
},
renderer: Traccar.AttributeFormatter.getFormatter('commandType')
}, {
- text: Strings.notificationSms,
+ text: Strings.commandSendSms,
dataIndex: 'textChannel',
renderer: Traccar.AttributeFormatter.getFormatter('textChannel'),
filter: 'boolean'
diff --git a/web/app/view/edit/Users.js b/web/app/view/edit/Users.js
index 2bcaefa..5d9a14f 100644
--- a/web/app/view/edit/Users.js
+++ b/web/app/view/edit/Users.js
@@ -101,7 +101,7 @@ Ext.define('Traccar.view.edit.Users', {
handler: 'onMaintenancesClick',
reference: 'userMaintenancesButton',
glyph: 'xf0ad@FontAwesome',
- tooltip: Strings.sharedMaintenances,
+ tooltip: Strings.sharedMaintenance,
tooltipType: 'title'
}]
},
diff --git a/web/app/view/edit/UsersController.js b/web/app/view/edit/UsersController.js
index 9d99816..9e81043 100644
--- a/web/app/view/edit/UsersController.js
+++ b/web/app/view/edit/UsersController.js
@@ -51,7 +51,7 @@ Ext.define('Traccar.view.edit.UsersController', {
Traccar.app.getBooleanAttributePreference('ui.disableCalendars'));
this.lookupReference('userCommandsButton').setHidden(Traccar.app.getPreference('limitCommands', false));
this.lookupReference('userMaintenancesButton').setHidden(
- Traccar.app.getVehicleFeaturesDisabled() || Traccar.app.getBooleanAttributePreference('ui.disableMaintenances'));
+ Traccar.app.getVehicleFeaturesDisabled() || Traccar.app.getBooleanAttributePreference('ui.disableMaintenance'));
},
onEditClick: function () {
@@ -215,7 +215,7 @@ Ext.define('Traccar.view.edit.UsersController', {
onMaintenancesClick: function () {
var user = this.getView().getSelectionModel().getSelection()[0];
Ext.create('Traccar.view.BaseWindow', {
- title: Strings.sharedMaintenances,
+ title: Strings.sharedMaintenance,
items: {
xtype: 'linkMaintenancesView',
baseObjectName: 'userId',
diff --git a/web/app/view/map/BaseMap.js b/web/app/view/map/BaseMap.js
index 07e391f..6c6522b 100644
--- a/web/app/view/map/BaseMap.js
+++ b/web/app/view/map/BaseMap.js
@@ -30,7 +30,7 @@ Ext.define('Traccar.view.map.BaseMap', {
},
initMap: function () {
- var server, layer, type, bingKey, lat, lon, zoom, maxZoom, target, poiLayer;
+ var server, layer, type, bingKey, lat, lon, zoom, maxZoom, target, poiLayer, self = this;
server = Traccar.app.getServer();
@@ -41,11 +41,18 @@ Ext.define('Traccar.view.map.BaseMap', {
case 'custom':
layer = new ol.layer.Tile({
source: new ol.source.XYZ({
- url: server.get('mapUrl'),
+ url: Ext.String.htmlDecode(server.get('mapUrl')),
attributions: ''
})
});
break;
+ case 'customArcgis':
+ layer = new ol.layer.Tile({
+ source: new ol.source.TileArcGISRest({
+ url: Ext.String.htmlDecode(server.get('mapUrl'))
+ })
+ });
+ break;
case 'bingRoad':
layer = new ol.layer.Tile({
source: new ol.source.BingMaps({
@@ -96,7 +103,7 @@ Ext.define('Traccar.view.map.BaseMap', {
if (y < 0) {
y = 'M' + -y;
}
- return 'http://online{}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl'
+ return 'https://online{}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl'
.replace('{}', index).replace('{x}', x).replace('{y}', y).replace('{z}', z);
},
tileGrid: new ol.tilegrid.TileGrid({
@@ -108,7 +115,7 @@ Ext.define('Traccar.view.map.BaseMap', {
1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0.5
]
}),
- attributions: '&copy; <a href="http://map.baidu.com/">Baidu</a>'
+ attributions: '&copy; <a href="https://map.baidu.com/">Baidu</a>'
})
});
break;
@@ -130,16 +137,16 @@ Ext.define('Traccar.view.map.BaseMap', {
})
});
break;
- case 'osm':
+ case 'wikimedia':
layer = new ol.layer.Tile({
- source: new ol.source.OSM({})
+ source: new ol.source.OSM({
+ url: 'https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png'
+ })
});
break;
default:
layer = new ol.layer.Tile({
- source: new ol.source.OSM({
- url: 'https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png'
- })
+ source: new ol.source.OSM({})
});
break;
}
@@ -207,19 +214,19 @@ Ext.define('Traccar.view.map.BaseMap', {
});
this.map.on('click', function (e) {
- var i, features = this.map.getFeaturesAtPixel(e.pixel, {
+ var i, features = self.map.getFeaturesAtPixel(e.pixel, {
layerFilter: function (layer) {
return !layer.get('name');
}
});
if (features) {
for (i = 0; i < features.length; i++) {
- this.fireEvent('selectfeature', features[i]);
+ self.fireEvent('selectfeature', features[i]);
}
} else {
- this.fireEvent('deselectfeature');
+ self.fireEvent('deselectfeature');
}
- }, this);
+ });
},
listeners: {
@@ -232,7 +239,12 @@ Ext.define('Traccar.view.map.BaseMap', {
}
}
}, function () {
+ var projection;
proj4.defs('BD-MC', '+proj=merc +lon_0=0 +units=m +ellps=clrk66 +no_defs');
proj4.defs('EPSG:3395', '+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
- ol.proj.get('EPSG:3395').setExtent([-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244]);
+ ol.proj.proj4.register(proj4);
+ projection = ol.proj.get('EPSG:3395');
+ if (projection) {
+ projection.setExtent([-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244]);
+ }
});
diff --git a/web/app/view/map/GeofenceMap.js b/web/app/view/map/GeofenceMap.js
index 6b06ae9..8cef574 100644
--- a/web/app/view/map/GeofenceMap.js
+++ b/web/app/view/map/GeofenceMap.js
@@ -105,13 +105,14 @@ Ext.define('Traccar.view.map.GeofenceMap', {
},
addInteraction: function (type) {
+ var self = this;
this.draw = new ol.interaction.Draw({
features: this.features,
type: type
});
this.draw.on('drawstart', function () {
- this.features.clear();
- }, this);
+ self.features.clear();
+ });
this.map.addInteraction(this.draw);
},
diff --git a/web/app/view/map/MapController.js b/web/app/view/map/MapController.js
index 38b2340..7ba0ee3 100644
--- a/web/app/view/map/MapController.js
+++ b/web/app/view/map/MapController.js
@@ -83,7 +83,8 @@ Ext.define('Traccar.view.map.MapController', {
var feature = new ol.Feature(
Traccar.GeofenceConverter.wktToGeometry(this.getView().getMapView(), geofence.get('area')));
feature.setStyle(this.getAreaStyle(
- geofence.get('name'), geofence.get('attributes') ? geofence.get('attributes').color : null));
+ Ext.String.htmlDecode(geofence.get('name')),
+ geofence.get('attributes') ? geofence.get('attributes').color : null));
this.getView().getGeofencesSource().addFeature(feature);
return true;
}, this);
diff --git a/web/app/view/map/MapMarkerController.js b/web/app/view/map/MapMarkerController.js
index bf6b0d4..785fcc6 100644
--- a/web/app/view/map/MapMarkerController.js
+++ b/web/app/view/map/MapMarkerController.js
@@ -130,7 +130,7 @@ Ext.define('Traccar.view.map.MapMarkerController', {
},
updateDevice: function (store, data) {
- var i, device, deviceId, marker, style;
+ var i, device, deviceId, deviceName, marker, style;
if (!Ext.isArray(data)) {
data = [data];
@@ -148,8 +148,9 @@ Ext.define('Traccar.view.map.MapMarkerController', {
this.updateDeviceMarker(style, this.getDeviceColor(device), device.get('category'));
marker.changed();
}
- if (style.getText().getText() !== device.get('name')) {
- style.getText().setText(device.get('name'));
+ deviceName = Ext.String.htmlDecode(device.get('name'));
+ if (style.getText().getText() !== deviceName) {
+ style.getText().setText(deviceName);
marker.changed();
}
}
@@ -187,6 +188,39 @@ Ext.define('Traccar.view.map.MapMarkerController', {
}
},
+ animateMarker: function (marker, geometry, course) {
+ var start, end, duration, timeout, line, updatePosition, self;
+
+ start = marker.getGeometry().getCoordinates();
+ end = geometry.getCoordinates();
+ line = new ol.geom.LineString([start, end]);
+ duration = Traccar.Style.mapAnimateMarkerDuration;
+ timeout = Traccar.Style.mapAnimateMarkerTimeout;
+ self = this;
+
+ updatePosition = function (position, marker) {
+ var coordinate, style;
+ coordinate = marker.get('line').getCoordinateAt(position / (duration / timeout));
+ style = marker.getStyle();
+ marker.setGeometry(new ol.geom.Point(coordinate));
+ if (position < duration / timeout) {
+ setTimeout(updatePosition, timeout, position + 1, marker);
+ } else {
+ if (style.getImage().angle !== marker.get('nextCourse')) {
+ self.rotateMarker(style, marker.get('nextCourse'));
+ }
+ marker.set('animating', false);
+ }
+ };
+
+ marker.set('line', line);
+ marker.set('nextCourse', course);
+ if (!marker.get('animating')) {
+ marker.set('animating', true);
+ updatePosition(1, marker);
+ }
+ },
+
updateLatest: function (store, data) {
var i, position, device, deviceStore;
@@ -209,15 +243,13 @@ Ext.define('Traccar.view.map.MapMarkerController', {
},
updateAccuracy: function (position, device) {
- var center, radius, feature, mapView, projection, pointResolution;
- mapView = this.getView().getMapView();
+ var center, radius, feature;
feature = this.accuracyCircles[position.get('deviceId')];
if (position.get('accuracy')) {
- projection = mapView.getProjection();
center = ol.proj.fromLonLat([position.get('longitude'), position.get('latitude')]);
- pointResolution = ol.proj.getPointResolution(projection, mapView.getResolution(), center);
- radius = position.get('accuracy') / ol.proj.METERS_PER_UNIT.m * mapView.getResolution() / pointResolution;
+ radius = Ext.getStore('DistanceUnits').convertValue(
+ position.get('accuracy'), Traccar.app.getAttributePreference('distanceUnit'), true);
if (feature) {
feature.getGeometry().setCenter(center);
@@ -248,11 +280,7 @@ Ext.define('Traccar.view.map.MapMarkerController', {
deviceId = position.get('deviceId');
if (deviceId in this.latestMarkers) {
marker = this.latestMarkers[deviceId];
- style = marker.getStyle();
- if (style.getImage().angle !== position.get('course')) {
- this.rotateMarker(style, position.get('course'));
- }
- marker.setGeometry(geometry);
+ this.animateMarker(marker, geometry, position.get('course'));
} else {
marker = new ol.Feature(geometry);
marker.set('record', device);
@@ -260,7 +288,7 @@ Ext.define('Traccar.view.map.MapMarkerController', {
style = this.getLatestMarker(this.getDeviceColor(device),
position.get('course'),
device.get('category'));
- style.getText().setText(device.get('name'));
+ style.getText().setText(Ext.String.htmlDecode(device.get('name')));
marker.setStyle(style);
marker.setId(device.get('id'));
this.latestMarkers[deviceId] = marker;
@@ -457,9 +485,6 @@ Ext.define('Traccar.view.map.MapMarkerController', {
if (this.selectedMarker) {
if (this.selectedMarker.get('event')) {
this.getView().getMarkersSource().removeFeature(this.selectedMarker);
- if (!marker || !marker.get('event')) {
- this.fireEvent('deselectevent');
- }
} else if (!Ext.getStore('ReportRoute').showMarkers &&
this.selectedMarker.get('record') instanceof Traccar.model.Position) {
this.getView().getMarkersSource().removeFeature(this.selectedMarker);
@@ -496,12 +521,13 @@ Ext.define('Traccar.view.map.MapMarkerController', {
this.reportMarkers[position.get('id')] = this.addReportMarker(position);
}
this.selectMarker(this.reportMarkers[position.get('id')], center);
+ } else if (this.selectedMarker) {
+ this.selectMarker(null, false);
}
},
selectEvent: function (position) {
var marker;
- this.fireEvent('deselectfeature');
if (position) {
marker = this.addReportMarker(position);
marker.set('event', true);
diff --git a/web/app/view/permissions/SavedCommands.js b/web/app/view/permissions/SavedCommands.js
index 52b759b..b57c07a 100644
--- a/web/app/view/permissions/SavedCommands.js
+++ b/web/app/view/permissions/SavedCommands.js
@@ -40,7 +40,7 @@ Ext.define('Traccar.view.permissions.SavedCommands', {
},
renderer: Traccar.AttributeFormatter.getFormatter('commandType')
}, {
- text: Strings.notificationSms,
+ text: Strings.commandSendSms,
dataIndex: 'textChannel',
flex: 1,
minWidth: Traccar.Style.columnWidthNormal,