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

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

For the description input field, we're no longer using the oninput event handler, but have switched to the onchange handler, because with autocomplete a newly entered or pasted value wasn't always getting stored properly when using oninput.

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