Improved the devices map a lot
Ylian Saint-Hilaire committed
Aug 29, 2017 at 14:34 UTC
7d21d41397570db3279e8917a6346fcad86bcf30
3 files changed
+716
-38
package.json
+1
-1
@@ -1,6 +1,6 @@
1
{
2
"name": "meshcentral",
3
- "version": "0.0.6-r",
3
+ "version": "0.0.6-t",
4
"keywords": [
5
"Remote Management",
6
"Intel AMT",
public/images/mapmarker.png
Binary files /dev/null and b/public/images/mapmarker.png differ
views/default.handlebars
+715
-37
@@ -105,10 +105,15 @@
105
<div class=h1 style=height:100%;float:left> </div>
106
<div id=devListToolbar class=style14 style=height:100%;float:left>
107
<input type="button" id="SelectAllButton" onclick="selectallButtonFunction();" value="Select All" />
108
- <input type="button" id="GroupActionButton" disabled="disabled" value="Group Action" onclick="groupActionFunction();" />
109
- <input id="SearchInput" type="text" style=width:120px placeholder=Search onchange="onSearchInputChanged()" onkeyup="onSearchInputChanged()" autocomplete=off onfocus="onSearchFocus(1)" onblur="onSearchFocus(0)" />
108
+ <input type=button id=GroupActionButton disabled="disabled" value="Group Action" onclick=groupActionFunction() />
109
+ <input id=SearchInput type=text style=width:120px placeholder=Search onchange=onSearchInputChanged() onkeyup=onSearchInputChanged() autocomplete=off onfocus=onSearchFocus(1) onblur=onSearchFocus(0) />
110
<input type=checkbox id=HostnameCheckBox onclick=onHostnameCheckBox() /><span title="Show device hostnames">Hostname</span>
111
</div>
112
+ <div id=devMapToolbar class=style14 style=height:100%;float:left>
113
+ <input type=text id=mapSearchLocation placeholder="Search Location" onfocus=onMapSearchFocus(1) onblur=onMapSearchFocus(0) />
114
+ <input type=button value=Search title="Search for location" onclick=getSearchLocation() />
115
+ <input type=button id=refreshmap title="Reset map view" value=Reset style=margin-left:5px onclick=refreshMap(false,true) />
116
+ </div>
117
<div class="auto-style1" style="height: 100%; float: right">
118
<div style="height:100%;width:4px;float:right;background-color:#ffffff"></div>
119
<div class=h2 style="height:100%;float:right"> </div>
@@ -145,7 +150,17 @@
150
<p></p>
151
</div>
152
<div id="xdevices" style="max-height:calc(100vh - 228px);overflow-y:auto;-webkit-overflow-scrolling:touch"></div>
148
- <div id="xdevicesmap" style="height:500px;width:100%;overflow:hidden"></div>
153
+ <div id="xdevicesmap" style="height:500px;width:100%;overflow:hidden;position:relative">
154
+ <div id=xmapSearchResultsDlg style="position:absolute;display:none;max-height:280px;left:5px;top:5px;max-width:250px;z-index:1000;background-color:#EEE;box-shadow:0px 0px 15px #666">
155
+ <div style="width:100%;background-color:#003366;color:#FFF;border-radius:5px 5px 0 0">
156
+ <div id=xmapSearchClose style=float:right;padding:5px;cursor:pointer onclick=mapCloseSearchWindow()><b>X</b></div>
157
+ <div style=padding:5px>Location Results</div>
158
+ <div style=width:100%;margin:6px></div>
159
+ </div>
160
+ <div id=xmapSearchResults style=margin:6px></div>
161
+ </div>
162
+ </div>
163
+ <div id="xmap-info-window" style="text-shadow: 0px 0px 15px #FFF"></div>
164
</div>
165
<div id=p2 style=display:none>
166
<h1>My Account</h1>
@@ -580,6 +595,7 @@
595
var StatusStrs = ['Disconnected', 'Connecting...', 'Setup...', 'Connected', 'Intel® AMT Connected'];
596
var sort = 0;
597
var searchFocus = 0;
598
+ var mapSearchFocus = 0;
599
var consoleFocus = 0;
600
var showHostnames = false;
601
var meshserver = null;
@@ -600,7 +616,7 @@
616
var features = {{{features}}};
617
var serverPublicNamePort = "{{{serverDnsName}}}:{{{serverPublicPort}}}";
618
var amtScanResults = null;
603
- var xxmap = null;
619
+ //var xxmap = null;
620
621
function startup() {
622
// Guard against other site's top frames (web bugs).
@@ -662,13 +678,6 @@
678
var x = '';
679
for (var c = 1; c < 27; c++) x += "<option value='" + c + "'>Ctrl-" + String.fromCharCode(64 + c) + " (" + c + ")</option>";
680
QH('specialkeylist', x);
665
-
666
- // Setup the map
667
- xxmap = new ol.Map({
668
- target: 'xdevicesmap',
669
- layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ],
670
- view: new ol.View({ center: ol.proj.fromLonLat([0, 0]), zoom: 1.8, minZoom: 1.8 })
671
- });
681
}
682
683
function getNodeFromId(id) {
@@ -784,6 +793,7 @@
793
onSortSelectChange();
794
onSearchInputChanged();
795
updateDevices();
796
+ refreshMap(false, true);
797
if (xxcurrentView == 0) { if ('{{viewmode}}' != '') { go({{viewmode}}); } else { go(1); } }
798
if ('{{currentNode}}' != '') { gotoDevice('{{currentNode}}',{{viewmode}});}
799
break;
@@ -961,6 +971,7 @@
971
onSortSelectChange();
972
onSearchInputChanged();
973
updateDevices();
974
+ updateMapMarkers();
975
break;
976
}
977
case 'removenode': {
@@ -974,6 +985,7 @@
985
}
986
nodes.splice(index, 1);
987
updateDevices();
988
+ updateMapMarkers();
989
}
990
break;
991
}
@@ -1012,6 +1024,7 @@
1024
onSortSelectChange(true);
1025
drawNotifications();
1026
refreshDevice(node._id);
1027
+ updateMapMarkers();
1028
}
1029
break;
1030
}
@@ -1026,6 +1039,7 @@
1039
node.conn = message.event.conn;
1040
node.pwr = message.event.pwr;
1041
updateDevices();
1042
+ updateMapMarkers();
1043
refreshDevice(node._id);
1044
}
1045
break;
@@ -1113,15 +1127,27 @@
1127
return;
1128
}
1129
if (e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
1116
- var processed = 0;
1117
- if (e.key) {
1118
- if (e.key.length === 1 && searchFocus == 0) { Q('SearchInput').value = ((Q('SearchInput').value + e.key)); processed = 1; }
1119
- if (e.keyCode == 8 && searchFocus == 0) { var x = Q('SearchInput').value; Q('SearchInput').value = x.substring(0, x.length - 1); processed = 1; }
1120
- if (e.keyCode == 27) { Q('SearchInput').value = ''; processed = 1; }
1121
- } else {
1122
- if (e.charCode != 0 && searchFocus == 0) { Q('SearchInput').value = ((Q('SearchInput').value + String.fromCharCode(e.charCode))); processed = 1; }
1130
+ if (Q('viewselect').value < 3) {
1131
+ var processed = 0;
1132
+ if (e.key) {
1133
+ if (e.key.length === 1 && searchFocus == 0) { Q('SearchInput').value = ((Q('SearchInput').value + e.key)); processed = 1; }
1134
+ if (e.keyCode == 8 && searchFocus == 0) { var x = Q('SearchInput').value; Q('SearchInput').value = x.substring(0, x.length - 1); processed = 1; }
1135
+ if (e.keyCode == 27) { Q('SearchInput').value = ''; processed = 1; }
1136
+ } else {
1137
+ if (e.charCode != 0 && searchFocus == 0) { Q('SearchInput').value = ((Q('SearchInput').value + String.fromCharCode(e.charCode))); processed = 1; }
1138
+ }
1139
+ if (processed > 0) { if (processed == 1) { onSearchInputChanged(); } return haltEvent(e); }
1140
+ }
1141
+ if (Q('viewselect').value == 3) {
1142
+ if (e.key) {
1143
+ if (e.key.length === 1 && mapSearchFocus == 0) { Q('mapSearchLocation').value = ((Q('mapSearchLocation').value + e.key)); processed = 1; }
1144
+ //if (e.keyCode == 8 && mapSearchFocus == 0) { var x = Q('mapSearchLocation').value; Q('mapSearchLocation').value = x.substring(0, x.length - 1); processed = 1; }
1145
+ if (e.keyCode == 27) { Q('mapSearchLocation').value = ''; mapCloseSearchWindow(); processed = 1; }
1146
+ if (e.keyCode == 13) { getSearchLocation(); }
1147
+ } else {
1148
+ if (e.charCode != 0 && mapSearchFocus == 0) { Q('mapSearchLocation').value = ((Q('mapSearchLocation').value + String.fromCharCode(e.charCode))); processed = 1; }
1149
+ }
1150
}
1124
- if (processed > 0) { if (processed == 1) { onSearchInputChanged(); } return haltEvent(e); }
1151
}
1152
1153
function ondockeydown(e) {
@@ -1130,9 +1156,15 @@
1156
if (!xxdialogMode && xxcurrentView == 13 && e.keyCode == 116 && p13filetree != null) { haltEvent(e); return false; } // F5 Refresh on files
1157
if (xxdialogMode || xxcurrentView != 1 || e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
1158
var processed = 0;
1133
- if (e.keyCode === 8 && searchFocus == 0) { var x = Q('SearchInput').value; Q('SearchInput').value = (x.substring(0, x.length - 1)); processed = 1; }
1134
- if (e.keyCode === 27) { Q('SearchInput').value = ''; processed = 1; }
1135
- if (processed > 0) { if (processed == 1) { onSearchInputChanged(); } return haltEvent(e); }
1159
+ if (Q('viewselect').value < 3) {
1160
+ if (e.keyCode === 8 && searchFocus == 0) { var x = Q('SearchInput').value; Q('SearchInput').value = (x.substring(0, x.length - 1)); processed = 1; }
1161
+ if (e.keyCode === 27) { Q('SearchInput').value = ''; processed = 1; }
1162
+ if (processed > 0) { if (processed == 1) { onSearchInputChanged(); } return haltEvent(e); }
1163
+ }
1164
+ if (Q('viewselect').value == 3) {
1165
+ if (e.keyCode === 8 && mapSearchFocus == 0) { var x = Q('mapSearchLocation').value; Q('mapSearchLocation').value = (x.substring(0, x.length - 1)); processed = 1; }
1166
+ if (e.keyCode === 27) { Q('mapSearchLocation').value = ''; mapCloseSearchWindow(); processed = 1; }
1167
+ }
1168
}
1169
1170
function ondockeyup(e) {
@@ -1140,7 +1172,8 @@
1172
if (!xxdialogMode && xxcurrentView == 12 && terminal && terminal.State == 3) return terminal.m.TermHandleKeyUp(e);
1173
if (!xxdialogMode && xxcurrentView == 13 && e.keyCode == 116 && p13filetree != null) { p13folderup(9999); haltEvent(e); return false; } // F5 Refresh on files
1174
if (xxdialogMode || xxcurrentView != 0 || e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
1143
- if ((e.keyCode === 8 && searchFocus == 0) || e.keyCode === 27) { return haltEvent(e); }
1175
+ if (Q('viewselect').value < 3) { if ((e.keyCode === 8 && searchFocus == 0) || e.keyCode === 27) { return haltEvent(e); } }
1176
+ if (Q('viewselect').value == 3) { if ((e.keyCode === 8 && mapSearchFocus == 0) || e.keyCode === 27) { return haltEvent(e); } }
1177
}
1178
1179
var deviceHeaderId = 0;
@@ -1151,9 +1184,10 @@
1184
QV('xdevices', view < 3);
1185
QV('xdevicesmap', view == 3);
1186
QV('devListToolbar', view < 3);
1187
+ QV('devMapToolbar', view == 3);
1188
QV('devListToolbarSort', view < 3);
1189
if (view == 3) {
1156
- setTimeout( function() { xxmap.updateSize();}, 200);
1190
+ setTimeout( function() { xxmap.map.updateSize();}, 200);
1191
// TODO
1192
} else {
1193
// 3 wide or list view
@@ -1546,6 +1580,7 @@
1580
function deviceHostSort(a, b) { if (a.hostl > b.hostl) return 1; if (a.hostl < b.hostl) return -1; return 0; }
1581
function onSearchInputChanged() { var x = Q('SearchInput').value.toLowerCase(); putstore("search", x); if (x == '') { for (var d in nodes) { nodes[d].v = true; } } else { for (var d in nodes) { nodes[d].v = (nodes[d].name.toLowerCase().indexOf(x) >= 0) || (nodes[d].hostl != undefined && nodes[d].hostl.toLowerCase().indexOf(x) >= 0); } } updateDevices(); }
1582
function onSearchFocus(x) { searchFocus = x; }
1583
+ function onMapSearchFocus(x) { mapSearchFocus = x; }
1584
function onConsoleFocus(x) { consoleFocus = x; }
1585
1586
var contextelement = null;
@@ -1602,6 +1637,661 @@
1637
contextelement = null;
1638
}
1639
1640
+ //
1641
+ // DEVICES MAP
1642
+ //
1643
+
1644
+ // Maps code starts from here. Initialize all the variables
1645
+ var xxmap = {
1646
+ map: null,
1647
+ contextmenu: null,
1648
+ activeInteractions: [], // Save Modified features in this list
1649
+ showindex: 0,
1650
+ markersSource: null, // Initialize a Source Vector
1651
+ markersLayer: null,
1652
+ mapLayer: null, // Create a tile and use OSM source
1653
+ mapView: null, // Sets the initial view
1654
+ selectedNodes: {} // Save details of Node that was selected from 'Put a node' option from contextmenu
1655
+ }
1656
+
1657
+ // Add a feature for every Node and change style if connection status changes
1658
+ function updateMapMarkers(selectedMesh) {
1659
+ var boundingBox = null;
1660
+ if (xxmap.map == null) loadmap();
1661
+ for (var i in nodes) {
1662
+ var loc = map_parseNodeLoc(nodes[i]);
1663
+ if (loc) { // Draw markers for devices with locations
1664
+ lat = loc[0];
1665
+ lon = loc[1];
1666
+ if (boundingBox == null) { boundingBox = [ lat, lon, lat, lon, 0 ]; } else { if (lat < boundingBox[0]) { boundingBox[0] = lat; } if (lon < boundingBox[1]) { boundingBox[1] = lon; } if (lat > boundingBox[2]) { boundingBox[2] = lat; } if (lon > boundingBox[3]) { boundingBox[3] = lon; } }
1667
+ var feature = xxmap.markersSource.getFeatureById(nodes[i]._id);
1668
+ if (nodes[i].meshid == selectedMesh || selectedMesh == undefined) {
1669
+ if (feature == null || feature == undefined) { addFeature(nodes[i]); boundingBox[4] = 1; } else { updateFeature(nodes[i], feature); } // Update Feature
1670
+ } else {
1671
+ if (feature) { xxmap.markersSource.removeFeature(feature); } // Remove if it does not belong to that mesh
1672
+ }
1673
+ }
1674
+ }
1675
+ return boundingBox;
1676
+ }
1677
+
1678
+ // Show node details on hovering over a feature
1679
+ var map_cm_popup = new ol.Overlay({ element: Q('xmap-info-window'), positioning: 'bottom-center', stopEvent: false });
1680
+
1681
+ // Edit Marker item
1682
+ var map_cm_editMarker = { text: "Modify Node location", callback: function (obj) { modifyMarkerloc(obj.data); } };
1683
+
1684
+ // Save Marker item
1685
+ var map_cm_saveMarker = { text: "Save Node location", callback: function (obj) { saveMarkerloc(obj.data); } };
1686
+
1687
+ // Build a context menu for a feature
1688
+ var map_cm_nodemenu_items = [
1689
+ { text: "General information", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 10); } } },
1690
+ { text: "Desktop", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 11); } } },
1691
+ { text: "Terminal", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 12); } } },
1692
+ { text: "Intel® AMT", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 14); } } },
1693
+ '-',
1694
+ { text: 'Zoom-in to extent', callback: function(obj) { var coords = obj.data.getGeometry().getCoordinates(); zoomToLocation(coords, 19); } },
1695
+ { text: 'Zoom-out to extent', callback: function(obj) { var coords = obj.data.getGeometry().getCoordinates(); zoomToLocation(coords, 2); } }
1696
+ ];
1697
+
1698
+ // Context menu for clicks other than on feature
1699
+ var contextmenu_items = [
1700
+ { text: 'Refresh', callback: function () { refreshMap(false, true); } },
1701
+ { text: 'Zoom to fit extent', callback: function () { zoomToFitExtent(); } },
1702
+ { text: 'Center map here', callback: function(obj) { xxmap.mapView.animate({ center: obj.coordinate } ); } },
1703
+ { text: 'Place a node', callback: function (obj) { placeNode(obj.coordinate); } }
1704
+ ];
1705
+
1706
+ function stringToIntHash(str) {
1707
+ var hash = 0, i;
1708
+ for (i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash |= 0; }
1709
+ return hash;
1710
+ };
1711
+
1712
+ // Get the lat/lon from a node
1713
+ function map_parseNodeLoc(node) {
1714
+ if (!node.iploc) return;
1715
+ var loc = node.iploc.split(',');
1716
+ return [parseFloat(loc[0]) + (stringToIntHash(node._id.substring(0, 20)) / 100000000000), parseFloat(loc[1]) + (stringToIntHash(node._id.substring(20)) / 100000000000)]
1717
+ }
1718
+
1719
+ // Load the entire map
1720
+ function loadmap() {
1721
+ // Initialize a Source Vector
1722
+ xxmap.markersSource = new ol.source.Vector();
1723
+
1724
+ xxmap.markersLayer = new ol.layer.Vector({
1725
+ source: xxmap.markersSource
1726
+ });
1727
+
1728
+ // Create a tile and use OSM source
1729
+ xxmap.mapLayer = new ol.layer.Tile({ source: new ol.source.OSM() });
1730
+
1731
+ xxmap.mapView = new ol.View({ // Set the initial view
1732
+ center: ol.proj.transform([0, 0], 'EPSG:4326', 'EPSG:3857'),
1733
+ zoom: 2,
1734
+ minZoom: 2,
1735
+ maxZoom: 20,
1736
+ extent: ol.proj.transformExtent([-100000, -69.55, 100000, 69.55], 'EPSG:4326', 'EPSG:3857')
1737
+ });
1738
+
1739
+ xxmap.map = new ol.Map({
1740
+ target: 'xdevicesmap',
1741
+ layers: [xxmap.mapLayer, xxmap.markersLayer],
1742
+ view: xxmap.mapView
1743
+ });
1744
+
1745
+ xxmap.map.addOverlay(map_cm_popup);
1746
+
1747
+ // Goto information tab if a user clicks on a feature
1748
+ xxmap.map.on('click', function(evt) {
1749
+ var feature = xxmap.map.forEachFeatureAtPixel(evt.pixel, function(feat, layer) { return feat; });
1750
+ if (feature) {
1751
+ var nodeid = feature.getId();
1752
+ if (nodeid != undefined) { gotoDevice(nodeid, 10); } // Goto general info tab
1753
+ else { // For pointer
1754
+ var nodeFeatgoto = getCorrespondingFeature(feature); gotoDevice(nodeFeatgoto.getId(), 10);
1755
+ }
1756
+ }
1757
+ });
1758
+
1759
+ // On hover feature show the name of the node. Also add pointer style
1760
+ xxmap.map.on('pointermove', function(evt) {
1761
+ var feature = xxmap.map.forEachFeatureAtPixel(evt.pixel, function(feat, layer) { return feat; });
1762
+ if (feature) {
1763
+ xxmap.map.getTargetElement().style.cursor = 'pointer';
1764
+ var coord = feature.getGeometry().getCoordinates();
1765
+ // map_cm_popup.setPosition(evt.coordinate);
1766
+ map_cm_popup.setPosition(coord);
1767
+ featid = feature.getId();
1768
+ if (featid) {
1769
+ QH('xmap-info-window', feature.get('name'));
1770
+ } else {
1771
+ var nodeFeat = getCorrespondingFeature(feature); // Return the node feature associated to pointer.
1772
+ QH('xmap-info-window', nodeFeat.get('name'));
1773
+ }
1774
+ } else {
1775
+ xxmap.map.getTargetElement().style.cursor = '';
1776
+ QH('xmap-info-window', '');
1777
+ }
1778
+ });
1779
+
1780
+ /*
1781
+ // Initialize context menu for openlayers
1782
+ contextmenu = new ContextMenu({
1783
+ width: 160,
1784
+ defaultItems: false, // defaultItems are Zoom In/Zoom Out
1785
+ items: contextmenu_items
1786
+ });
1787
+
1788
+ // On right click open the context menu
1789
+ contextmenu.on("open", function (evt) {
1790
+ var feature = xxmap.map.forEachFeatureAtPixel(evt.pixel, function(ft, l){ return ft; });
1791
+ xxmap.contextmenu.clear(); //Clear the context menu
1792
+ if (feature) {
1793
+ var featId=feature.getId();
1794
+ if (featId) { // Node feature will have an id
1795
+ addContextMenuItems(feature);
1796
+ }
1797
+ else { // If the feature is a pointer, Get its corresponding Node feature
1798
+ var nodeFeature= getCorrespondingFeature(feature); //return the node feature associated to pointer.
1799
+ if (nodeFeature) { addContextMenuItems(nodeFeature); }
1800
+ else{ xxmap.contextmenu.extend(contextmenu_items); }
1801
+ }
1802
+ }
1803
+ else { xxmap.contextmenu.extend(contextmenu_items); }
1804
+ });
1805
+ xxmap.map.addControl(xxmap.contextmenu);
1806
+ */
1807
+ //addMeshOptions(); // Adds Mesh names to mesh dropdown
1808
+ }
1809
+
1810
+ // Add feature on to Map for a Node
1811
+ function addFeature(node, lat, lon){
1812
+ var existingfeature = getModifiedFeature(node._id); // Check if Corresponding feature was Modified ( Modifed feature are in active interactions list)
1813
+ if (existingfeature) { xxmap.markersSource.addFeature(existingfeature); } // Add that existing feature
1814
+ else { // Add new feature for this node
1815
+ if (!lat && !lon) { var loc = map_parseNodeLoc(node); lat = loc[0]; lon = loc[1]; }
1816
+ var feature = new ol.Feature({ geometry: new ol.geom.Point(ol.proj.transform([lon, lat], 'EPSG:4326','EPSG:3857')), name: node.name, status: node.conn, lat: lat, lon: lon });
1817
+ feature.setId(node._id); // Set id for the device as nodeid
1818
+ feature.setStyle(markerStyle(node));
1819
+ xxmap.markersSource.addFeature(feature); // Add the feature to Marker Source
1820
+ }
1821
+ }
1822
+
1823
+ // Removing any feature from map
1824
+ function removeFeature(node) {
1825
+ if (node.iploc) {
1826
+ var feature = xxmap.markersSource.getFeatureById(node._id);
1827
+ if (feature) { xxmap.markersSource.removeFeature(feature); }
1828
+ }
1829
+ }
1830
+
1831
+ // Update feature
1832
+ function updateFeature(node, feature) {
1833
+ if (node.conn != feature.get('status') ) { // Update status if changed
1834
+ feature.set('status',node.conn)
1835
+ feature.setStyle(markerStyle(node));
1836
+ }
1837
+
1838
+ // Since this is IP address location, add some fixed randomness to the location. Avoid pin pile-up.
1839
+ var loc = map_parseNodeLoc(node); lat = loc[0]; lon = loc[1];
1840
+
1841
+ if ((lat != feature.get('lat')) || (lon != feature.get('lon'))) { // Update lat and lon if changed
1842
+ feature.set('lat', lat); feature.set('lon', lon);
1843
+ var modifiedCoordinates = ol.proj.transform([parseFloat(lon), parseFloat(lat)], 'EPSG:4326','EPSG:3857');
1844
+ feature.getGeometry().setCoordinates(modifiedCoordinates);
1845
+ }
1846
+
1847
+ if (node.name != feature.get('name') ) { feature.set('name', node.name); } // Update name
1848
+ }
1849
+
1850
+ // Enable dragging of a marker after edit option is clicked in context menu
1851
+ function modifyMarkerloc(ft){
1852
+ var featid = ft.getId();
1853
+ if (featid) {
1854
+ if ( !getActiveInteractions(ft)) {
1855
+ var dragInteration = new ol.interaction.Modify({
1856
+ features: new ol.Collection([ft]),
1857
+ pixelTolerance: 10
1858
+ });
1859
+ xxmap.activeInteractions.push({ featureid: featid, feature:ft, interaction: dragInteration }); // Also keep track of Interactions
1860
+ xxmap.map.addInteraction(dragInteration);
1861
+ }
1862
+ }
1863
+ }
1864
+
1865
+ // This will be called when save location option is clicked in context menu
1866
+ function saveMarkerloc(ft){
1867
+ var featid = ft.getId()
1868
+ if (featid) {
1869
+ var actInteraction = getActiveInteractions(ft);
1870
+ if (actInteraction) { // Check if the interaction exists
1871
+ xxmap.map.removeInteraction(actInteraction); //Clear Interaction for that node
1872
+ removeInteraction(featid);
1873
+ var coord = ft.getGeometry().getCoordinates();
1874
+ var v = ol.proj.transform(coord, 'EPSG:3857', 'EPSG:4326');
1875
+ meshserver.Send({ action: 'changelocation', nodeid: featid, value: v }); // Send them to server to save changes
1876
+ }
1877
+ }
1878
+ }
1879
+
1880
+ // Style the Markers
1881
+ function markerStyle(node) {
1882
+ var color = connStateColor(node);
1883
+ var style = new ol.style.Style({
1884
+ image: new ol.style.Icon({ color: color, anchor: [0.5, 1], src: 'images/mapmarker.png' })
1885
+ //stroke: new ol.style.Stroke({ color: '#000', width: 20 })
1886
+ //text: new ol.style.Text({ text: 'bob!', textAlign: 'right', offsetX: -10, fill: new ol.style.Fill({ color: '#000' }), stroke: new ol.style.Stroke({ color: '#fff', width: 2 }) })
1887
+ });
1888
+
1889
+ /*
1890
+ deviceMark.setStyle(new ol.style.Style({
1891
+ text: new ol.style.Text({
1892
+ //font: '12px helvetica,sans-serif',
1893
+ text: currentNode.name,
1894
+ textAlign: 'right',
1895
+ offsetX: -10,
1896
+ fill: new ol.style.Fill({ color: '#000' }),
1897
+ stroke: new ol.style.Stroke({ color: '#fff', width: 2 })
1898
+ }),
1899
+ image: new ol.style.Icon(({ color: [113, 140, 0], src: 'images/dot.png' })) }));
1900
+ */
1901
+
1902
+ return [ style ];
1903
+ }
1904
+
1905
+ // TODO: Add more connection status types. Currently we only change color if connection status changes
1906
+ function connStateColor(nodeConn){
1907
+ if (nodeConn.conn == 1 || nodeConn.conn == 5) { return '#00ffdd'; } // Green for connected devices
1908
+ return '#C70039'; // Red if the Agent is not connected
1909
+ }
1910
+
1911
+ // Add save/edit option to context menu
1912
+ function addContextMenuItems(feature) {
1913
+ if (getActiveInteractions(feature)) { // If this feature is modified then display save option in contextmenu
1914
+ map_cm_saveMarker.data = feature;
1915
+ xxmap.contextmenu.push(map_cm_saveMarker);
1916
+ } else {
1917
+ map_cm_editMarker.data = feature;
1918
+ xxmap.contextmenu.push(map_cm_editMarker);
1919
+ }
1920
+ map_cm_nodemenu_items.forEach(function (item){
1921
+ if (item.text == 'Zoom-in to extent' || item.text == 'Zoom-out to extent') { item.data = feature; }
1922
+ else { item.data = feature.getId(); }
1923
+ });
1924
+ xxmap.contextmenu.extend(map_cm_nodemenu_items);
1925
+ }
1926
+
1927
+ // Return a active Interaction if it exists in activeInteractions list
1928
+ function getActiveInteractions(feature) {
1929
+ var featid = feature.getId();
1930
+ for (var i = 0; i < xxmap.activeInteractions.length; i++) {
1931
+ if (xxmap.activeInteractions[i].featureid == featid) { return xxmap.activeInteractions[i].interaction; }
1932
+ }
1933
+ return false;
1934
+ }
1935
+
1936
+ // Return Modified feature based on Id
1937
+ function getModifiedFeature(featid) {
1938
+ if (featid) {
1939
+ for (var i = 0; i < xxmap.activeInteractions.length; i++) {
1940
+ if (xxmap.activeInteractions[i].featureid == featid) { return xxmap.activeInteractions[i].feature; }
1941
+ }
1942
+ }
1943
+ return null;
1944
+ }
1945
+
1946
+ // Remove Interaction
1947
+ function removeInteraction(ftid) {
1948
+ var index = -1;
1949
+ for (var i = 0; i < xxmap.activeInteractions.length; i++) {
1950
+ if (xxmap.activeInteractions[i].featureid === ftid) { index = i; break; }
1951
+ }
1952
+ if (index >= 0) { xxmap.activeInteractions.splice(index, 1); }
1953
+ }
1954
+
1955
+ // Check if pointer coordinates are equal to features and return node feature
1956
+ function getCorrespondingFeature(pointerFeat) {
1957
+ var pointerCoord = pointerFeat.getGeometry().getCoordinates();
1958
+ for (var i = 0; i < xxmap.activeInteractions.length ; i++) {
1959
+ var modifiedFeatures = xxmap.activeInteractions[i].feature;
1960
+ var fearCoord = modifiedFeatures.getGeometry().getCoordinates();
1961
+ if (fearCoord[0].toFixed(5) == pointerCoord[0].toFixed(5) && fearCoord[1].toFixed(5) == pointerCoord[1].toFixed(5) ) { return modifiedFeatures; }
1962
+ }
1963
+ return null;
1964
+ }
1965
+
1966
+ // Refresh the map and clear list
1967
+ function refreshMap(reset, rebound){
1968
+ if (reset) {
1969
+ xxmap.map.setTarget(null);
1970
+ xxmap.map = null;
1971
+ xxmap.markersSource = null;
1972
+ xxmap.mapView = null;
1973
+ xxmap.mapLayer = null;
1974
+ xxmap.activeInteractions = []; // Clear Active Interaction list
1975
+ }
1976
+ //clearMeshOptions();
1977
+ //onSelectMeshChange();
1978
+ var box = updateMapMarkers();
1979
+ if ((box != null) && (rebound || (box[4] == 1))) {
1980
+ var clat = (box[0] + box[2]) / 2;
1981
+ var clon = (box[1] + box[3]) / 2;
1982
+ var cscale = Math.max(Math.abs(box[0] - box[2]), Math.abs(box[1] - box[3]));
1983
+ var view = xxmap.map.getView();
1984
+ view.setCenter(ol.proj.transform([clon, clat], 'EPSG:4326', 'EPSG:3857'));
1985
+ var i = 360, j = -2;
1986
+ while (i > cscale) { j++; i = i / 2; }
1987
+ view.setZoom(j);
1988
+ }
1989
+ }
1990
+
1991
+ // Called When Place a node option is clicked from context menu
1992
+ function placeNode(coords) {
1993
+ if (xxdialogMode) return;
1994
+ clearSelectedNode();
1995
+ var x = '<div style="float: left; margin-bottom: 6px"><label for="selectnode-search">Search</label>  <input type="text" placeholder="Device Name" id="selectnode-search" onchange="onPNSearchInputChange()" onkeyup="onPNSearchInputChange()" autocomplete="off" style="width: 120px"></div>';
1996
+ var selectMeshElement = Q("select-mesh");
1997
+ var selectedIndex = selectMeshElement.selectedIndex;
1998
+ var selectedMeshValue = 'all';
1999
+ if (selectedIndex != 0) {
2000
+ x += '<div class="selectedmesh"> Mesh: ' + selectMeshElement[selectedIndex].text + '</div>';
2001
+ selectedMeshValue=selectMeshElement[selectedIndex].value;
2002
+ }
2003
+ x += '<div id="placenode" style="max-height:258px;overflow-y:auto;width:100%;">';
2004
+ var count = 0;
2005
+ var table = updatePlaceNodeTable('', selectedIndex, selectedMeshValue);
2006
+ if (table != 0) { x += table; count = 1; x += '</div>'; } else { count = 0;}
2007
+ if (count == 0) {
2008
+ if (selectedIndex != 0) {
2009
+ var noMeshNodes='<div class="flexboxdiv"><div style="margin-right:10px"><i class="fa fa-info-circle fa-3x" style="color:green" aria-hidden="true"></i></div><div style="max-width: 300px">No nodes found in Mesh '+ selectMeshElement[selectedIndex].text + '. Go to mesh <a onclick="closePNDialog(\''+selectMeshElement[selectedIndex].value+'\')" style="cursor:pointer">'+ selectMeshElement[selectedIndex].text +'</a> in My Account page to add a node.</div></div>';
2010
+ setDialogMode(2, "Select a Node to place marker", 1, null, noMeshNodes, null);
2011
+ } else {
2012
+ var noNodesFound = '';
2013
+ if (!meshExists()) {
2014
+ noNodesFound = '<div class="flexboxdiv"><div style="margin-right:10px"><i class="fa fa-info-circle fa-3x" style="color:green" aria-hidden="true"></i></div><div style="max-width: 300px"> No nodes found. To create a mesh network and add devices, go to <a onclick="closePNDialog(2)" style="cursor:pointer">My Account</a> page.</div></div>';
2015
+ } else {
2016
+ noNodesFound = '<div class="flexboxdiv"><div style="margin-right:10px"><i class="fa fa-info-circle fa-3x" style="color:green" aria-hidden="true"></i></div><div style="max-width: 300px"> No nodes found. To add devices, go to <a onclick="closePNDialog(1)" style="cursor:pointer">My Devices</a> page.</div></div>';
2017
+ }
2018
+ setDialogMode(2, "Select a Node to place marker", 1, null, noNodesFound, null);
2019
+ }
2020
+ } else {
2021
+ setDialogMode(2, "Select a Node to place marker", 18, placeNodeEx, x, coords);
2022
+ }
2023
+ }
2024
+
2025
+ function placeNodeEx(button, coords){
2026
+ for (var i in xxmap.selectedNodes) {
2027
+ var node = getNodeFromId(i);
2028
+ if (node) {
2029
+ var feature = markersSource.getFeatureById(i);
2030
+ var v = ol.proj.transform(coords, 'EPSG:3857', 'EPSG:4326');
2031
+ if (button == 2) {
2032
+ if (feature) {
2033
+ feature.getGeometry().setCoordinates(coords);
2034
+ var activeInteraction = getActiveInteractions(feature);
2035
+ if (activeInteraction) {
2036
+ saveMarkerloc(feature);
2037
+ } else { // If this feature is not modified, then send updated coords to server.
2038
+ meshserver.Send({ action: 'changelocation', nodeid: node._id, value: v }); // Send them to server to save changes
2039
+ }
2040
+ } else {
2041
+ meshserver.Send({ action: 'changelocation', nodeid: node._id, value: v }); // This Node is not yet added to maps.
2042
+ }
2043
+ }
2044
+ else if (button == 1) {
2045
+ if (feature) {
2046
+ feature.getGeometry().setCoordinates(coords);
2047
+ modifyMarkerloc(feature);
2048
+ } else {
2049
+ if (!node.iploc) {
2050
+ addFeature(node, v[0], v[1]);
2051
+ var newFeature = markersSource.getFeatureById(node._id);
2052
+ modifyMarkerloc(newFeature);
2053
+ }
2054
+ }
2055
+ }
2056
+ }
2057
+ }
2058
+ }
2059
+
2060
+ // Close place node dialog
2061
+ function closePNDialog(id){
2062
+ dialogclose(1);
2063
+ switch (id) {
2064
+ case 1:
2065
+ Q("showselect")[0].selected = true;
2066
+ onShowSelectChange();
2067
+ go(1);
2068
+ break;
2069
+ case 2:
2070
+ go(2);
2071
+ break;
2072
+ default:
2073
+ gotoMesh(id);
2074
+ }
2075
+ }
2076
+
2077
+ // On input search change
2078
+ function onPNSearchInputChange() {
2079
+ QH('placenode', '');
2080
+ var inputSearchData = Q('selectnode-search').value.toLowerCase();
2081
+ var selectMeshElement=Q("select-mesh");
2082
+ var selectedIndex = selectMeshElement.selectedIndex;
2083
+ var selectedMeshValue=selectMeshElement[selectedIndex].value;
2084
+ updatePlaceNodeTable(inputSearchData, selectedIndex, selectedMeshValue, 1);
2085
+ }
2086
+
2087
+ function updatePlaceNodeTable(inputSearch, selectedIndex, selectedMeshValue, innerHtmlflag) {
2088
+ var x = '<table id="placenode-table" class="selectnode-table"><tbody>';
2089
+ var count = 0;
2090
+ for (var i in nodes) {
2091
+ if (nodes[i].mtype == 2) {
2092
+ if ((nodes[i].meshid == selectedMeshValue || selectedIndex == 0) && ((nodes[i].name.toLowerCase().indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].hostl != undefined && nodes[i].hostl.toLowerCase().indexOf(inputSearch) >= 0))) {
2093
+ count++;
2094
+ x+='<tr id="'+ nodes[i]._id +'-rowid" onclick=selectNodetoplace(\''+ nodes[i]._id +'\') onmouseover="changeRadioImg(\''+ nodes[i]._id +'!#!mouseover'+ '\')" onmouseout="changeRadioImg(\''+ nodes[i]._id +'!#!mouseout'+ '\')">';
2095
+ x+='<td style="width: 30px; border-radius: 4px; text-align: center;"><i class="fa fa-square-o fa-lg selectnode-checkbox " id="'+ nodes[i]._id +'-img' +'" style="border-color: #7CFC00;" aria-hidden="true"></i></td>';
2096
+ x+='<td class="selectnode-td">'+ nodes[i].name + '</td>';
2097
+ x+='</tr>';
2098
+ }
2099
+ }
2100
+ }
2101
+ x+='</tbody></table>';
2102
+ if (innerHtmlflag) {
2103
+ if (count == 0) { QH('placenode', '<div class="flexboxdiv"><div style="margin: 0px 10px 0px 4px"><i class="fa fa-exclamation-circle fa-2x" style="color:red" aria-hidden="true"></i></div><div style="max-width: 300px"><b>No Nodes found with this Search Criteria.</b></div></div>'); } else { QH('placenode', x);}
2104
+ }else {
2105
+ if (count == 0) { return 0; }
2106
+ else { return x; }
2107
+ }
2108
+ }
2109
+
2110
+ function selectNodetoplace(id){
2111
+ var imgeElement = Q(id + '-img');
2112
+ if (xxmap.selectedNodes[id]) {
2113
+ imgeElement.classList.remove('fa-check-square-o');
2114
+ imgeElement.classList.add('fa-square');
2115
+ delete xxmap.selectedNodes[id];
2116
+ CheckedNodesforPN();
2117
+ } else {
2118
+ imgeElement.classList.remove('fa-square');
2119
+ imgeElement.classList.add('fa-check-square-o');
2120
+ xxmap.selectedNodes[id] = { color: Q(id+'-rowid').style.backgroundColor};
2121
+ QE('idx_dlgPlaceandSave', true);
2122
+ QE('idx_dlgPlaceButton', true);
2123
+ }
2124
+ }
2125
+
2126
+ function CheckedNodesforPN() {
2127
+ var c = 0;
2128
+ for (var i in xxmap.selectedNodes) { if (xxmap.selectedNodes[i]) { c++; } }
2129
+ if (c == 0) {
2130
+ QE('idx_dlgPlaceandSave', false);
2131
+ QE('idx_dlgPlaceButton', false);
2132
+ }
2133
+ }
2134
+
2135
+ // Set selected node to null
2136
+ function clearSelectedNode() {
2137
+ xxmap.selectedNodes = {};
2138
+ QE('idx_dlgPlaceandSave', false);
2139
+ QE('idx_dlgPlaceButton', false);
2140
+ }
2141
+
2142
+ // Change radio button on mouseover and out for 'select a node to place dialog'
2143
+ function changeRadioImg(name) {
2144
+ var index = name.indexOf("mouseout");
2145
+ var element = name.split('!#!');
2146
+ if (!xxmap.selectedNodes[element[0]]) {
2147
+ var imgeElement= Q(element[0]+'-img');
2148
+ if (index > -1) {
2149
+ imgeElement.classList.remove('fa-square');
2150
+ imgeElement.classList.add('fa-square-o');
2151
+ } else {
2152
+ imgeElement.classList.remove('fa-square-o');
2153
+ imgeElement.classList.add('fa-square');
2154
+ }
2155
+ }
2156
+ }
2157
+
2158
+ // Add option for available meshes in mesh Dropdown
2159
+ function addMeshOptions(addMeshid, meshName) {
2160
+ /*
2161
+ var meshOptions = Q('select-mesh');
2162
+ if (addMeshid && meshName) {
2163
+ var option = document.createElement('option');
2164
+ option.value =addMeshid;
2165
+ option.text = meshName;
2166
+ meshOptions.add(option); // Add specific option
2167
+ }
2168
+ else {
2169
+ for (var i in meshes) { // Add all options
2170
+ var option = document.createElement('option');
2171
+ option.value = i;
2172
+ option.text = meshes[i].name;
2173
+ meshOptions.add(option);
2174
+ }
2175
+ }
2176
+ */
2177
+ }
2178
+
2179
+ // Remove/Modify options in Mesh dropdown (if modMeshname is defined then Modify else Remove)
2180
+ function meshOptionRmvMod(delMeshid, modMeshname){
2181
+ /*
2182
+ var meshOptions = Q('select-mesh');
2183
+ if (delMeshid) {
2184
+ var index=-1;
2185
+ for (var i = 1; i < meshOptions.options.length; i++) {
2186
+ if (meshOptions[i].value === delMeshid) { index=i; }
2187
+ }
2188
+ if (index > 0) {
2189
+ if (modMeshname) {
2190
+ meshOptions[index].innerHTML=modMeshname; // If Mesh name is Modified
2191
+ }
2192
+ else { meshOptions.remove(index); }
2193
+ }
2194
+ }
2195
+ */
2196
+ }
2197
+
2198
+ //Check if there is any mesh created
2199
+ function meshExists() {
2200
+ for (var i in meshes) { if (meshes[i]) { return true; } }
2201
+ return false;
2202
+ }
2203
+
2204
+ // Reset Mesh dropdown option to 'All' when a current view mesh is deleted.
2205
+ function setMeshView(emeshid) {
2206
+ var selectMeshElement=Q("select-mesh");
2207
+ var selectedIndex = selectMeshElement.selectedIndex;
2208
+ if (selectMeshElement[selectedIndex].value == emeshid) { selectMeshElement[0].selected = true; onSelectMeshChange(); }
2209
+ }
2210
+
2211
+ // Clear all mesh options except 'All'
2212
+ function clearMeshOptions() {
2213
+ /*
2214
+ var meshOptions=Q('select-mesh');
2215
+ for(var i = meshOptions.options.length - 1 ; i > 0 ; i--) { meshOptions.remove(i); }
2216
+ */
2217
+ }
2218
+
2219
+ // Make a http get call- Replace this with AJAX get if jquery is used
2220
+ function getSearchLocation() {
2221
+ try {
2222
+ var searchdata = Q('mapSearchLocation').value.trim();
2223
+ if (searchdata.length > 0) {
2224
+ var xmlhttp = new XMLHttpRequest(); // Compatible with Chrome, Opera, Safari, IE7+, Firefox.
2225
+ xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { formatSearchData(xmlhttp.responseText); } }
2226
+ xmlhttp.open("GET", 'https://nominatim.openstreetmap.org/search?q=' + searchdata + '&format=json', true); // Get request
2227
+ xmlhttp.send();
2228
+ }
2229
+ } catch (e) {}
2230
+ }
2231
+
2232
+ // Format data recieved from nominatim API and display it on content window
2233
+ function formatSearchData(data) {
2234
+ try {
2235
+ QH('xmapSearchResults','');
2236
+ var dataInfo = JSON.parse(data), count = 0, x = '<div style="overflow-y:auto;width:100%;max-height:240px">';
2237
+ for (var i = 0; i < dataInfo.length; i++) {
2238
+ if (dataInfo[i].display_name && dataInfo[i].boundingbox[0] && dataInfo[i].boundingbox[1] && dataInfo[i].boundingbox[2] && dataInfo[i].boundingbox[3]) {
2239
+ count++;
2240
+ var color = (i % 2 == 0)?'F5F5F5':'EBEBEB';
2241
+ x += '<div style=cursor:pointer;padding:5px;background-color:#' + color + ' onclick=mapGotoSelectedLocation(this)><div>' + dataInfo[i].display_name + '</div><div style=display:none>' + dataInfo[i].boundingbox[0] + '!#!' + dataInfo[i].boundingbox[1] + '!#!' + dataInfo[i].boundingbox[2] + '!#!' + dataInfo[i].boundingbox[3] + '</div></div>';
2242
+ }
2243
+ }
2244
+ x += '</div>';
2245
+ if (count == 1) {
2246
+ // If only one result is returned then zoom to that location
2247
+ var extent = [ parseFloat(dataInfo[0].boundingbox[2]), parseFloat(dataInfo[0].boundingbox[0]), parseFloat(dataInfo[0].boundingbox[3]), parseFloat(dataInfo[0].boundingbox[1]) ];
2248
+ zoomToExtent(extent);
2249
+ } else {
2250
+ if (count == 0) { x = '<div style=width:200px>No location found.<div>'; }
2251
+ QV('xmapSearchResultsDlg', true);
2252
+ }
2253
+ QH('xmapSearchResults', x);
2254
+ }
2255
+ catch (e) {}
2256
+ }
2257
+
2258
+ // Zoom into the bounding box
2259
+ function mapGotoSelectedLocation(obj) {
2260
+ var objchildren = obj.children;
2261
+ var boundingBox = objchildren[1].innerHTML.split('!#!');
2262
+ var extent = [parseFloat(boundingBox[2]), parseFloat(boundingBox[0]), parseFloat(boundingBox[3]), parseFloat(boundingBox[1])];
2263
+ //Q('search-location').value = objchildren[0].innerHTML;
2264
+ zoomToExtent(extent);
2265
+ mapCloseSearchWindow();
2266
+ }
2267
+
2268
+ // Close the search window
2269
+ function mapCloseSearchWindow() {
2270
+ QH('xmapSearchResults', '');
2271
+ QV('xmapSearchResultsDlg', false);
2272
+ }
2273
+
2274
+ // Zoom to specific cordinates
2275
+ function zoomToLocation(coordinates, zoomVal) {
2276
+ var view = xxmap.map.getView();
2277
+ view.setCenter(coordinates);
2278
+ view.setZoom(zoomVal);
2279
+ }
2280
+
2281
+ function zoomToFitExtent() {
2282
+ var features = xxmap.markersSource.getFeatures();
2283
+ if (features.length > 0) {
2284
+ var extent = xxmap.markersSource.getExtent();
2285
+ xxmap.map.getView().fit(extent, xxmap.map.getSize());
2286
+ }
2287
+ }
2288
+
2289
+ function zoomToExtent(extent){
2290
+ var boundingExtent = ol.proj.transformExtent(extent, ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857'));
2291
+ xxmap.map.getView().fit(boundingExtent, xxmap.map.getSize());
2292
+ }
2293
+
2294
+
2295
//
2296
// MY DEVICE
2297
//
@@ -1943,18 +2633,7 @@
2633
2634
// Setup the device mark layer
2635
var deviceMark = new ol.Feature({ geometry: new ol.geom.Point(ol.proj.fromLonLat([lng, lat])) });
1946
- //deviceMark.setStyle(new ol.style.Style({ image: new ol.style.Icon(({ color: [113, 140, 0], src: 'images/dot.png' })) }));
1947
- deviceMark.setStyle(new ol.style.Style({
1948
-
1949
- text: new ol.style.Text({
1950
- //font: '12px helvetica,sans-serif',
1951
- text: currentNode.name,
1952
- textAlign: 'right',
1953
- offsetX: -10,
1954
- fill: new ol.style.Fill({ color: '#000' }),
1955
- stroke: new ol.style.Stroke({ color: '#fff', width: 2 })
1956
- }),
1957
- image: new ol.style.Icon(({ color: [113, 140, 0], src: 'images/dot.png' })) }));
2636
+ deviceMark.setStyle(markerStyle(currentNode));
2637
var vectorSource = new ol.source.Vector({ features: [deviceMark] });
2638
var vectorLayer = new ol.layer.Vector({ source: vectorSource });
2639
@@ -2135,7 +2814,6 @@
2814
}
2815
2816
function applyDesktopSettings() {
2138
- console.log(desktopsettings);
2817
d7desktopmode.value = desktopsettings.encoding;
2818
d7showfocus.checked = desktopsettings.showfocus;
2819
d7showcursor.checked = desktopsettings.showmouse;