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

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

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

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