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

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

In previous commits we weren't using the capabilities of the (hash)map. Now we finally make proper use of it to efficiently ensure the uniqueness of labels we put into the autocomplete list.

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