source: main/trunk/greenstone3/web/interfaces/default/js/map-scripts-editor.js

Last change on this file was 33300, checked in by ak19, 5 years ago

Any map-editor opened now centres on the shape overlays instead of always centering on on Uni of Waikato. It will now only continue to do the latter if a map editor has no shapes/overlays

File size: 30.6 KB
Line 
1// Global autocomplete labels list: one list for all the map editors' shape labels in a page
2// We use the global labels hashmap to more efficiently ensure uniqueness of labels in our autocomplete list
3var global_autocompleteLabelsList = [];
4var global_labels_hashmap = {};
5
6function MapEditor(id) {
7 // TODO: investigate const, see https://www.w3schools.com/js/js_const.asp and check it will work on older browsers,
8 // https://stackoverflow.com/questions/4271566/how-do-i-know-which-version-of-javascript-im-using
9 this.MAX_THICKNESS = 5.0;
10 this.MIN_THICKNESS = 0.0;
11 this.MAX_OPACITY = 100.00;
12 this.MIN_OPACITY = 0.00;
13
14 // WORK-IN-PROGRESS FEATURE: label on Map (to be changed to a label associated with each shape later)
15 // Also uncomment import of label-overlay-class.js in document.xsl
16 //this.labelOverlay = null;
17
18
19 this.id = id;
20 this.shiftKeyPressed = false;
21 this.beingDragged = false;
22 this.allowDeselect = true;
23 this.colors = ['#1E90FF', '#FF1493', '#4B0082', '#32CD32', '#FF8C00', '#000000'];
24 this.selectedColor;
25 this.colorButtons = {};
26 this.thicknessValue = 1;
27 this.opacityValue = 40;
28 this.overlays = [];
29 this.selectedShapes = [];
30 this.listenersArray = [];
31 this.mapsArray = [];
32 this.drawingManager;
33 this.selectedShape;
34 this.savedOverlays = null;
35 this.map = null;
36 this.counter = 0;
37 this.branchNum = 1;
38 this.mouseState = "up";
39 this.thicknessRangeListener = this.thicknessValue; // ????
40 this.resizable = false;
41 this.dontResize = false;
42
43 this.shapeOptions = {
44 suppressUndo: true,
45 fillColor: '#CA4A2F',
46 strokeWeight: this.thicknessValue,
47 fillOpacity: this.opacityValue / 100,
48 editable: true,
49 geodesic: false,
50 draggable: true,
51 description: ""
52 };
53 this.mapEditorHistory = new MapEditorHistory(this);
54}
55
56//draggable checkbox control
57MapEditor.prototype.initMapEditorControls = function () {
58 var that = this;
59
60 var draggableCB = document.getElementById("draggableCB-"+ this.id);
61 draggableCB.addEventListener('change', function () {
62 if (this.checked) {
63 for (var i = 0; i < that.overlays.length; i++) {
64 that.overlays[i].draggable = false;
65 that.shapeOptions.draggable = false;
66 }
67 } else {
68 for (var i = 0; i < that.overlays.length; i++) {
69 that.overlays[i].draggable = true;
70 that.shapeOptions.draggable = true;
71 }
72 }
73 });
74
75
76 //Update thickness
77 var thicknessSliderOutput = document.getElementById("thicknessRangeVal" + "-" + this.id);
78 var thicknessSlider = document.getElementById("thicknessRange" + "-" + this.id);
79 var thicknessValue = ((thicknessSlider.value / 20) * 100) / 100;
80 thicknessSliderOutput.value = thicknessValue.toFixed(2);
81
82 thicknessSlider.oninput = function () {
83 that.shapeSpecsChangeMD();
84 var thicknessVal = ((this.value / 20) * 100) / 100;
85 thicknessSliderOutput.value = thicknessVal.toFixed(2);
86 that.thicknessValue = this.value / 20;
87 that.shapeOptions.strokeWeight = that.thicknessValue;
88 that.setSelectedThickness(that.thicknessValue);
89 }
90
91 thicknessSliderOutput.oninput = function () {
92 that.shapeSpecsChangeMD(); // TODO: DO WE NEED THIS LINE? (LINE COPIED & PASTED FROM ABOVE)
93 if(this.value > that.MAX_THICKNESS) this.value = that.MAX_THICKNESS;
94 if(this.value < that.MIN_THICKNESS) this.value = that.MIN_THICKNESS;
95 var thicknessVal = this.value * 20;
96 thicknessSlider.value = thicknessVal.toFixed(2);
97 that.thicknessValue = this.value;
98 that.shapeOptions.strokeWeight = that.thicknessValue;
99 that.setSelectedThickness(that.thicknessValue);
100 }
101 //Update opacity
102 // TODO: https://stackoverflow.com/questions/469357/html-text-input-allow-only-numeric-input?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
103 var opacitySlider = document.getElementById("colourOpacity" + "-" + this.id);
104 var opacitySliderOutput = document.getElementById("opacityRangeVal" + "-" + this.id);
105
106 opacitySlider.oninput = function () {
107 that.shapeSpecsChangeMD();
108 opacitySliderOutput.value = this.value;
109 that.opacityValue = this.value / 100;
110 that.shapeOptions.fillOpacity = that.opacityValue;
111 that.setSelectedOpacity(that.opacityValue);
112 }
113 opacitySliderOutput.oninput = function () {
114 that.shapeSpecsChangeOnInput();
115 if(this.value > that.MAX_OPACITY) this.value = that.MAX_OPACITY;
116 if(this.value < that.MIN_OPACITY) this.value = that.MIN_OPACITY;
117 opacitySlider.value = this.value;
118 that.opacityValue = this.value / 100;
119 that.shapeOptions.fillOpacity = that.opacityValue;
120 that.setSelectedOpacity(that.opacityValue);
121 }
122
123 var descriptionInput = document.getElementById("description" + "-" + this.id);
124 // don't use oninput, use onchange, because with autocomplete a newly entered or pasted value
125 // doesn't always get stored properly when using oninput.
126 descriptionInput.onchange = function () {
127 that.shapeSpecsChangeOnInput(); // takes care of history (undo/redo)
128 var description = this.value;
129 that.shapeOptions.description = description;
130 that.setSelectedDescription(that.shapeOptions.description);
131 }
132
133 // Also add a COMPLETED description (i.e. when description input box loses focus)
134 // to the autocomplete list of descriptions/labels
135 descriptionInput.onblur = function () {
136 var description = this.value;
137 that.addToAutocompleteLabelsList(description);
138 }
139
140 // TODO: Do we need these listeners, when we already have onInput methods above? Test.
141 document.getElementById("color-palette1" + "-" + this.id).addEventListener("mousedown", function() { that.shapeSpecsChangeMD() });
142 document.getElementById("thicknessRange" + "-" + this.id).addEventListener("mouseup", function () { that.shapeSpecsChangeMU() });
143 document.getElementById("colourOpacity" + "-" + this.id).addEventListener("mouseup", function () { that.shapeSpecsChangeMU() });
144 //document.getElementById("description" + "-" + this.id).addEventListener("keypress", function () { that.shapeSpecsChangeOnInput() });
145
146 document.onmousedown = function (ev) {
147 that.mouseState = "down";
148 // console.log('Down State you can now start dragging');
149 //do not write any code here in this function
150 }
151
152 document.onmouseup = function (ev) {
153 that.mouseState = "up";
154 // console.log('up state you cannot drag now because you are not holding your mouse')
155 //do not write any code here in this function
156 }
157
158
159 //prompts the user to save the changes before reloading/leaving the page
160 window.onbeforeunload = function (e) {
161 var currentOverlays = JSON.stringify(ShapesUtil.overlayToJSON(that.overlays));
162 var enableMessage = currentOverlays !== that.savedOverlays;
163 var message = "Changes are not saved. Are you sure you want to leave?";
164
165
166 // Comment out following section in entirety -- from "e = e || window.event" to end of "if(e)" -- if
167 // you don't want to see the popup about changes that haven't been saved yet
168 e = e || window.event;
169 // For IE and Firefox
170 if (e) {
171
172 if(enableMessage){
173 if(currentOverlays !== "[]") {
174 alert(message);
175 e.returnValue = message;
176
177 // For Safari
178 return message;
179 }
180 }
181 }
182 }
183}
184
185// Ensure only unique labels are added to our autocomplete list
186MapEditor.prototype.addToAutocompleteLabelsList = function (newLabel) {
187
188 if(typeof(newLabel) === 'undefined') { return; }
189
190 // We use a hashmap to more efficiently ensure uniqueness of labels in our array
191 // https://stackoverflow.com/questions/11040472/how-to-check-if-object-property-exists-with-a-variable-holding-the-property-name
192 if (newLabel !== "" && !global_labels_hashmap.hasOwnProperty(newLabel)) { // label is not empty and unique, so
193 // add to hashmap now
194 global_labels_hashmap[newLabel] = 1;
195 // add to autocomplete list and sort alphabetically
196 global_autocompleteLabelsList.push(newLabel);
197 global_autocompleteLabelsList.sort();
198 }
199}
200
201MapEditor.prototype.settingThePath = function () {
202 var that = this;
203 this.listenersArray = []
204 this.counter = 0;
205 this.branchNum = 1;
206
207 for (var i = 0; i < this.selectedShapes.length * 2; i++) {
208 for (var j = 1; j < 6; j++) {
209 var path = "//*[@id='map-" + this.id + "']/div/div/div[1]/div[3]/div/div[3]/div[" + this.branchNum + "]/div[" + j + "]/div";
210 this.listenersArray[this.counter] = this.getElementByXpath(path);
211 if (this.listenersArray[this.counter] !== (undefined || null)) {
212 this.listenersArray[this.counter].addEventListener("mousemove", function () {
213 that.resizable = true;
214 that.shapeResize();
215 });
216 this.listenersArray[this.counter].addEventListener("mouseout", function () {
217 if (this.mouseDown) {
218 that.resizable = true;
219 that.shapeResize();
220
221 }
222 });
223 }
224 this.counter++;
225 }
226 this.branchNum++;
227 }
228}
229
230MapEditor.prototype.shapeResize = function () {
231 if (this.mouseState == "down") {
232 if (this.selectedShapes.length > 0) {
233 if (this.resizable) {
234 if (this.dontResize == false) {
235 this.mapEditorHistory.historyOverlayPush();
236 }
237
238 }
239 }
240 }
241}
242
243MapEditor.prototype.shapeSpecsChangeMD = function () {
244 if (this.selectedShapes.length > 0) {
245 this.mapEditorHistory.historyOverlayPush();
246 }
247}
248
249MapEditor.prototype.shapeSpecsChangeMU = function () {
250 if (this.selectedShapes.length > 0) {
251 this.mapEditorHistory.presentOverlayPush();
252 }
253}
254
255MapEditor.prototype.shapeSpecsChangeOnInput = function () {
256 if (this.selectedShapes.length > 0) {
257 this.mapEditorHistory.presentOverlayPush();
258 }
259}
260
261MapEditor.prototype.makeColorButton = function (color) {
262 var that = this;
263
264 var button = document.createElement('span');
265 button.className = 'color-buttons1';
266 button.style.backgroundColor = color;
267 google.maps.event.addDomListener(button, 'click', function () {
268 that.selectColor(color);
269 that.setSelectedShapeColor(color);
270 that.shapeSpecsChangeMU();
271 });
272 return button;
273}
274
275MapEditor.prototype.buildColorPalette = function () {
276 var colorPalette = document.getElementById("color-palette1" + "-" + this.id);
277 for (var i = 0; i < this.colors.length; ++i) {
278 var currColor = this.colors[i];
279 var colorButton = this.makeColorButton(currColor);
280 colorPalette.appendChild(colorButton);
281 this.colorButtons[currColor] = colorButton;
282 }
283 this.selectColor(this.colors[0]);
284};
285
286MapEditor.prototype.selectColor = function (color) {
287 this.selectedColor = color;
288 for (var i = 0; i < this.colors.length; ++i) {
289 var currColor = this.colors[i];
290 this.colorButtons[currColor].style.border = currColor == color ? '2px solid #789' : '2px solid #fff';
291 }
292
293 // Retrieves the current options from the drawing manager and replaces the
294 // stroke or fill color as appropriate.
295 var polylineOptions = this.drawingManager.get('polylineOptions');
296 polylineOptions.strokeColor = color;
297 this.drawingManager.set('polylineOptions', polylineOptions);
298
299 var rectangleOptions = this.drawingManager.get('rectangleOptions');
300 rectangleOptions.fillColor = color;
301 this.drawingManager.set('rectangleOptions', rectangleOptions);
302
303 var circleOptions = this.drawingManager.get('circleOptions');
304 circleOptions.fillColor = color;
305 this.drawingManager.set('circleOptions', circleOptions);
306
307 var polygonOptions = this.drawingManager.get('polygonOptions');
308 polygonOptions.fillColor = color;
309 this.drawingManager.set('polygonOptions', polygonOptions);
310}
311
312MapEditor.prototype.initMapEditor = function () {
313 var that = this;
314
315 // By default, the map editor will be centred on Waikato Uni at a particular zoom
316 this.map = new google.maps.Map(document.getElementById("map-" + this.id), {
317 center: {
318 lat: -37.7891,
319 lng: 175.3180
320 },
321 zoom: 14,
322 mapId: this.id,
323 });
324
325 // WORK-IN-PROGRESS FEATURE: label on Map (to be changed to a label associated with each shape later)
326 // let's associate a label with the map (for now, later associate labels for each shape)
327 //this.labelOverlay = new LabelOverlay(this.map);
328
329 this.mapsArray.push(this.map);
330 // Add a style-selector control to the map.
331 var styleControl = document.getElementById('style-selector-control' + "-" + this.id);
332 this.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(styleControl);
333
334 /*
335 // Set the map's style to the initial value of the selector.
336 var styleSelector = document.getElementById('style-selector' + "-" + this.id);
337 //console.log(styleSelector);
338 //map = google.maps.Map(document.getElementById('map' +"-"+ this.id));
339 this.map.setOptions({
340 styles: styles[styleSelector.value]
341
342 });
343
344 // Apply new JSON when the user selects a different style.
345 styleSelector.addEventListener('change', function () {
346 that.map.setOptions({
347 styles: styles[styleSelector.value]
348 });
349 });
350 */
351
352 this.drawingManager = new google.maps.drawing.DrawingManager({
353 //drawingMode: google.maps.drawing.OverlayType.RECTANGLE,
354 drawingControl: true,
355 drawingControlOptions: {
356 position: google.maps.ControlPosition.TOP_CENTER,
357 drawingModes: ['marker', 'circle', 'polygon', 'polyline', 'rectangle']
358 },
359 markerOptions: {
360 draggable: true
361 },
362 circleOptions: this.shapeOptions,
363 polylineOptions: this.shapeOptions,
364 polygonOptions: this.shapeOptions,
365 rectangleOptions: this.shapeOptions,
366 });
367
368 this.drawingManager.setMap(this.map);
369
370 google.maps.event.addListener(this.drawingManager, "drawingmode_changed", function () {
371 if (that.shiftKeyPressed != true && that.drawingManager.drawingMode !== null) {
372 that.deselectAll();
373 }
374 that.settingThePath();
375
376 })
377
378 // store reference to added overlay
379 google.maps.event.addListener(this.drawingManager, 'overlaycomplete', function (e) {
380 that.allowDeselect = true;
381 that.mapEditorHistory.historyOverlayPush();
382 that.overlays.push(e.overlay); // store reference to added overlay
383 var newShape = e.overlay;
384 newShape.type = e.type;
385 that.mapEditorHistory.presentOverlayPush();
386
387 if (e.type !== google.maps.drawing.OverlayType.MARKER) {
388 that.addShapeListeners(newShape, e);
389 that.setSelection(newShape, e);
390 } else {
391 that.addMarkerListeners(newShape, e);
392 that.setSelection(newShape, e);
393 }
394 });
395
396 //Clears selection if clicked on the map when shift is not presseed
397 google.maps.event.addListener(this.map, 'click', function (e) {
398 var c = document.body.childNodes;
399 if (e.target && e.target.matches("a.classA")) {
400 console.log("Anchor element clicked!");
401 }
402 if (that.shiftKeyPressed == false) {
403 that.clearSelection();
404 that.selectedShape = null;
405 }
406 });
407
408 google.maps.event.addListener(this.map, 'mousedown', function (e) {
409 that.dontResize = true;
410 });
411
412 google.maps.event.addListener(this.map, 'mouseup', function (e) {
413 that.dontResize = false;
414 });
415
416 //Keyboard shortcuts
417 var mapAndControls = document.getElementById("map-and-controls-" + this.id);
418 var thicknessField = document.getElementById ("thicknessRangeVal-" + this.id);
419 var opacityField = document.getElementById ("opacityRangeVal-" + this.id);
420 var openMapFunction = function() {
421 //Sets shift as unpressed
422 mapAndControls.addEventListener('keyup', function (event) {
423 if (event.keyCode == '16') {
424 that.shiftKeyPressed = false;
425 }
426 });
427
428 mapAndControls.addEventListener('keydown', function (event) {
429
430 // https://stackoverflow.com/questions/2220196/how-to-decode-character-pressed-from-jquerys-keydowns-event-handler
431 var keyCode = String.fromCharCode(event.which);
432 //console.log("Key pressed: " + keyCode);
433
434 //disable keyboard shortcut within the number input field
435 var activeElement = $(document.activeElement);
436 if(activeElement.attr('type') == 'number' || activeElement.attr('type') == 'text'){
437 //console.log('number detected')
438 return;
439 }
440 //Sets shift as pressed
441 if (event.keyCode == '16') {
442 that.shiftKeyPressed = true;
443 }
444 else if (keyCode == 'Y' && (event.ctrlKey || event.metaKey)) {
445 that.mapEditorHistory.redo();
446 }
447 else if (keyCode == 'Z' && (event.ctrlKey || event.metaKey) ) {
448 if (that.shiftKeyPressed == false) {
449 that.mapEditorHistory.undo();
450 }
451 }
452 else if (keyCode == 'A' && (event.ctrlKey || event.metaKey)) {
453 event.preventDefault();
454 that.drawingManager.setDrawingMode(null);
455 that.selectAll();
456 }
457 else if (keyCode == 'D' && (event.ctrlKey || event.metaKey)) {
458 event.preventDefault();
459 that.deselectAll();
460 }
461
462 else if (keyCode == '0' || keyCode == 'À' || (keyCode == 'G'&& (event.ctrlKey || event.metaKey))) {
463 event.preventDefault();
464 that.drawingManager.setDrawingMode(null);
465 } else if (keyCode == '1') {
466 that.drawingManager.setDrawingMode('marker');
467 } else if (keyCode == '2') {
468 that.drawingManager.setDrawingMode('circle');
469 } else if (keyCode == '3') {
470 that.drawingManager.setDrawingMode('polygon');
471 } else if (keyCode == '4') {
472 that.drawingManager.setDrawingMode('polyline');
473 } else if (keyCode == '5') {
474 that.drawingManager.setDrawingMode('rectangle');
475 }
476
477 //else if (keyCode == 'S') {
478 // that.saveToArchives();
479 //}
480 else if (keyCode == 'Q') { // for debugging information, press Q (easy to hit key)
481 that.printHistory();
482 }
483 else if (keyCode == '.') {
484 that.deleteSelectedShapes();
485 }
486 // console.log(keyCode);
487 });
488 };
489
490 openMapFunction();
491
492 this.buildColorPalette();
493
494
495 var collection = gs.cgiParams.c;
496 var site_name = gs.xsltParams.site_name;
497 var nodeID = this.id; // documentID, hopefully contains section ID too
498 var metaname = gps_metadata_name;
499
500 // collection, site, documentID, metadataName, metadataPosition, responseFunction
501 gs.functions.getArchivesMetadata(collection, site_name, nodeID, metaname, 0, function(responseText){
502 // responseText is of type GSMetadata
503
504 // called when data has been retrieved from archives
505 var JSONString = responseText.getValue();
506 if(JSONString !== "")
507 {
508 that.LOAD(JSONString, nodeID);
509 that.savedOverlays = JSONString;
510
511 // centre on the map's overlays now they're loaded up
512 var map_editor = gsmap_store["map-"+nodeID];
513 var overlayBounds = ShapesUtil.overlayBounds(map_editor.overlays);
514 if(map_editor.overlays.length === 1 && map_editor.overlays[0].type === google.maps.drawing.OverlayType.MARKER) {
515 that.map.setCenter(overlayBounds.getCenter());
516 that.map.setZoom(18);
517 } else {
518 that.map.fitBounds(overlayBounds);
519 }
520 }
521 }
522 ); // responseFunctions are now in the setMeta calls
523}
524
525//Deletes a vertex if clicked on it
526MapEditor.prototype.vertexAndPolyDel = function (e, newShape) {
527 var vertex = e.vertex;
528 if (e.vertex !== undefined) {
529 if (newShape.type === google.maps.drawing.OverlayType.POLYGON) {
530 var path = newShape.getPaths().getAt(e.path);
531 path.removeAt(e.vertex);
532 if (path.length < 3) {
533 newShape.setMap(null);
534 }
535 }
536 if (newShape.type === google.maps.drawing.OverlayType.POLYLINE) {
537 var path = newShape.getPath();
538 path.removeAt(e.vertex);
539 if (path.length < 2) {
540 newShape.setMap(null);
541 }
542 }
543 }
544}
545
546MapEditor.prototype.addMarkerListeners = function (newShape, e) {
547 var that = this;
548 //Click event if a marker is created
549 google.maps.event.addListener(newShape, 'click', function (e) {
550 if(that.shiftKeyPressed){
551
552 } else {
553 that.mapEditorHistory.historyOverlayPush();
554 //newShape.setMap(null);
555 that.mapEditorHistory.presentOverlayPush();
556 }
557
558 });
559
560 google.maps.event.addListener(newShape, 'dragstart', function (e) {
561 that.beingDragged = true;
562 that.mapEditorHistory.historyOverlayPush();
563
564 });
565
566 google.maps.event.addListener(newShape, 'dragend', function () {
567 that.beingDragged = false;
568 that.mapEditorHistory.presentOverlayPush();
569 that.allowDeselect = false;
570 });
571}
572
573MapEditor.prototype.addShapeListeners = function (newShape, e) {
574 var that = this;
575 // Add an event listener that selects the newly-drawn shape when the user
576 // mouses down on it.
577 google.maps.event.addListener(newShape, 'click', function (e) {
578 that.vertexAndPolyDel(e, newShape);
579 });
580
581 google.maps.event.addListener(newShape, 'dragstart', function (e) {
582 that.allowDeselect = false;
583 that.mapEditorHistory.historyOverlayPush();
584 });
585
586 google.maps.event.addListener(newShape, 'dragend', function () {
587 that.beingDragged = false;
588 that.mapEditorHistory.presentOverlayPush();
589 that.settingThePath();
590
591 that.allowDeselect = false;
592 that.setSelection(newShape, e);
593 });
594
595 //Store information after the event ends
596 google.maps.event.addListener(newShape, 'bounds_changed', function (e) {
597 if (that.beingDragged == false) {
598 that.mapEditorHistory.presentOverlayPush();
599 }
600 });
601
602 //Add an event listener to select a shape if the mouse hovers over it
603 google.maps.event.addListener(newShape, 'mousedown', function (e) {
604 if (e.target && e.target.matches("a.classA")) {
605 console.log("Anchor element clicked!");
606 }
607 if (e.vertex !== undefined || e.edge !== undefined) {
608 that.mapEditorHistory.historyOverlayPush()
609 }
610 if (that.drawingManager.drawingMode == null) {
611 that.setSelection(newShape, e);
612 }
613 });
614
615 google.maps.event.addListener(newShape, 'mouseup', function (e) {
616 if (e.vertex !== undefined || e.edge !== undefined) {
617 that.mapEditorHistory.presentOverlayPush()
618 } else {
619 //that.setSelection(newShape, e);
620 }
621
622 });
623}
624MapEditor.prototype.clearSelection = function () {
625 if (this.selectedShape) {
626 if (this.shiftKeyPressed == false) {
627 for (var i = 0; i < this.selectedShapes.length; i++) {
628 if(this.selectedShapes[i].type !== 'marker') {
629 this.selectedShapes[i].setEditable(false);
630 }
631 }
632 this.selectedShapes = [];
633 }
634 this.selectedShape = null;
635 }
636}
637
638//Set selection for the selected overlay
639MapEditor.prototype.setSelection = function (shape, e) {
640 //var that = this;
641 if (shape.type !== 'marker') {
642 if (this.shiftKeyPressed == false) {
643 if (e !== null) {
644 if (e.vertex == undefined) {
645 if (e.edge == undefined) {
646 this.clearSelection();
647 shape.setEditable(true);
648 }
649 }
650 }
651 }
652 if (this.selectedShapes.includes(shape)) {
653 if (e !== null) {
654 if (e.vertex == undefined) {
655 if (e.edge == undefined) {
656 this.allowDeselect = true;
657 this.removeFromSelectedShapes(shape);
658 }
659 }
660 }
661 } else {
662 this.allowDeselect = false;
663 shape.setEditable(true);
664 this.selectedShapes.push(shape);
665 }
666
667 //Send the values to be updated
668 var thi = shape.strokeWeight;
669 var opa = shape.fillOpacity;
670 var fCol = shape.fillColor;
671 var sCol = shape.strokeColor;
672 var description = shape.description;
673 this.updateMenuValues(thi, opa, fCol, sCol, description);
674
675 } else if (shape.type == 'marker') {
676 this.allowDeselect = false;
677 this.selectedShapes.push(shape);
678 }
679 this.selectedShape = shape;
680 this.settingThePath();
681}
682
683MapEditor.prototype.removeFromSelectedShapes = function (shape) {
684 if (this.selectedShapes.includes(shape)) {
685 if (this.allowDeselect) {
686 const index = this.selectedShapes.indexOf(shape);
687 this.selectedShapes.splice(index, 1);
688 shape.setEditable(false);
689 }
690 this.allowDeselect = true;
691 }
692}
693
694//Set selected label
695MapEditor.prototype.setSelectedDescription = function (label) {
696 if (this.selectedShapes.length > 0) {
697 for (var i = 0; i < this.selectedShapes.length; i++) {
698 this.selectedShapes[i].set('description', label); //SAME: this.selectedShapes[i].description = label;
699 }
700 }
701}
702
703//Set selected thickness
704MapEditor.prototype.setSelectedThickness = function (sWeight) {
705 if (this.selectedShapes.length > 0) {
706 for (var i = 0; i < this.selectedShapes.length; i++) {
707 this.selectedShapes[i].set('strokeWeight', sWeight);
708 }
709 }
710}
711
712//Set selected opacity
713MapEditor.prototype.setSelectedOpacity = function (fOpacity) {
714
715 if (this.selectedShapes.length > 0) {
716 for (var i = 0; i < this.selectedShapes.length; i++) {
717 this.selectedShapes[i].set('fillOpacity', fOpacity);
718 }
719 }
720}
721
722//set selected fill colour
723MapEditor.prototype.setSelectedShapeColor = function (color) {
724 if (this.selectedShapes.length > 0) {
725 for (var i = 0; i < this.selectedShapes.length; i++) {
726 this.selectedShapes[i].set('fillColor', color);
727 this.selectedShapes[i].set('strokeColor', color);
728 }
729 }
730}
731
732MapEditor.prototype.getElementByXpath = function (path) {
733 return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
734}
735
736MapEditor.prototype.updateMenuValues = function (thi, opa, fCol, sCol, description) {
737 //Update thickness slider and value on the settings menu
738 var thicknessSliderOutput = document.getElementById("thicknessRangeVal" + "-" + this.id);
739 // update the thickness innerHTML's value to always have 2 decimal places, https://www.w3schools.com/js/js_number_methods.asp
740 thi = parseFloat(thi); //Ensure the thi is a number
741 thicknessSliderOutput.value = thi.toFixed(2);
742 document.getElementById("thicknessRange" + "-" + this.id).value = Math.round((thi * 20) * 100) / 100;
743
744 //Update the opacity slider and value on the settings menu
745 var opacitySliderOutput = document.getElementById("opacityRangeVal" + "-" + this.id);
746 opacitySliderOutput.value = opa * 100;
747 document.getElementById("colourOpacity" + "-" + this.id).value = opa * 100;
748
749 // Show the description in the description field
750 var descriptionInput = document.getElementById("description" + "-" + this.id);
751 descriptionInput.value = description;
752
753 if (this.drawingManager.drawingMode == null) {
754 this.selectColor(fCol);
755 }
756}
757MapEditor.prototype.selectAll = function () {
758 this.shiftKeyPressed = true;
759 var e = new Object();
760 e.vertex = undefined;
761 this.selectedShapes = [];
762 for (var i = 0; i < this.overlays.length; i++) {
763 this.setSelection(this.overlays[i], e);
764 }
765 this.shiftKeyPressed = false;
766}
767MapEditor.prototype.deselectAll = function () {
768 for (var i = 0; i < this.selectedShapes.length; i++) {
769 if (this.selectedShapes[i].type !== google.maps.drawing.OverlayType.MARKER) {
770 this.selectedShapes[i].setEditable(false);
771 }
772
773 }
774 this.selectedShapes = [];
775}
776
777// event handler for s being pressed *when map editor has the focus*
778// For saving and rebuilding, see map_scripts
779MapEditor.prototype.saveToArchives = function () {
780 var that = this;
781 console.log("Save pressed");
782
783 var json_overlays = JSON.stringify(ShapesUtil.overlayToJSON(this.overlays));
784 that.savedOverlays = json_overlays; // save the old version to compare with future changes
785 var collection = gs.cgiParams.c;
786 var site_name = gs.xsltParams.site_name;
787 var nodeID = this.id; // documentID, hopefully contains section ID too
788 var metaname = "GPS.mapOverlay";
789
790 // collection, site, documentID, metadataName, metadataPosition, metadataValue, prevMetadataValue, metamode, responseFunction
791 gs.functions.setArchivesMetadata(collection, site_name, nodeID, metaname, 0, json_overlays, null, "override", function(){
792 console.log("SAVED");
793 }
794 );
795}
796
797// TODO: When finished testing, can remove this debug function that just prints to console
798MapEditor.prototype.printHistory = function () {
799 console.log("prev", this.mapEditorHistory.prevOverlays);
800 console.log("present ", this.mapEditorHistory.presentOverlays);
801 console.log("undone ", this.mapEditorHistory.undoneOverlays);
802 console.log("@@@@ allShapes: ", this.overlays);
803 console.log("@@@@ selectedShapes: ", this.selectedShapes);
804}
805
806// to be called after reading back in stored JSON from archives meta
807MapEditor.prototype.LOAD = function (json_str, nodeID) {
808 this.mapEditorHistory.historyOverlayPush();
809
810 // This seems to convert the map_store object into an array and forces array index access, instead of convenient property access using nodeID
811 //Object.values(gsmap_store)[0]; // Always gets top level section's map-editor, not what we want.
812
813 // Get the map editor for the nodeID, as we're asked to load that editor
814 var map_editor = gsmap_store["map-"+nodeID];
815
816 var new_overlays = ShapesUtil.JSONToOverlays(json_str);
817 for (var i=0; i<map_editor.overlays.length; i++) {
818 map_editor.overlays[i].setMap(null);
819 }
820
821 map_editor.overlays = new_overlays;
822
823 for (var i=0; i<map_editor.overlays.length; i++) {
824 var shape = map_editor.overlays[i];
825
826 // set up the autocomplete list using saved labels/descriptions
827 map_editor.addToAutocompleteLabelsList(shape.description); // now efficiently ensures uniqueness of values using (hash)map
828
829 // make the shapes selectable on load:
830 if (ShapesUtil.overlayItemIsShape(shape)) {
831 map_editor.addShapeListeners(shape, null); // don't have an overlay event!
832 } else {
833 map_editor.addMarkerListeners(shape, null); // don't have an overlay event!
834 }
835 shape.setMap(map_editor.map);
836 }
837
838 this.mapEditorHistory.presentOverlayPush();
839}
840
841MapEditor.prototype.deleteSelectedShapes = function () {
842 if(this.selectedShapes.length !== 0) {
843 this.mapEditorHistory.historyOverlayPush();
844 for (var i = 0; i < this.selectedShapes.length; i++) {
845 this.selectedShapes[i].setMap(null);
846
847 if (this.overlays.includes(this.selectedShapes[i])) {
848 const index = this.overlays.indexOf(this.selectedShapes[i]);
849 this.overlays.splice(index, 1);
850// this.selectedShapes[i].setEditable(false);
851 }
852 }
853 this.selectedShapes = [];
854 this.mapEditorHistory.presentOverlayPush();
855 }
856}
857
858MapEditor.prototype.draggableState = function () {
859
860 var draggableCB = document.querySelector("input[name=draggableCB]");
861 draggableCB.addEventListener('change', function () {
862 if (this.checked) {
863 this.overlays.draggable = false;
864 } else {
865 this.overlays.draggable = false;
866 }
867 });
868}
869
870MapEditor.prototype.deleteAllShapes = function () {
871 if(this.overlays.length !== 0) {
872 //console.log("deleteAllShape() this.id = " + this.id);
873 this.mapEditorHistory.historyOverlayPush();
874 for (var i = 0; i < this.overlays.length; i++) {
875 this.overlays[i].setMap(null);
876 }
877 this.overlays = [];
878 this.mapEditorHistory.presentOverlayPush();
879 }
880}
881
882// Global function that uses jquery to set the autocomplete list's data source to whatever's in our global autocomplete labels array
883$(function setupAutocompleteLabelsList() {
884 // Overrides the default autocomplete filter function to
885 // search only from the beginning of the string
886 //resource: https://miroslavpopovic.com/posts/2012/06/jqueryui-autocomplete-filter-words-starting-with-term
887 $.ui.autocomplete.filter = function (array, term) {
888 var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(term), "i");
889 return $.grep(array, function (value) {
890 return matcher.test(value.label || value.value || value);
891 });
892 };
893
894 $(".description").autocomplete({
895 source: global_autocompleteLabelsList
896 });
897});
Note: See TracBrowser for help on using the repository browser.