diff options
Diffstat (limited to 'web')
38 files changed, 1003 insertions, 100 deletions
diff --git a/web/app.css b/web/app.css index eb0fdf136..0dbf94ca6 100644 --- a/web/app.css +++ b/web/app.css @@ -8,6 +8,10 @@ background-color: rgba(255, 84, 104, 0.3); } +.x-tree-icon { + display: none !important; +} + .state-indicator { position: absolute; top: -999em; diff --git a/web/app/Application.js b/web/app/Application.js index fc4344a08..69ce8f891 100644 --- a/web/app/Application.js +++ b/web/app/Application.js @@ -26,6 +26,7 @@ Ext.define('Traccar.Application', { models: [ 'Server', 'User', + 'Group', 'Device', 'Position', 'Attribute', @@ -33,7 +34,9 @@ Ext.define('Traccar.Application', { ], stores: [ + 'Groups', 'Devices', + 'AllGroups', 'AllDevices', 'Positions', 'LatestPositions', diff --git a/web/app/controller/Root.js b/web/app/controller/Root.js index 23ca94972..5bd567619 100644 --- a/web/app/controller/Root.js +++ b/web/app/controller/Root.js @@ -73,6 +73,7 @@ Ext.define('Traccar.controller.Root', { }, loadApp: function () { + Ext.getStore('Groups').load(); Ext.getStore('Devices').load(); Ext.get('attribution').remove(); if (this.isPhone) { @@ -86,7 +87,7 @@ Ext.define('Traccar.controller.Root', { asyncUpdate: function (first) { var protocol, socket, self = this; protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; - socket = new WebSocket(protocol + window.location.host + '/api/socket'); + socket = new WebSocket(protocol + '//' + window.location.host + '/api/socket'); socket.onclose = function (event) { self.asyncUpdate(false); diff --git a/web/app/model/Device.js b/web/app/model/Device.js index b8434d6ad..588d53c1f 100644 --- a/web/app/model/Device.js +++ b/web/app/model/Device.js @@ -33,6 +33,9 @@ Ext.define('Traccar.model.Device', { }, { name: 'lastUpdate', type: 'date', - dateWriteFormat: 'c' + dateFormat: 'c' + }, { + name: 'groupId', + type: 'int' }] }); diff --git a/web/app/model/Group.js b/web/app/model/Group.js new file mode 100644 index 000000000..a28897feb --- /dev/null +++ b/web/app/model/Group.js @@ -0,0 +1,31 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Ext.define('Traccar.model.Group', { + extend: 'Ext.data.Model', + identifier: 'negative', + + fields: [{ + name: 'id', + type: 'int' + }, { + name: 'name', + type: 'string' + }, { + name: 'groupId', + type: 'int' + }] +}); diff --git a/web/app/model/Position.js b/web/app/model/Position.js index 365b06115..e559a7eab 100644 --- a/web/app/model/Position.js +++ b/web/app/model/Position.js @@ -29,13 +29,16 @@ Ext.define('Traccar.model.Position', { type: 'int' }, { name: 'serverTime', - type: 'date' + type: 'date', + dateFormat: 'c' }, { name: 'deviceTime', - type: 'date' + type: 'date', + dateFormat: 'c' }, { name: 'fixTime', - type: 'date' + type: 'date', + dateFormat: 'c' }, { name: 'valid', type: 'boolean' diff --git a/web/app/store/AllGroups.js b/web/app/store/AllGroups.js new file mode 100644 index 000000000..880ccc8f5 --- /dev/null +++ b/web/app/store/AllGroups.js @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Ext.define('Traccar.store.AllGroups', { + extend: 'Ext.data.Store', + model: 'Traccar.model.Group', + + proxy: { + type: 'rest', + url: '/api/groups', + extraParams: { + all: true + } + } +}); diff --git a/web/app/store/Groups.js b/web/app/store/Groups.js new file mode 100644 index 000000000..938abed64 --- /dev/null +++ b/web/app/store/Groups.js @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Ext.define('Traccar.store.Groups', { + extend: 'Ext.data.Store', + model: 'Traccar.model.Group', + + proxy: { + type: 'rest', + url: '/api/groups', + writer: { + writeAllFields: true + } + } +}); diff --git a/web/app/view/DeviceDialog.js b/web/app/view/DeviceDialog.js index c42af95d0..4a22ca008 100644 --- a/web/app/view/DeviceDialog.js +++ b/web/app/view/DeviceDialog.js @@ -29,13 +29,20 @@ Ext.define('Traccar.view.DeviceDialog', { items: [{ xtype: 'textfield', name: 'name', - fieldLabel: Strings.deviceName, + fieldLabel: Strings.sharedName, allowBlank: false }, { xtype: 'textfield', name: 'uniqueId', fieldLabel: Strings.deviceIdentifier, allowBlank: false + }, { + xtype: 'combobox', + name: 'groupId', + fieldLabel: Strings.groupParent, + store: 'Groups', + displayField: 'name', + valueField: 'id' }] } }); diff --git a/web/app/view/Devices.js b/web/app/view/Devices.js index 66c4e813b..ebe3ca195 100644 --- a/web/app/view/Devices.js +++ b/web/app/view/Devices.js @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ Ext.define('Traccar.view.Devices', { - extend: 'Ext.grid.Panel', + extend: 'Ext.tree.Panel', xtype: 'devicesView', requires: [ @@ -25,7 +25,17 @@ Ext.define('Traccar.view.Devices', { ], controller: 'devices', - store: 'Devices', + rootVisible: false, + store: { + type: 'tree', + parentIdProperty: 'groupId', + proxy: { + type: 'memory', + reader: { + type: 'json' + } + } + }, title: Strings.deviceTitle, selType: 'rowmodel', @@ -53,11 +63,13 @@ Ext.define('Traccar.view.Devices', { }, listeners: { - selectionchange: 'onSelectionChange' + selectionchange: 'onSelectionChange', + beforeselect: 'onBeforeSelect' }, columns: [{ - text: Strings.deviceName, + xtype: 'treecolumn', + text: Strings.sharedName, dataIndex: 'name', flex: 1 }, { @@ -65,19 +77,20 @@ Ext.define('Traccar.view.Devices', { dataIndex: 'lastUpdate', flex: 1, renderer: function (value, metaData, record) { - var status = record.get('status'); - switch (status) { - case 'online': - metaData.tdCls = 'status-color-online'; - break; - case 'offline': - metaData.tdCls = 'status-color-offline'; - break; - default: - metaData.tdCls = 'status-color-unknown'; - break; + if (record.get('leaf')) { + switch (record.get('status')) { + case 'online': + metaData.tdCls = 'status-color-online'; + break; + case 'offline': + metaData.tdCls = 'status-color-offline'; + break; + default: + metaData.tdCls = 'status-color-unknown'; + break; + } + return Ext.Date.format(value, Traccar.Style.dateTimeFormat); } - return Ext.Date.format(value, Traccar.Style.dateTimeFormat); } }] diff --git a/web/app/view/DevicesController.js b/web/app/view/DevicesController.js index 8c6c72725..4d8231d94 100644 --- a/web/app/view/DevicesController.js +++ b/web/app/view/DevicesController.js @@ -1,5 +1,5 @@ /* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,9 +30,54 @@ Ext.define('Traccar.view.DevicesController', { selectDevice: 'selectDevice', selectReport: 'selectReport' } + }, + store: { + '#Groups': { + datachanged: 'storeUpdate', + update: 'storeUpdate' + }, + '#Devices': { + datachanged: 'storeUpdate', + update: 'storeUpdate' + } } } }, + + storeUpdate: function () { + var nodes = []; + Ext.getStore('Groups').each(function (record) { + var groupId, node = { + id: 'g' + record.get('id'), + original: record, + name: record.get('name') + }; + groupId = record.get('groupId'); + if (groupId !== 0) { + node.groupId = 'g' + groupId; + } + nodes.push(node); + }, this); + Ext.getStore('Devices').each(function (record) { + var groupId, node = { + id: 'd' + record.get('id'), + original: record, + name: record.get('name'), + status: record.get('status'), + lastUpdate: record.get('lastUpdate'), + leaf: true + }; + groupId = record.get('groupId'); + if (groupId !== 0) { + node.groupId = 'g' + groupId; + } + nodes.push(node); + }, this); + this.getView().getStore().getProxy().setData(nodes); + this.getView().getStore().load(); + this.getView().expandAll(); + }, + init: function () { var readonly = Traccar.app.getServer().get('readonly') && !Traccar.app.getUser().get('admin'); this.lookupReference('toolbarAddButton').setVisible(!readonly); @@ -43,7 +88,7 @@ Ext.define('Traccar.view.DevicesController', { onAddClick: function () { var device, dialog; device = Ext.create('Traccar.model.Device'); - device.store = this.getView().getStore(); + device.store = Ext.getStore('Devices'); dialog = Ext.create('Traccar.view.DeviceDialog'); dialog.down('form').loadRecord(device); dialog.show(); @@ -51,14 +96,14 @@ Ext.define('Traccar.view.DevicesController', { onEditClick: function () { var device, dialog; - device = this.getView().getSelectionModel().getSelection()[0]; + device = this.getView().getSelectionModel().getSelection()[0].get('original'); dialog = Ext.create('Traccar.view.DeviceDialog'); dialog.down('form').loadRecord(device); dialog.show(); }, onRemoveClick: function () { - var device = this.getView().getSelectionModel().getSelection()[0]; + var device = this.getView().getSelectionModel().getSelection()[0].get('original'); Ext.Msg.show({ title: Strings.deviceDialog, message: Strings.sharedRemoveConfirm, @@ -80,7 +125,7 @@ Ext.define('Traccar.view.DevicesController', { onCommandClick: function () { var device, command, dialog; - device = this.getView().getSelectionModel().getSelection()[0]; + device = this.getView().getSelectionModel().getSelection()[0].get('original'); command = Ext.create('Traccar.model.Command'); command.set('deviceId', device.get('id')); dialog = Ext.create('Traccar.view.CommandDialog'); @@ -89,17 +134,22 @@ Ext.define('Traccar.view.DevicesController', { }, onSelectionChange: function (selected) { - var empty = selected.getCount() === 0; + var empty = selected.getCount() === 0 || !this.getView().getSelectionModel().getSelection()[0].get('leaf'); this.lookupReference('toolbarEditButton').setDisabled(empty); this.lookupReference('toolbarRemoveButton').setDisabled(empty); this.lookupReference('deviceCommandButton').setDisabled(empty); if (!empty) { - this.fireEvent('selectDevice', selected.getLastSelected(), true); + this.fireEvent('selectDevice', selected.getLastSelected().get('original'), true); } }, + onBeforeSelect: function (row, record) { + return record.get('leaf'); + }, + selectDevice: function (device, center) { - this.getView().getSelectionModel().select([device], false, true); + var node = this.getView().getStore().getNodeById('d' + device.get('id')); + this.getView().getSelectionModel().select([node], false, true); }, selectReport: function (position) { diff --git a/web/app/view/GroupDialog.js b/web/app/view/GroupDialog.js new file mode 100644 index 000000000..2cca61ef5 --- /dev/null +++ b/web/app/view/GroupDialog.js @@ -0,0 +1,43 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Ext.define('Traccar.view.GroupDialog', { + extend: 'Traccar.view.BaseEditDialog', + + requires: [ + 'Traccar.view.BaseEditDialogController' + ], + + controller: 'baseEditDialog', + title: Strings.groupDialog, + + items: { + xtype: 'form', + items: [{ + xtype: 'textfield', + name: 'name', + fieldLabel: Strings.sharedName, + allowBlank: false + }, { + xtype: 'combobox', + name: 'groupId', + fieldLabel: Strings.groupParent, + store: 'Groups', + displayField: 'name', + valueField: 'id' + }] + } +}); diff --git a/web/app/view/Groups.js b/web/app/view/Groups.js new file mode 100644 index 000000000..8404c59a9 --- /dev/null +++ b/web/app/view/Groups.js @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Ext.define('Traccar.view.Groups', { + extend: 'Ext.grid.Panel', + xtype: 'groupsView', + + requires: [ + 'Traccar.view.GroupsController', + 'Traccar.view.EditToolbar' + ], + + controller: 'groups', + store: 'Groups', + + selType: 'rowmodel', + + tbar: { + xtype: 'editToolbar' + }, + + listeners: { + selectionchange: 'onSelectionChange' + }, + + columns: [{ + text: Strings.sharedName, + dataIndex: 'name', + flex: 1 + }] +}); diff --git a/web/app/view/GroupsController.js b/web/app/view/GroupsController.js new file mode 100644 index 000000000..6cc568ea2 --- /dev/null +++ b/web/app/view/GroupsController.js @@ -0,0 +1,63 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Ext.define('Traccar.view.GroupsController', { + extend: 'Ext.app.ViewController', + alias: 'controller.groups', + + onAddClick: function () { + var group, dialog; + group = Ext.create('Traccar.model.Group'); + group.store = this.getView().getStore(); + dialog = Ext.create('Traccar.view.GroupDialog'); + dialog.down('form').loadRecord(group); + dialog.show(); + }, + + onEditClick: function () { + var group, dialog; + group = this.getView().getSelectionModel().getSelection()[0]; + dialog = Ext.create('Traccar.view.GroupDialog'); + dialog.down('form').loadRecord(group); + dialog.show(); + }, + + onRemoveClick: function () { + var group = this.getView().getSelectionModel().getSelection()[0]; + Ext.Msg.show({ + title: Strings.groupDialog, + message: Strings.sharedRemoveConfirm, + buttons: Ext.Msg.YESNO, + buttonText: { + yes: Strings.sharedRemove, + no: Strings.sharedCancel + }, + fn: function (btn) { + var store = Ext.getStore('Groups'); + if (btn === 'yes') { + store.remove(group); + store.sync(); + } + } + }); + }, + + onSelectionChange: function (selected) { + var disabled = selected.length > 0; + this.lookupReference('toolbarEditButton').setDisabled(disabled); + this.lookupReference('toolbarRemoveButton').setDisabled(disabled); + } +}); diff --git a/web/app/view/MapController.js b/web/app/view/MapController.js index ce420c2ec..918f81390 100644 --- a/web/app/view/MapController.js +++ b/web/app/view/MapController.js @@ -113,29 +113,31 @@ Ext.define('Traccar.view.MapController', { deviceId = position.get('deviceId'); device = Ext.getStore('Devices').findRecord('id', deviceId, 0, false, false, true); - geometry = new ol.geom.Point(ol.proj.fromLonLat([ - position.get('longitude'), - position.get('latitude') - ])); - - if (deviceId in this.latestMarkers) { - marker = this.latestMarkers[deviceId]; - marker.setGeometry(geometry); - } else { - marker = new ol.Feature(geometry); - marker.set('record', device); - this.latestMarkers[deviceId] = marker; - this.getView().getLatestSource().addFeature(marker); - - style = this.getLatestMarker(this.getDeviceColor(device)); - style.getText().setText(device.get('name')); - marker.setStyle(style); - } + if (device) { + geometry = new ol.geom.Point(ol.proj.fromLonLat([ + position.get('longitude'), + position.get('latitude') + ])); + + if (deviceId in this.latestMarkers) { + marker = this.latestMarkers[deviceId]; + marker.setGeometry(geometry); + } else { + marker = new ol.Feature(geometry); + marker.set('record', device); + this.latestMarkers[deviceId] = marker; + this.getView().getLatestSource().addFeature(marker); + + style = this.getLatestMarker(this.getDeviceColor(device)); + style.getText().setText(device.get('name')); + marker.setStyle(style); + } - marker.getStyle().getImage().setRotation(position.get('course') * Math.PI / 180); + marker.getStyle().getImage().setRotation(position.get('course') * Math.PI / 180); - if (marker === this.selectedMarker && this.followSelected()) { - this.getView().getMapView().setCenter(marker.getGeometry().getCoordinates()); + if (marker === this.selectedMarker && this.followSelected()) { + this.getView().getMapView().setCenter(marker.getGeometry().getCoordinates()); + } } } }, diff --git a/web/app/view/Register.js b/web/app/view/Register.js index 7c2881d62..198e10b8f 100644 --- a/web/app/view/Register.js +++ b/web/app/view/Register.js @@ -33,7 +33,7 @@ Ext.define('Traccar.view.Register', { items: [{ xtype: 'textfield', name: 'name', - fieldLabel: Strings.userName, + fieldLabel: Strings.sharedName, allowBlank: false }, { xtype: 'textfield', diff --git a/web/app/view/SettingsMenu.js b/web/app/view/SettingsMenu.js index c828b37af..1ada94359 100644 --- a/web/app/view/SettingsMenu.js +++ b/web/app/view/SettingsMenu.js @@ -32,6 +32,9 @@ Ext.define('Traccar.view.SettingsMenu', { text: Strings.settingsUser, handler: 'onUserClick' }, { + text: Strings.settingsGroups, + handler: 'onGroupsClick' + }, { text: Strings.settingsServer, hidden: true, handler: 'onServerClick', diff --git a/web/app/view/SettingsMenuController.js b/web/app/view/SettingsMenuController.js index c52f0a75e..6d767e3a9 100644 --- a/web/app/view/SettingsMenuController.js +++ b/web/app/view/SettingsMenuController.js @@ -23,6 +23,7 @@ Ext.define('Traccar.view.SettingsMenuController', { 'Traccar.view.UserDialog', 'Traccar.view.ServerDialog', 'Traccar.view.Users', + 'Traccar.view.Groups', 'Traccar.view.BaseWindow' ], @@ -39,6 +40,16 @@ Ext.define('Traccar.view.SettingsMenuController', { dialog.show(); }, + onGroupsClick: function () { + Ext.create('Traccar.view.BaseWindow', { + title: Strings.settingsGroups, + modal: false, + items: { + xtype: 'groupsView' + } + }).show(); + }, + onServerClick: function () { var dialog = Ext.create('Traccar.view.ServerDialog'); dialog.down('form').loadRecord(Traccar.app.getServer()); diff --git a/web/app/view/UserDevices.js b/web/app/view/UserDevices.js index f9ab48266..fe16dd93a 100644 --- a/web/app/view/UserDevices.js +++ b/web/app/view/UserDevices.js @@ -37,10 +37,12 @@ Ext.define('Traccar.view.UserDevices', { }, columns: [{ - text: Strings.deviceName, - dataIndex: 'name', flex: 1 + text: Strings.sharedName, + dataIndex: 'name', + flex: 1 }, { text: Strings.deviceIdentifier, - dataIndex: 'uniqueId', flex: 1 + dataIndex: 'uniqueId', + flex: 1 }] }); diff --git a/web/app/view/UserDevicesController.js b/web/app/view/UserDevicesController.js index 4f013fd64..e5dbbdbb8 100644 --- a/web/app/view/UserDevicesController.js +++ b/web/app/view/UserDevicesController.js @@ -47,7 +47,7 @@ Ext.define('Traccar.view.UserDevicesController', { onBeforeSelect: function (object, record, index) { Ext.Ajax.request({ scope: this, - url: '/api/permissions', + url: '/api/permissions/devices', jsonData: { userId: this.userId, deviceId: record.getData().id @@ -64,7 +64,7 @@ Ext.define('Traccar.view.UserDevicesController', { Ext.Ajax.request({ scope: this, method: 'DELETE', - url: '/api/permissions', + url: '/api/permissions/devices', jsonData: { userId: this.userId, deviceId: record.getData().id diff --git a/web/app/view/UserDialog.js b/web/app/view/UserDialog.js index 783ddd159..c1ed2fece 100644 --- a/web/app/view/UserDialog.js +++ b/web/app/view/UserDialog.js @@ -30,7 +30,7 @@ Ext.define('Traccar.view.UserDialog', { items: [{ xtype: 'textfield', name: 'name', - fieldLabel: Strings.userName + fieldLabel: Strings.sharedName }, { xtype: 'textfield', name: 'email', diff --git a/web/app/view/UserGroups.js b/web/app/view/UserGroups.js new file mode 100644 index 000000000..cb0f0bd5d --- /dev/null +++ b/web/app/view/UserGroups.js @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Ext.define('Traccar.view.UserGroups', { + extend: 'Ext.grid.Panel', + xtype: 'userGroupsView', + + requires: [ + 'Traccar.view.UserGroupsController' + ], + + controller: 'userGroups', + store: 'AllGroups', + + selModel: { + selType: 'checkboxmodel', + checkOnly: true, + showHeaderCheckbox: false + }, + + listeners: { + beforedeselect: 'onBeforeDeselect', + beforeselect: 'onBeforeSelect' + }, + + columns: [{ + text: Strings.sharedName, + dataIndex: 'name', + flex: 1 + }] +}); diff --git a/web/app/view/UserGroupsController.js b/web/app/view/UserGroupsController.js new file mode 100644 index 000000000..662508f0a --- /dev/null +++ b/web/app/view/UserGroupsController.js @@ -0,0 +1,79 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +Ext.define('Traccar.view.UserGroupsController', { + extend: 'Ext.app.ViewController', + alias: 'controller.userGroups', + + init: function () { + this.userId = this.getView().user.getData().id; + this.getView().getStore().load({ + scope: this, + callback: function (records, operation, success) { + var userStore = Ext.create('Traccar.store.Groups'); + + userStore.load({ + params: { + userId: this.userId + }, + scope: this, + callback: function (records, operation, success) { + var i, index; + if (success) { + for (i = 0; i < records.length; i++) { + index = this.getView().getStore().find('id', records[i].getData().id); + this.getView().getSelectionModel().select(index, true, true); + } + } + } + }); + } + }); + }, + + onBeforeSelect: function (object, record, index) { + Ext.Ajax.request({ + scope: this, + url: '/api/permissions/groups', + jsonData: { + userId: this.userId, + groupId: record.getData().id + }, + callback: function (options, success, response) { + if (!success) { + Traccar.app.showError(response); + } + } + }); + }, + + onBeforeDeselect: function (object, record, index) { + Ext.Ajax.request({ + scope: this, + method: 'DELETE', + url: '/api/permissions/groups', + jsonData: { + userId: this.userId, + groupId: record.getData().id + }, + callback: function (options, success, response) { + if (!success) { + Traccar.app.showError(response); + } + } + }); + } +}); diff --git a/web/app/view/Users.js b/web/app/view/Users.js index f4ef332b4..408a70885 100644 --- a/web/app/view/Users.js +++ b/web/app/view/Users.js @@ -35,6 +35,11 @@ Ext.define('Traccar.view.Users', { disabled: true, handler: 'onDevicesClick', reference: 'userDevicesButton' + }, { + text: Strings.settingsGroups, + disabled: true, + handler: 'onGroupsClick', + reference: 'userGroupsButton' }] }, @@ -43,7 +48,7 @@ Ext.define('Traccar.view.Users', { }, columns: [{ - text: Strings.userName, + text: Strings.sharedName, dataIndex: 'name', flex: 1 }, { diff --git a/web/app/view/UsersController.js b/web/app/view/UsersController.js index 3d0e813e8..c48f57cf4 100644 --- a/web/app/view/UsersController.js +++ b/web/app/view/UsersController.js @@ -21,6 +21,7 @@ Ext.define('Traccar.view.UsersController', { requires: [ 'Traccar.view.UserDialog', 'Traccar.view.UserDevices', + 'Traccar.view.UserGroups', 'Traccar.view.BaseWindow' ], @@ -75,10 +76,22 @@ Ext.define('Traccar.view.UsersController', { }).show(); }, + onGroupsClick: function () { + var user = this.getView().getSelectionModel().getSelection()[0]; + Ext.create('Traccar.view.BaseWindow', { + title: Strings.settingsGroups, + items: { + xtype: 'userGroupsView', + user: user + } + }).show(); + }, + onSelectionChange: function (selected) { var disabled = selected.length > 0; this.lookupReference('toolbarEditButton').setDisabled(disabled); this.lookupReference('toolbarRemoveButton').setDisabled(disabled); this.lookupReference('userDevicesButton').setDisabled(disabled); + this.lookupReference('userGroupsButton').setDisabled(disabled); } }); diff --git a/web/arrowstyle.js b/web/arrowstyle.js index c1eb88909..ad0ba08ee 100644 --- a/web/arrowstyle.js +++ b/web/arrowstyle.js @@ -1,12 +1,10 @@ goog.provide('ol.style.Arrow'); goog.require('goog.asserts'); -goog.require('goog.dom'); goog.require('ol'); goog.require('ol.color'); goog.require('ol.has'); goog.require('ol.render.canvas'); -goog.require('ol.structs.IHasChecksum'); goog.require('ol.style.AtlasManager'); goog.require('ol.style.Fill'); goog.require('ol.style.Image'); @@ -22,7 +20,6 @@ goog.require('ol.style.Stroke'); * @constructor * @param {olx.style.ArrowOptions} options Options. * @extends {ol.style.Image} - * @implements {ol.structs.IHasChecksum} * @api */ ol.style.Arrow = function(options) { @@ -119,9 +116,15 @@ ol.style.Arrow = function(options) { var snapToPixel = options.snapToPixel !== undefined ? options.snapToPixel : true; + /** + * @type {boolean} + */ + var rotateWithView = options.rotateWithView !== undefined ? + options.rotateWithView : false; + goog.base(this, { opacity: 1, - rotateWithView: false, + rotateWithView: rotateWithView, rotation: options.rotation !== undefined ? options.rotation : 0, scale: 1, snapToPixel: snapToPixel @@ -283,7 +286,7 @@ ol.style.Arrow.RenderOptions; /** * @private - * @param {ol.style.AtlasManager|undefined} atlasManager + * @param {ol.style.AtlasManager|undefined} atlasManager An atlas manager. */ ol.style.Arrow.prototype.render_ = function(atlasManager) { var imageSize; @@ -334,7 +337,7 @@ ol.style.Arrow.prototype.render_ = function(atlasManager) { if (atlasManager === undefined) { // no atlas manager is used, create a new canvas this.canvas_ = /** @type {HTMLCanvasElement} */ - (goog.dom.createElement('CANVAS')); + (document.createElement('CANVAS')); this.canvas_.height = size; this.canvas_.width = size; @@ -357,12 +360,12 @@ ol.style.Arrow.prototype.render_ = function(atlasManager) { if (hasCustomHitDetectionImage) { // render the hit-detection image into a separate atlas image renderHitDetectionCallback = - goog.bind(this.drawHitDetectionCanvas_, this, renderOptions); + this.drawHitDetectionCanvas_.bind(this, renderOptions); } var id = this.getChecksum(); var info = atlasManager.add( - id, size, size, goog.bind(this.draw_, this, renderOptions), + id, size, size, this.draw_.bind(this, renderOptions), renderHitDetectionCallback); goog.asserts.assert(info, 'arrow size is too large'); @@ -388,8 +391,8 @@ ol.style.Arrow.prototype.render_ = function(atlasManager) { /** * @private - * @param {ol.style.Arrow.RenderOptions} renderOptions - * @param {CanvasRenderingContext2D} context + * @param {ol.style.Arrow.RenderOptions} renderOptions Render options. + * @param {CanvasRenderingContext2D} context The rendering context. * @param {number} x The origin for the symbol (x). * @param {number} y The origin for the symbol (y). */ @@ -419,7 +422,7 @@ ol.style.Arrow.prototype.draw_ = function(renderOptions, context, x, y) { lineTo(this.radius_, 0); if (this.fill_) { - context.fillStyle = ol.color.asString(this.fill_.getColor()); + context.fillStyle = ol.colorlike.asColorLike(this.fill_.getColor()); context.fill(); } if (this.stroke_) { @@ -439,10 +442,9 @@ ol.style.Arrow.prototype.draw_ = function(renderOptions, context, x, y) { /** * @private - * @param {ol.style.Arrow.RenderOptions} renderOptions + * @param {ol.style.Arrow.RenderOptions} renderOptions Render options. */ -ol.style.Arrow.prototype.createHitDetectionCanvas_ = - function(renderOptions) { +ol.style.Arrow.prototype.createHitDetectionCanvas_ = function(renderOptions) { this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size]; if (this.fill_) { this.hitDetectionCanvas_ = this.canvas_; @@ -452,7 +454,7 @@ ol.style.Arrow.prototype.createHitDetectionCanvas_ = // if no fill style is set, create an extra hit-detection image with a // default fill style this.hitDetectionCanvas_ = /** @type {HTMLCanvasElement} */ - (goog.dom.createElement('CANVAS')); + (document.createElement('CANVAS')); var canvas = this.hitDetectionCanvas_; canvas.height = renderOptions.size; @@ -466,13 +468,12 @@ ol.style.Arrow.prototype.createHitDetectionCanvas_ = /** * @private - * @param {ol.style.Arrow.RenderOptions} renderOptions - * @param {CanvasRenderingContext2D} context + * @param {ol.style.Arrow.RenderOptions} renderOptions Render options. + * @param {CanvasRenderingContext2D} context The context. * @param {number} x The origin for the symbol (x). * @param {number} y The origin for the symbol (y). */ -ol.style.Arrow.prototype.drawHitDetectionCanvas_ = - function(renderOptions, context, x, y) { +ol.style.Arrow.prototype.drawHitDetectionCanvas_ = function(renderOptions, context, x, y) { var innerRadius = this.radius_ / Math.sin(Math.PI - this.backAngle_ / 2) * Math.sin(this.backAngle_ / 2 - this.frontAngle_); @@ -511,7 +512,7 @@ ol.style.Arrow.prototype.drawHitDetectionCanvas_ = /** - * @inheritDoc + * @return {string} The checksum. */ ol.style.Arrow.prototype.getChecksum = function() { var strokeChecksum = this.stroke_ ? diff --git a/web/debug.html b/web/debug.html index 3608930c0..351bb9f9f 100644 --- a/web/debug.html +++ b/web/debug.html @@ -5,7 +5,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>Traccar</title> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/resources/theme-neptune-all.css"> -<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/ol3/3.12.1/ol.css"> +<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/ol3/3.14.1/ol.css"> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"> <link rel="stylesheet" href="app.css"> </head> @@ -14,8 +14,11 @@ <div id="attribution">Powered by <a href="https://www.traccar.org/">Traccar GPS Tracking System</a></div> <script src="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all-debug.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/theme-neptune.js"></script> -<script src="//cdnjs.cloudflare.com/ajax/libs/ol3/3.12.1/ol-debug.js"></script> +<script src="//cdnjs.cloudflare.com/ajax/libs/ol3/3.14.1/ol-debug.js"></script> <script src="arrowstyle.js"></script> +<script type="text/javascript"> +Ext.Loader.setConfig({ disableCaching: false }); +</script> <script src="locale.js"></script> <script src="app.js"></script> </body> diff --git a/web/l10n/en.json b/web/l10n/en.json index ec8018863..cabd76362 100644 --- a/web/l10n/en.json +++ b/web/l10n/en.json @@ -13,10 +13,10 @@ "sharedHour": "Hour", "sharedMinute": "Minute", "sharedSecond": "Second", + "sharedName": "Name", "errorTitle": "Error", "errorUnknown": "Unknown error", "errorConnection": "Connection error", - "userName": "Name", "userEmail": "Email", "userPassword": "Password", "userAdmin": "Admin", @@ -30,13 +30,15 @@ "devicesAndState": "Devices and State", "deviceDialog": "Device", "deviceTitle": "Devices", - "deviceName": "Name", "deviceIdentifier": "Identifier", "deviceLastUpdate": "Last Update", "deviceCommand": "Command", "deviceFollow": "Follow", + "groupDialog": "Group", + "groupParent": "Group", "settingsTitle": "Settings", "settingsUser": "Account", + "settingsGroups": "Groups", "settingsServer": "Server", "settingsUsers": "Users", "settingsDistanceUnit": "Distance", diff --git a/web/l10n/es.json b/web/l10n/es.json index 2ace91caf..7cca1c8f2 100644 --- a/web/l10n/es.json +++ b/web/l10n/es.json @@ -1,6 +1,6 @@ { "sharedLoading": "Cargando...", - "sharedSave": "Grabar", + "sharedSave": "Guardar", "sharedCancel": "Cancelar", "sharedAdd": "Agregar", "sharedEdit": "Editar", @@ -12,14 +12,14 @@ "sharedMph": "MPH", "sharedHour": "Hora", "sharedMinute": "Minuto", - "sharedSecond": "Segundos", + "sharedSecond": "Segundo", "errorTitle": "Error", "errorUnknown": "Error Desconocido", "errorConnection": "Error de Conexión", "userName": "Nombre", "userEmail": "Email", "userPassword": "Contraseña", - "userAdmin": "Admin", + "userAdmin": "Administrador", "loginTitle": "Ingresar", "loginLanguage": "Idioma", "loginRegister": "Registrar", @@ -27,14 +27,14 @@ "loginFailed": "Dirección de Correo o Contraseña Incorrecta", "loginCreated": "Nuevo Usuario ha sido registrado", "loginLogout": "Salir", - "devicesAndState": "Devices and State", + "devicesAndState": "Dispositivos y Estado", "deviceDialog": "Dispositivo", "deviceTitle": "Dispositivos", "deviceName": "Nombre", "deviceIdentifier": "Identificador", - "deviceLastUpdate": "Last Update", + "deviceLastUpdate": "Última Actualización", "deviceCommand": "Comando", - "deviceFollow": "Follow", + "deviceFollow": "Seguir", "settingsTitle": "Preferencias", "settingsUser": "Cuenta", "settingsServer": "Servidor", @@ -59,7 +59,7 @@ "serverTitle": "Preferencias Servidor", "serverZoom": "Zoom", "serverRegistration": "Registrar", - "serverReadonly": "Readonly", + "serverReadonly": "Sólo Lectura", "mapTitle": "Mapa", "mapLayer": "Capa de Mapa", "mapCustom": "Mapa Personalizado", diff --git a/web/l10n/fa.json b/web/l10n/fa.json new file mode 100644 index 000000000..3497dff23 --- /dev/null +++ b/web/l10n/fa.json @@ -0,0 +1,83 @@ +{ + "sharedLoading": "در حال بارگزارى ...", + "sharedSave": "ذخيره", + "sharedCancel": "Cancel", + "sharedAdd": "اضافه كردن", + "sharedEdit": "Edit", + "sharedRemove": "Remove", + "sharedRemoveConfirm": "Remove item?", + "sharedKm": "km", + "sharedMi": "mi", + "sharedKmh": "km/h", + "sharedMph": "mph", + "sharedHour": "Hour", + "sharedMinute": "دقيقه", + "sharedSecond": "ثانيه", + "errorTitle": "Error", + "errorUnknown": "Unknown error", + "errorConnection": "Connection error", + "userName": "نام", + "userEmail": "نام كاربرى ( ايميل )", + "userPassword": "گذرواژه", + "userAdmin": "Admin", + "loginTitle": "ورود", + "loginLanguage": "انتخاب زبان", + "loginRegister": "ثبت نام", + "loginLogin": "ورود", + "loginFailed": "نام كاربرى ( ايميل ) يا گذرواژه اشتباه است", + "loginCreated": "ثبت نام با موفقيت انجام شد", + "loginLogout": "خروج", + "devicesAndState": "Devices and State", + "deviceDialog": "دستگاه", + "deviceTitle": "دستگاه ها", + "deviceName": "نام خودرو", + "deviceIdentifier": "سريال دستگاه", + "deviceLastUpdate": "آخرين بروزرسانى", + "deviceCommand": "Command", + "deviceFollow": "Follow", + "settingsTitle": "تنظيمات", + "settingsUser": "حساب كاربرى", + "settingsServer": "Server", + "settingsUsers": "Users", + "settingsDistanceUnit": "Distance", + "settingsSpeedUnit": "سرعت", + "reportTitle": "گزارشات ", + "reportDevice": "دستگاه", + "reportFrom": "از", + "reportTo": "To", + "reportShow": "Show", + "reportClear": "Clear", + "positionFixTime": "Time", + "positionValid": "Valid", + "positionLatitude": "عرض جغرافيايى", + "positionLongitude": "طول جغرافيايى", + "positionAltitude": "Altitude", + "positionSpeed": "سرعت", + "positionCourse": "Course", + "positionAddress": "Address", + "positionProtocol": "Protocol", + "serverTitle": "Server Settings", + "serverZoom": "Zoom", + "serverRegistration": "Registration", + "serverReadonly": "Readonly", + "mapTitle": "Map", + "mapLayer": "Map Layer", + "mapCustom": "Custom Map", + "mapOsm": "Open Street Map", + "mapBingKey": "Bing Maps Key", + "mapBingRoad": "Bing Maps Road", + "mapBingAerial": "Bing Maps Aerial", + "stateTitle": "State", + "stateName": "Attribute", + "stateValue": "Value", + "commandTitle": "Command", + "commandSend": "ارسال", + "commandType": "Type", + "commandSent": "Command has been sent", + "commandPositionPeriodic": "Periodic Reporting", + "commandPositionStop": "Stop Reporting", + "commandEngineStop": "Engine Stop", + "commandEngineResume": "Engine Resume", + "commandFrequency": "Frequency", + "commandUnit": "Unit" +}
\ No newline at end of file diff --git a/web/l10n/fi.json b/web/l10n/fi.json new file mode 100644 index 000000000..dc44cb03a --- /dev/null +++ b/web/l10n/fi.json @@ -0,0 +1,83 @@ +{ + "sharedLoading": "Ladataan...", + "sharedSave": "Tallenna", + "sharedCancel": "Peruuta", + "sharedAdd": "Lisää", + "sharedEdit": "Muokkaa", + "sharedRemove": "Poista", + "sharedRemoveConfirm": "Poista kohde?", + "sharedKm": "km", + "sharedMi": "mi", + "sharedKmh": "km/h", + "sharedMph": "mph", + "sharedHour": "Tunti", + "sharedMinute": "Minuutti", + "sharedSecond": "Sekunti", + "errorTitle": "Virhe", + "errorUnknown": "Tuntematon virhe", + "errorConnection": "Yhteysvirhe", + "userName": "Nimi", + "userEmail": "Email", + "userPassword": "Salasana", + "userAdmin": "Ylläpito", + "loginTitle": "Kirjaudu", + "loginLanguage": "Kieli", + "loginRegister": "Rekisteröidy", + "loginLogin": "Kirjaudu", + "loginFailed": "Virheellinen email tai salasana", + "loginCreated": "Uusi käyttäjä on rekisteröitynyt", + "loginLogout": "Kirjaudu ulos", + "devicesAndState": "Laitteet ja Tilat", + "deviceDialog": "Laite", + "deviceTitle": "Laitteet", + "deviceName": "Nimi", + "deviceIdentifier": "Tunniste", + "deviceLastUpdate": "Viimeisin päivitys", + "deviceCommand": "Komento", + "deviceFollow": "Seuraa", + "settingsTitle": "Asetukset", + "settingsUser": "Tili", + "settingsServer": "Palvelin", + "settingsUsers": "Käyttäjät", + "settingsDistanceUnit": "Etäisyys", + "settingsSpeedUnit": "Nopeus", + "reportTitle": "Raportit", + "reportDevice": "Laite", + "reportFrom": "Mistä", + "reportTo": "Mihin", + "reportShow": "Näytä", + "reportClear": "Tyhjennä", + "positionFixTime": "Aika", + "positionValid": "Kelvollinen", + "positionLatitude": "Latitude", + "positionLongitude": "Longitude", + "positionAltitude": "Korkeus", + "positionSpeed": "Nopeus", + "positionCourse": "Suunta", + "positionAddress": "Osoite", + "positionProtocol": "Protokolla", + "serverTitle": "Palvelinasetukset", + "serverZoom": "Lähennä", + "serverRegistration": "Rekisteröinti", + "serverReadonly": "Vain luku", + "mapTitle": "Kartta", + "mapLayer": "Karttataso", + "mapCustom": "Oma kartta", + "mapOsm": "Open Street Map", + "mapBingKey": "Bing Maps avain", + "mapBingRoad": "Bign Maps tiet", + "mapBingAerial": "Bing Maps ilmakuva", + "stateTitle": "Tila", + "stateName": "Ominaisuus", + "stateValue": "Arvo", + "commandTitle": "Komento", + "commandSend": "Lähetä", + "commandType": "Tyyppi", + "commandSent": "Komento on lähetetty", + "commandPositionPeriodic": "Määräaikaisraportointi", + "commandPositionStop": "Lopeta raportointi", + "commandEngineStop": "Sammuta moottori", + "commandEngineResume": "Palauta moottori", + "commandFrequency": "Taajuus", + "commandUnit": "Yksikkö" +}
\ No newline at end of file diff --git a/web/l10n/lt.json b/web/l10n/lt.json index 3b32a9c3d..77e7b95a5 100644 --- a/web/l10n/lt.json +++ b/web/l10n/lt.json @@ -27,14 +27,14 @@ "loginFailed": "Neteisingas el.paštas ir/ar slaptažodis", "loginCreated": "Registracija sėmkinga", "loginLogout": "Atsijungti", - "devicesAndState": "Devices and State", + "devicesAndState": "Prietaisai ir Statusas", "deviceDialog": "Prietaisas", "deviceTitle": "Prietaisai", "deviceName": "Pavadinimas", "deviceIdentifier": "Identifikacinis kodas", "deviceLastUpdate": "Naujausias atnaujinimas", "deviceCommand": "Komanda", - "deviceFollow": "Follow", + "deviceFollow": "Sekti", "settingsTitle": "Nustatymai", "settingsUser": "Paskyra", "settingsServer": "Serveris", @@ -59,7 +59,7 @@ "serverTitle": "Serverio nustatymai", "serverZoom": "Priartinimas", "serverRegistration": "Registracija", - "serverReadonly": "Readonly", + "serverReadonly": "Tik skaityti", "mapTitle": "Žemėlapis", "mapLayer": "Žemėlapio sluoksnis", "mapCustom": "Pasirinktinis Žemėlapis", diff --git a/web/l10n/ml.json b/web/l10n/ml.json new file mode 100644 index 000000000..02bd39b00 --- /dev/null +++ b/web/l10n/ml.json @@ -0,0 +1,83 @@ +{ + "sharedLoading": "ലോഡുചെയ്യുന്നു ..", + "sharedSave": "Save", + "sharedCancel": "റദ്ദാക്കുക", + "sharedAdd": "ചേര്ക്കുക", + "sharedEdit": "തിരുത്തുക", + "sharedRemove": "നീക്കം ചെയ്യുക", + "sharedRemoveConfirm": "വിഷയം നീക്കം ചെയ്യുക", + "sharedKm": "കിലോമീറ്റർ", + "sharedMi": "mi", + "sharedKmh": "കിലോമീറ്റർ / മണിക്കൂർ", + "sharedMph": "mph", + "sharedHour": "മണിക്കൂര്", + "sharedMinute": "മിനിറ്റ്", + "sharedSecond": "സെക്കന്റ്", + "errorTitle": "പിശക്", + "errorUnknown": "അജ്ഞാത പിശക്", + "errorConnection": "കണക്ഷൻ പിശക്", + "userName": "പേര്", + "userEmail": "ഇമെയിൽ", + "userPassword": "രഹസ്യ കോഡ്", + "userAdmin": "Admin", + "loginTitle": "അകത്തു പ്രവേശിക്കുക", + "loginLanguage": "ഭാഷ", + "loginRegister": "Register", + "loginLogin": "അകത്തു പ്രവേശിക്കുക", + "loginFailed": "തെറ്റായ ഇമെയിൽ വിലാസവും പാസ്വേഡും", + "loginCreated": "പുതിയ ഉപയോക്താവ് രജിസ്റ്റർ ചെയ്തു", + "loginLogout": "പുറത്തുകടക്കുക", + "devicesAndState": "Devices and State", + "deviceDialog": "ഉപകരണം", + "deviceTitle": "Devices", + "deviceName": "പേര്", + "deviceIdentifier": "Identifier", + "deviceLastUpdate": "Last Update", + "deviceCommand": "Command", + "deviceFollow": "Follow", + "settingsTitle": "Settings", + "settingsUser": "Account", + "settingsServer": "Server", + "settingsUsers": "Users", + "settingsDistanceUnit": "Distance", + "settingsSpeedUnit": "വേഗം", + "reportTitle": "Reports", + "reportDevice": "ഉപകരണം", + "reportFrom": "From", + "reportTo": "To", + "reportShow": "Show", + "reportClear": "Clear", + "positionFixTime": "സമയം", + "positionValid": "Valid", + "positionLatitude": "അക്ഷാംശം", + "positionLongitude": "രേഖാംശം", + "positionAltitude": "Altitude", + "positionSpeed": "വേഗം", + "positionCourse": "Course", + "positionAddress": "Address", + "positionProtocol": "Protocol", + "serverTitle": "Server Settings", + "serverZoom": "വലുതാക്കിയോ ചെറുതാക്കിയോ കാണിക്കുക", + "serverRegistration": "രജിസ്ട്രേഷൻ", + "serverReadonly": "Readonly", + "mapTitle": "ഭൂപടം", + "mapLayer": "Map Layer", + "mapCustom": "Custom Map", + "mapOsm": "Open Street Map", + "mapBingKey": "Bing Maps Key", + "mapBingRoad": "Bing Maps Road", + "mapBingAerial": "Bing Maps Aerial", + "stateTitle": "State", + "stateName": "Attribute", + "stateValue": "Value", + "commandTitle": "Command", + "commandSend": "Send", + "commandType": "Type", + "commandSent": "Command has been sent", + "commandPositionPeriodic": "Periodic Reporting", + "commandPositionStop": "Stop Reporting", + "commandEngineStop": "Engine Stop", + "commandEngineResume": "Engine Resume", + "commandFrequency": "Frequency", + "commandUnit": "Unit" +}
\ No newline at end of file diff --git a/web/l10n/ms.json b/web/l10n/ms.json new file mode 100644 index 000000000..ff5f02f01 --- /dev/null +++ b/web/l10n/ms.json @@ -0,0 +1,83 @@ +{ + "sharedLoading": "Memuatkan...", + "sharedSave": "Simpan", + "sharedCancel": "Batal", + "sharedAdd": "Tambah", + "sharedEdit": "Ubah", + "sharedRemove": "Hapus", + "sharedRemoveConfirm": "Hapuskan item?", + "sharedKm": "km", + "sharedMi": "mi", + "sharedKmh": "km/h", + "sharedMph": "mph", + "sharedHour": "Jam", + "sharedMinute": "Minit", + "sharedSecond": "Saat", + "errorTitle": "Ralat", + "errorUnknown": "Ralat tidak diketahui", + "errorConnection": "Ralat penyambungan", + "userName": "Nama", + "userEmail": "Emel", + "userPassword": "Katalaluan", + "userAdmin": "Admin", + "loginTitle": "Log masuk", + "loginLanguage": "Bahasa", + "loginRegister": "Daftar", + "loginLogin": "Log masuk", + "loginFailed": "Kesalahan emel atau katalaluan", + "loginCreated": "Pengguna baru telah didaftarkan", + "loginLogout": "Keluar", + "devicesAndState": "Peranti dan State", + "deviceDialog": "Peranti", + "deviceTitle": "Peranti", + "deviceName": "Nama", + "deviceIdentifier": "IMEI/ID", + "deviceLastUpdate": "Kemaskini Terakhir", + "deviceCommand": "Arahan", + "deviceFollow": "Ikut", + "settingsTitle": "Tetapan", + "settingsUser": "Akaun", + "settingsServer": "Server", + "settingsUsers": "Pengguna", + "settingsDistanceUnit": "Jarak", + "settingsSpeedUnit": "Kelajuan", + "reportTitle": "Laporan", + "reportDevice": "Peranti", + "reportFrom": "Daripada", + "reportTo": "Ke", + "reportShow": "Papar", + "reportClear": "Kosongkan", + "positionFixTime": "Masa", + "positionValid": "Sah", + "positionLatitude": "Latitud", + "positionLongitude": "Longitud", + "positionAltitude": "Altitud", + "positionSpeed": "Kelajuan", + "positionCourse": "Course", + "positionAddress": "Alamat", + "positionProtocol": "Protokol", + "serverTitle": "Tetapan Server", + "serverZoom": "Besarkan", + "serverRegistration": "Pendaftaran", + "serverReadonly": "Baca Sahaja", + "mapTitle": "Peta", + "mapLayer": "Map Layer", + "mapCustom": "Peta Lain", + "mapOsm": "Open Street Map", + "mapBingKey": "Bing Maps Key", + "mapBingRoad": "Bing Maps Road", + "mapBingAerial": "Bing Maps Aerial", + "stateTitle": "Negeri", + "stateName": "Atribut", + "stateValue": "Nilai", + "commandTitle": "Arahan", + "commandSend": "Hantar", + "commandType": "Jenis", + "commandSent": "Arahan telah dihantar", + "commandPositionPeriodic": "Laporan Berkala", + "commandPositionStop": "Hentikan Laporan", + "commandEngineStop": "Matikan Enjin", + "commandEngineResume": "Hidupkan Enjin", + "commandFrequency": "Frekuensi", + "commandUnit": "Unit" +}
\ No newline at end of file diff --git a/web/l10n/no.json b/web/l10n/no.json index dcfef54ce..d3dbb003e 100644 --- a/web/l10n/no.json +++ b/web/l10n/no.json @@ -67,7 +67,7 @@ "mapBingKey": "Bing Maps Nøkkel", "mapBingRoad": "Bing Maps Vei", "mapBingAerial": "Bing Maps Fly", - "stateTitle": "Stat", + "stateTitle": "Status", "stateName": "Egenskap", "stateValue": "Verdi", "commandTitle": "Kommando", diff --git a/web/l10n/tr.json b/web/l10n/tr.json new file mode 100644 index 000000000..6eacb12d0 --- /dev/null +++ b/web/l10n/tr.json @@ -0,0 +1,83 @@ +{ + "sharedLoading": "Yükleniyor...", + "sharedSave": "Kaydet", + "sharedCancel": "İptal", + "sharedAdd": "Ekle", + "sharedEdit": "Düzenle", + "sharedRemove": "Kaldır", + "sharedRemoveConfirm": "Öğeyi kaldır", + "sharedKm": "km", + "sharedMi": "mil", + "sharedKmh": "km/s", + "sharedMph": "mil/s", + "sharedHour": "Saat", + "sharedMinute": "Dakika", + "sharedSecond": "Saniye", + "errorTitle": "Hata", + "errorUnknown": "Bilinmeyen hata ", + "errorConnection": "Bağlantı Hatası", + "userName": "Ad", + "userEmail": "Eposta", + "userPassword": "Şifre", + "userAdmin": "Yönetici", + "loginTitle": "Oturum aç", + "loginLanguage": "Lisan", + "loginRegister": "Kayıt", + "loginLogin": "Oturumu aç", + "loginFailed": "Geçersiz eposta veya şifre", + "loginCreated": "Yeni kullanıcı kaydedildi", + "loginLogout": "Oturumu sonlandır", + "devicesAndState": "Cihazlar ve Bölge", + "deviceDialog": "Cihaz", + "deviceTitle": "Cihazlar", + "deviceName": "İsim", + "deviceIdentifier": "Kimlik", + "deviceLastUpdate": "Son Güncelleme", + "deviceCommand": "Komut", + "deviceFollow": "Takip", + "settingsTitle": "Ayarlar", + "settingsUser": "Hesap", + "settingsServer": "Sunucu", + "settingsUsers": "Kullanıcı", + "settingsDistanceUnit": "Mesafe", + "settingsSpeedUnit": "Hız", + "reportTitle": "Raporlar", + "reportDevice": "Aygıt", + "reportFrom": "Başlangıç", + "reportTo": "Varış", + "reportShow": "Göster", + "reportClear": "Temizle", + "positionFixTime": "Süre", + "positionValid": "Geçerli", + "positionLatitude": "Enlem", + "positionLongitude": "Boylam", + "positionAltitude": "Rakım", + "positionSpeed": "Sürat", + "positionCourse": "Yön", + "positionAddress": "Adres", + "positionProtocol": "Protokol", + "serverTitle": "Sunucu Ayarları", + "serverZoom": "Yakınlaştırma", + "serverRegistration": "Kayıt", + "serverReadonly": "Saltokunur", + "mapTitle": "Harita", + "mapLayer": "Harita Katmanı", + "mapCustom": "Özelleştirilmiş Harita", + "mapOsm": "Open Street Map", + "mapBingKey": "Bing Maps Key", + "mapBingRoad": "Bing Maps Road", + "mapBingAerial": "Bing Maps Aerial", + "stateTitle": "Bölge", + "stateName": "Özellik", + "stateValue": "Değer", + "commandTitle": "Komut", + "commandSend": "Gönder", + "commandType": "Tip", + "commandSent": "Komut gönderildi", + "commandPositionPeriodic": "Periyodik Rapor", + "commandPositionStop": "Raporlamayı Durdur", + "commandEngineStop": "Motoru Durdur", + "commandEngineResume": "Motoru Çalıştır", + "commandFrequency": "Frekans", + "commandUnit": "Ünite" +}
\ No newline at end of file diff --git a/web/locale.js b/web/locale.js index 7dc94ec45..c0b3af90b 100644 --- a/web/locale.js +++ b/web/locale.js @@ -16,9 +16,6 @@ var Locale = {}; -Ext.Ajax.disableCaching = false; -Ext.Loader.disableCaching = false; - Locale.languages = { 'ar': { name: 'العربية', code: 'en' }, 'bg': { name: 'Български', code: 'bg' }, @@ -28,12 +25,16 @@ Locale.languages = { 'el': { name: 'Ελληνικά', code: 'el' }, 'en': { name: 'English', code: 'en' }, 'es': { name: 'Español', code: 'es' }, + 'fa': { name: 'فارسی', code: 'fa' }, + 'fi': { name: 'Suomi', code: 'fi' }, 'fr': { name: 'Français', code: 'fr' }, 'hu': { name: 'Magyar', code: 'hu' }, 'id': { name: 'Bahasa Indonesia', code: 'id' }, 'it': { name: 'Italiano', code: 'it' }, 'ka': { name: 'ქართული', code: 'en' }, 'lt': { name: 'Lietuvių', code: 'lt' }, + 'ml': { name: 'മലയാളം', code: 'en' }, + 'ms': { name: 'بهاس ملايو', code: 'en' }, 'nl': { name: 'Nederlands', code: 'nl' }, 'no': { name: 'Norsk', code: 'no_NB' }, 'pl': { name: 'Polski', code: 'pl' }, @@ -47,6 +48,7 @@ Locale.languages = { 'sr': { name: 'Srpski', code: 'sr' }, 'ta': { name: 'தமிழ்', code: 'en' }, 'th': { name: 'ไทย', code: 'th' }, + 'tr': { name: 'Türkçe', code: 'tr' }, 'uk': { name: 'Українська', code: 'ukr' }, 'vi': { name: 'Tiếng Việt', code: 'en' }, 'zh': { name: '中文', code: 'zh_CN' } diff --git a/web/release.html b/web/release.html index d5fc60e2b..155e13f1a 100644 --- a/web/release.html +++ b/web/release.html @@ -5,7 +5,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>Traccar</title> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/resources/theme-neptune-all.css"> -<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/ol3/3.12.1/ol.min.css"> +<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/ol3/3.14.1/ol.min.css"> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"> <link rel="stylesheet" href="app.css"> </head> @@ -14,7 +14,7 @@ <div id="attribution">Powered by <a href="https://www.traccar.org/">Traccar GPS Tracking System</a></div> <script src="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-neptune/theme-neptune.js"></script> -<script src="//cdnjs.cloudflare.com/ajax/libs/ol3/3.12.1/ol-debug.min.js"></script> +<script src="//cdnjs.cloudflare.com/ajax/libs/ol3/3.14.1/ol-debug.min.js"></script> <script src="arrowstyle.js"></script> <script src="locale.js"></script> <script type="text/javascript"> |