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

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

Removed debugging and tidied up the format.

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