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

Last change on this file since 33149 was 33149, checked in by wy59, 5 years ago

Bugfix to dealing with any undefined label values for map editor shapes.

File size: 30.0 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 this.map = new google.maps.Map(document.getElementById("map-" + this.id), {
316 center: {
317 lat: -37.7891,
318 lng: 175.3180
319 },
320 zoom: 14,
321 mapId: this.id,
322 });
323
324 // WORK-IN-PROGRESS FEATURE: label on Map (to be changed to a label associated with each shape later)
325 // let's associate a label with the map (for now, later associate labels for each shape)
326 //this.labelOverlay = new LabelOverlay(this.map);
327
328 this.mapsArray.push(this.map);
329 // Add a style-selector control to the map.
330 var styleControl = document.getElementById('style-selector-control' + "-" + this.id);
331 this.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(styleControl);
332
333 /*
334 // Set the map's style to the initial value of the selector.
335 var styleSelector = document.getElementById('style-selector' + "-" + this.id);
336 //console.log(styleSelector);
337 //map = google.maps.Map(document.getElementById('map' +"-"+ this.id));
338 this.map.setOptions({
339 styles: styles[styleSelector.value]
340
341 });
342
343 // Apply new JSON when the user selects a different style.
344 styleSelector.addEventListener('change', function () {
345 that.map.setOptions({
346 styles: styles[styleSelector.value]
347 });
348 });
349 */
350
351 this.drawingManager = new google.maps.drawing.DrawingManager({
352 //drawingMode: google.maps.drawing.OverlayType.RECTANGLE,
353 drawingControl: true,
354 drawingControlOptions: {
355 position: google.maps.ControlPosition.TOP_CENTER,
356 drawingModes: ['marker', 'circle', 'polygon', 'polyline', 'rectangle']
357 },
358 markerOptions: {
359 draggable: true
360 },
361 circleOptions: this.shapeOptions,
362 polylineOptions: this.shapeOptions,
363 polygonOptions: this.shapeOptions,
364 rectangleOptions: this.shapeOptions,
365 });
366
367 this.drawingManager.setMap(this.map);
368
369 google.maps.event.addListener(this.drawingManager, "drawingmode_changed", function () {
370 if (that.shiftKeyPressed != true && that.drawingManager.drawingMode !== null) {
371 that.deselectAll();
372 }
373 that.settingThePath();
374
375 })
376
377 // store reference to added overlay
378 google.maps.event.addListener(this.drawingManager, 'overlaycomplete', function (e) {
379 that.allowDeselect = true;
380 that.mapEditorHistory.historyOverlayPush();
381 that.overlays.push(e.overlay); // store reference to added overlay
382 var newShape = e.overlay;
383 newShape.type = e.type;
384 that.mapEditorHistory.presentOverlayPush();
385
386 if (e.type !== google.maps.drawing.OverlayType.MARKER) {
387 that.addShapeListeners(newShape, e);
388 that.setSelection(newShape, e);
389 } else {
390 that.addMarkerListeners(newShape, e);
391 that.setSelection(newShape, e);
392 }
393 });
394
395 //Clears selection if clicked on the map when shift is not presseed
396 google.maps.event.addListener(this.map, 'click', function (e) {
397 var c = document.body.childNodes;
398 if (e.target && e.target.matches("a.classA")) {
399 console.log("Anchor element clicked!");
400 }
401 if (that.shiftKeyPressed == false) {
402 that.clearSelection();
403 that.selectedShape = null;
404 }
405 });
406
407 google.maps.event.addListener(this.map, 'mousedown', function (e) {
408 that.dontResize = true;
409 });
410
411 google.maps.event.addListener(this.map, 'mouseup', function (e) {
412 that.dontResize = false;
413 });
414
415 //Keyboard shortcuts
416 var mapAndControls = document.getElementById("map-and-controls-" + this.id);
417 var thicknessField = document.getElementById ("thicknessRangeVal-" + this.id);
418 var opacityField = document.getElementById ("opacityRangeVal-" + this.id);
419 var openMapFunction = function() {
420 //Sets shift as unpressed
421 mapAndControls.addEventListener('keyup', function (event) {
422 if (event.keyCode == '16') {
423 that.shiftKeyPressed = false;
424 }
425 });
426
427 mapAndControls.addEventListener('keydown', function (event) {
428
429 // https://stackoverflow.com/questions/2220196/how-to-decode-character-pressed-from-jquerys-keydowns-event-handler
430 var keyCode = String.fromCharCode(event.which);
431 //console.log("Key pressed: " + keyCode);
432
433 //disable keyboard shortcut within the number input field
434 var activeElement = $(document.activeElement);
435 if(activeElement.attr('type') == 'number' || activeElement.attr('type') == 'text'){
436 //console.log('number detected')
437 return;
438 }
439 //Sets shift as pressed
440 if (event.keyCode == '16') {
441 that.shiftKeyPressed = true;
442 }
443 else if (keyCode == 'Y' && (event.ctrlKey || event.metaKey)) {
444 that.mapEditorHistory.redo();
445 }
446 else if (keyCode == 'Z' && (event.ctrlKey || event.metaKey) ) {
447 if (that.shiftKeyPressed == false) {
448 that.mapEditorHistory.undo();
449 }
450 }
451 else if (keyCode == 'A' && (event.ctrlKey || event.metaKey)) {
452 event.preventDefault();
453 that.drawingManager.setDrawingMode(null);
454 that.selectAll();
455 }
456 else if (keyCode == 'D' && (event.ctrlKey || event.metaKey)) {
457 event.preventDefault();
458 that.deselectAll();
459 }
460
461 else if (keyCode == '0' || keyCode == 'À' || (keyCode == 'G'&& (event.ctrlKey || event.metaKey))) {
462 event.preventDefault();
463 that.drawingManager.setDrawingMode(null);
464 } else if (keyCode == '1') {
465 that.drawingManager.setDrawingMode('marker');
466 } else if (keyCode == '2') {
467 that.drawingManager.setDrawingMode('circle');
468 } else if (keyCode == '3') {
469 that.drawingManager.setDrawingMode('polygon');
470 } else if (keyCode == '4') {
471 that.drawingManager.setDrawingMode('polyline');
472 } else if (keyCode == '5') {
473 that.drawingManager.setDrawingMode('rectangle');
474 }
475
476 //else if (keyCode == 'S') {
477 // that.saveToArchives();
478 //}
479 else if (keyCode == 'Q') { // for debugging information, press Q (easy to hit key)
480 that.printHistory();
481 }
482 else if (keyCode == '.') {
483 that.deleteSelectedShapes();
484 }
485 // console.log(keyCode);
486 });
487 };
488
489 openMapFunction();
490
491 this.buildColorPalette();
492
493
494 var collection = gs.cgiParams.c;
495 var site_name = gs.xsltParams.site_name;
496 var nodeID = this.id; // documentID, hopefully contains section ID too
497 var metaname = gps_metadata_name;
498
499 // collection, site, documentID, metadataName, metadataPosition, responseFunction
500 gs.functions.getArchivesMetadata(collection, site_name, nodeID, metaname, 0, function(responseText){
501 // responseText is of type GSMetadata
502
503 // called when data has been retrieved from archives
504 var JSONString = responseText.getValue();
505 if(JSONString !== "")
506 {
507 that.LOAD(JSONString, nodeID);
508 that.savedOverlays = JSONString;
509 }
510 }
511 ); // responseFunctions are now in the setMeta calls
512}
513
514//Deletes a vertex if clicked on it
515MapEditor.prototype.vertexAndPolyDel = function (e, newShape) {
516 var vertex = e.vertex;
517 if (e.vertex !== undefined) {
518 if (newShape.type === google.maps.drawing.OverlayType.POLYGON) {
519 var path = newShape.getPaths().getAt(e.path);
520 path.removeAt(e.vertex);
521 if (path.length < 3) {
522 newShape.setMap(null);
523 }
524 }
525 if (newShape.type === google.maps.drawing.OverlayType.POLYLINE) {
526 var path = newShape.getPath();
527 path.removeAt(e.vertex);
528 if (path.length < 2) {
529 newShape.setMap(null);
530 }
531 }
532 }
533}
534
535MapEditor.prototype.addMarkerListeners = function (newShape, e) {
536 var that = this;
537 //Click event if a marker is created
538 google.maps.event.addListener(newShape, 'click', function (e) {
539 if(that.shiftKeyPressed){
540
541 } else {
542 that.mapEditorHistory.historyOverlayPush();
543 newShape.setMap(null);
544 that.mapEditorHistory.presentOverlayPush();
545 }
546
547 });
548
549 google.maps.event.addListener(newShape, 'dragstart', function (e) {
550 that.beingDragged = true;
551 that.mapEditorHistory.historyOverlayPush();
552
553 });
554
555 google.maps.event.addListener(newShape, 'dragend', function () {
556 that.beingDragged = false;
557 that.mapEditorHistory.presentOverlayPush();
558 that.allowDeselect = false;
559 });
560}
561
562MapEditor.prototype.addShapeListeners = function (newShape, e) {
563 var that = this;
564 // Add an event listener that selects the newly-drawn shape when the user
565 // mouses down on it.
566 google.maps.event.addListener(newShape, 'click', function (e) {
567 that.vertexAndPolyDel(e, newShape);
568 });
569
570 google.maps.event.addListener(newShape, 'dragstart', function (e) {
571 that.allowDeselect = false;
572 that.mapEditorHistory.historyOverlayPush();
573 });
574
575 google.maps.event.addListener(newShape, 'dragend', function () {
576 that.beingDragged = false;
577 that.mapEditorHistory.presentOverlayPush();
578 that.settingThePath();
579
580 that.allowDeselect = false;
581 that.setSelection(newShape, e);
582 });
583
584 //Store information after the event ends
585 google.maps.event.addListener(newShape, 'bounds_changed', function (e) {
586 if (that.beingDragged == false) {
587 that.mapEditorHistory.presentOverlayPush();
588 }
589 });
590
591 //Add an event listener to select a shape if the mouse hovers over it
592 google.maps.event.addListener(newShape, 'mousedown', function (e) {
593 if (e.target && e.target.matches("a.classA")) {
594 console.log("Anchor element clicked!");
595 }
596 if (e.vertex !== undefined || e.edge !== undefined) {
597 that.mapEditorHistory.historyOverlayPush()
598 }
599 if (that.drawingManager.drawingMode == null) {
600 that.setSelection(newShape, e);
601 }
602 });
603
604 google.maps.event.addListener(newShape, 'mouseup', function (e) {
605 if (e.vertex !== undefined || e.edge !== undefined) {
606 that.mapEditorHistory.presentOverlayPush()
607 } else {
608 //that.setSelection(newShape, e);
609 }
610
611 });
612}
613MapEditor.prototype.clearSelection = function () {
614 if (this.selectedShape) {
615 if (this.shiftKeyPressed == false) {
616 for (var i = 0; i < this.selectedShapes.length; i++) {
617 if(this.selectedShapes[i].type !== 'marker') {
618 this.selectedShapes[i].setEditable(false);
619 }
620 }
621 this.selectedShapes = [];
622 }
623 this.selectedShape = null;
624 }
625}
626
627//Set selection for the selected overlay
628MapEditor.prototype.setSelection = function (shape, e) {
629 //var that = this;
630 if (shape.type !== 'marker') {
631 if (this.shiftKeyPressed == false) {
632 if (e !== null) {
633 if (e.vertex == undefined) {
634 if (e.edge == undefined) {
635 this.clearSelection();
636 shape.setEditable(true);
637 }
638 }
639 }
640 }
641 if (this.selectedShapes.includes(shape)) {
642 if (e !== null) {
643 if (e.vertex == undefined) {
644 if (e.edge == undefined) {
645 this.allowDeselect = true;
646 this.removeFromSelectedShapes(shape);
647 }
648 }
649 }
650 } else {
651 this.allowDeselect = false;
652 shape.setEditable(true);
653 this.selectedShapes.push(shape);
654 }
655
656 //Send the values to be updated
657 var thi = shape.strokeWeight;
658 var opa = shape.fillOpacity;
659 var fCol = shape.fillColor;
660 var sCol = shape.strokeColor;
661 var description = shape.description;
662 this.updateMenuValues(thi, opa, fCol, sCol, description);
663
664 } else if (shape.type == 'marker') {
665 this.allowDeselect = false;
666 this.selectedShapes.push(shape);
667 }
668 this.selectedShape = shape;
669 this.settingThePath();
670}
671
672MapEditor.prototype.removeFromSelectedShapes = function (shape) {
673 if (this.selectedShapes.includes(shape)) {
674 if (this.allowDeselect) {
675 const index = this.selectedShapes.indexOf(shape);
676 this.selectedShapes.splice(index, 1);
677 shape.setEditable(false);
678 }
679 this.allowDeselect = true;
680 }
681}
682
683//Set selected label
684MapEditor.prototype.setSelectedDescription = function (label) {
685 if (this.selectedShapes.length > 0) {
686 for (var i = 0; i < this.selectedShapes.length; i++) {
687 this.selectedShapes[i].set('description', label); //SAME: this.selectedShapes[i].description = label;
688 }
689 }
690}
691
692//Set selected thickness
693MapEditor.prototype.setSelectedThickness = function (sWeight) {
694 if (this.selectedShapes.length > 0) {
695 for (var i = 0; i < this.selectedShapes.length; i++) {
696 this.selectedShapes[i].set('strokeWeight', sWeight);
697 }
698 }
699}
700
701//Set selected opacity
702MapEditor.prototype.setSelectedOpacity = function (fOpacity) {
703
704 if (this.selectedShapes.length > 0) {
705 for (var i = 0; i < this.selectedShapes.length; i++) {
706 this.selectedShapes[i].set('fillOpacity', fOpacity);
707 }
708 }
709}
710
711//set selected fill colour
712MapEditor.prototype.setSelectedShapeColor = function (color) {
713 if (this.selectedShapes.length > 0) {
714 for (var i = 0; i < this.selectedShapes.length; i++) {
715 this.selectedShapes[i].set('fillColor', color);
716 this.selectedShapes[i].set('strokeColor', color);
717 }
718 }
719}
720
721MapEditor.prototype.getElementByXpath = function (path) {
722 return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
723}
724
725MapEditor.prototype.updateMenuValues = function (thi, opa, fCol, sCol, description) {
726 //Update thickness slider and value on the settings menu
727 var thicknessSliderOutput = document.getElementById("thicknessRangeVal" + "-" + this.id);
728 // update the thickness innerHTML's value to always have 2 decimal places, https://www.w3schools.com/js/js_number_methods.asp
729 thi = parseFloat(thi); //Ensure the thi is a number
730 thicknessSliderOutput.value = thi.toFixed(2);
731 document.getElementById("thicknessRange" + "-" + this.id).value = Math.round((thi * 20) * 100) / 100;
732
733 //Update the opacity slider and value on the settings menu
734 var opacitySliderOutput = document.getElementById("opacityRangeVal" + "-" + this.id);
735 opacitySliderOutput.value = opa * 100;
736 document.getElementById("colourOpacity" + "-" + this.id).value = opa * 100;
737
738 // Show the description in the description field
739 var descriptionInput = document.getElementById("description" + "-" + this.id);
740 descriptionInput.value = description;
741
742 if (this.drawingManager.drawingMode == null) {
743 this.selectColor(fCol);
744 }
745}
746MapEditor.prototype.selectAll = function () {
747 this.shiftKeyPressed = true;
748 var e = new Object();
749 e.vertex = undefined;
750 this.selectedShapes = [];
751 for (var i = 0; i < this.overlays.length; i++) {
752 this.setSelection(this.overlays[i], e);
753 }
754 this.shiftKeyPressed = false;
755}
756MapEditor.prototype.deselectAll = function () {
757 for (var i = 0; i < this.selectedShapes.length; i++) {
758 if (this.selectedShapes[i].type !== google.maps.drawing.OverlayType.MARKER) {
759 this.selectedShapes[i].setEditable(false);
760 }
761
762 }
763 this.selectedShapes = [];
764}
765
766// event handler for s being pressed *when map editor has the focus*
767// For saving and rebuilding, see map_scripts
768MapEditor.prototype.saveToArchives = function () {
769 var that = this;
770 console.log("Save pressed");
771
772 var json_overlays = JSON.stringify(ShapesUtil.overlayToJSON(this.overlays));
773 that.savedOverlays = json_overlays; // save the old version to compare with future changes
774 var collection = gs.cgiParams.c;
775 var site_name = gs.xsltParams.site_name;
776 var nodeID = this.id; // documentID, hopefully contains section ID too
777 var metaname = "GPS.mapOverlay";
778
779 // collection, site, documentID, metadataName, metadataPosition, metadataValue, prevMetadataValue, metamode, responseFunction
780 gs.functions.setArchivesMetadata(collection, site_name, nodeID, metaname, 0, json_overlays, null, "override", function(){
781 console.log("SAVED");
782 }
783 );
784}
785
786// TODO: When finished testing, can remove this debug function that just prints to console
787MapEditor.prototype.printHistory = function () {
788 console.log("prev", this.mapEditorHistory.prevOverlays);
789 console.log("present ", this.mapEditorHistory.presentOverlays);
790 console.log("undone ", this.mapEditorHistory.undoneOverlays);
791 console.log("@@@@ allShapes: ", this.overlays);
792 console.log("@@@@ selectedShapes: ", this.selectedShapes);
793}
794
795// to be called after reading back in stored JSON from archives meta
796MapEditor.prototype.LOAD = function (json_str, nodeID) {
797 this.mapEditorHistory.historyOverlayPush();
798
799 // This seems to convert the map_store object into an array and forces array index access, instead of convenient property access using nodeID
800 //Object.values(gsmap_store)[0]; // Always gets top level section's map-editor, not what we want.
801
802 // Get the map editor for the nodeID, as we're asked to load that editor
803 var map_editor = gsmap_store["map-"+nodeID];
804
805 var new_overlays = ShapesUtil.JSONToOverlays(json_str);
806 for (var i=0; i<map_editor.overlays.length; i++) {
807 map_editor.overlays[i].setMap(null);
808 }
809
810 map_editor.overlays = new_overlays;
811
812 for (var i=0; i<map_editor.overlays.length; i++) {
813 var shape = map_editor.overlays[i];
814
815 // set up the autocomplete list using saved labels/descriptions
816 map_editor.addToAutocompleteLabelsList(shape.description); // now efficiently ensures uniqueness of values using (hash)map
817
818 // make the shapes selectable on load:
819 if (ShapesUtil.overlayItemIsShape(shape)) {
820 map_editor.addShapeListeners(shape, null); // don't have an overlay event!
821 } else {
822 map_editor.addMarkerListeners(shape, null); // don't have an overlay event!
823 }
824 shape.setMap(map_editor.map);
825 }
826
827 this.mapEditorHistory.presentOverlayPush();
828}
829
830MapEditor.prototype.deleteSelectedShapes = function () {
831 if(this.selectedShapes.length !== 0) {
832 this.mapEditorHistory.historyOverlayPush();
833 for (var i = 0; i < this.selectedShapes.length; i++) {
834 this.selectedShapes[i].setMap(null);
835
836 if (this.overlays.includes(this.selectedShapes[i])) {
837 const index = this.overlays.indexOf(this.selectedShapes[i]);
838 this.overlays.splice(index, 1);
839// this.selectedShapes[i].setEditable(false);
840 }
841 }
842 this.selectedShapes = [];
843 this.mapEditorHistory.presentOverlayPush();
844 }
845}
846
847MapEditor.prototype.draggableState = function () {
848
849 var draggableCB = document.querySelector("input[name=draggableCB]");
850 draggableCB.addEventListener('change', function () {
851 if (this.checked) {
852 this.overlays.draggable = false;
853 } else {
854 this.overlays.draggable = false;
855 }
856 });
857}
858
859MapEditor.prototype.deleteAllShapes = function () {
860 if(this.overlays.length !== 0) {
861 //console.log("deleteAllShape() this.id = " + this.id);
862 this.mapEditorHistory.historyOverlayPush();
863 for (var i = 0; i < this.overlays.length; i++) {
864 this.overlays[i].setMap(null);
865 }
866 this.overlays = [];
867 this.mapEditorHistory.presentOverlayPush();
868 }
869}
870
871// Global function that uses jquery to set the autocomplete list's data source to whatever's in our global autocomplete labels array
872$(function setupAutocompleteLabelsList() {
873 // Overrides the default autocomplete filter function to
874 // search only from the beginning of the string
875 //resource: https://miroslavpopovic.com/posts/2012/06/jqueryui-autocomplete-filter-words-starting-with-term
876 $.ui.autocomplete.filter = function (array, term) {
877 var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(term), "i");
878 return $.grep(array, function (value) {
879 return matcher.test(value.label || value.value || value);
880 });
881 };
882
883 $(".description").autocomplete({
884 source: global_autocompleteLabelsList
885 });
886});
Note: See TracBrowser for help on using the repository browser.