define('jcr-map', ['Component', '../es6-promise-auto-min'], function () { var module = new Component('jcr-map'); var atracoes; var mapContainer; var mapLoading; var imageOverlay = '/images/mapa-bcw-original.jpg'; var iconeAtracao = '/images/new-map-marker.png'; module.elements = {}; module.install = function () { var self = this; mapContainer = self.querySelector('.mapa-atracao_container-mapa'); mapLoading = self.querySelector('.carregando-mapa'); if (!mapContainer) return; atracoes = JSON.parse(self.attributes['data-atracoes'].value); mapa(); } module.bootstrap(); return module; function mapa() { class Pointer { params; flip180 = false; scale = 1.0; width = 50; height = 50; actualPosition = { x: 0, y: 0 }; touch = false; imageElement = null; overlayElement = null; observerOverlay; onMove = false; fnCallbackToCalculePositionOnMap; debug = false; debugDiv = null; constructor(_param, _width, _height, _contentHTML, _flip180 = false, _callbackSetCenter, imageElement, overlay, fnCallbackToCalculePositionOnMap, debug = false) { this.container = document.createElement('div'); this.element = document.createElement("img"); this.contentHTML = document.createElement("div"); this.contentHTML.innerHTML = _contentHTML; this.element.src = iconeAtracao; this.width = _width; this.height = _height; this.id = _param.id; this.params = _param; this.imageElement = imageElement; this.overlayElement = overlay; this.fnCallbackToCalculePositionOnMap = fnCallbackToCalculePositionOnMap; this.debug = debug; if (debug) { this.debugDiv = imageElement.parentElement.querySelector('div#debug'); if (!this.debugDiv) { console.error('debug is enabled but does not contain the debug div') debug = false; } } var styles = { width: `${_width}px`, maxWidth: `${_width}px`, height: `${_height}px`, maxHeight: `${_height}px`, position: 'absolute', cursor: 'pointer', 'pointer-events': 'initial', // 'pointer-events': 'none', } var pointerStyle = { width: `${_width}px`, maxWidth: `${_width}px`, height: `${_height}px`, maxHeight: `${_height}px`, } var contentHTMLStyle = { display: 'flex', width: '200px', position: 'absolute', 'pointer-events': 'initial' } this.addStyle(this.container, styles); this.addStyle(this.element, pointerStyle); this.addStyle(this.contentHTML, contentHTMLStyle); this.flip180 = _flip180; this.addStyle(this.container, { transform: `rotate(${_flip180 ? '180deg' : '0deg'}) translate(-50%, 0%)`, 'transform-origin': 'left bottom' }); this.addStyle(this.contentHTML, { transform: `rotate(${_flip180 ? '180deg' : '0deg'}) translate(-50%, -${_height}px)`, 'transform-origin': 'left bottom' }); this.container.appendChild(this.element); this.container.setAttribute('data-id', this.id); this.element.setAttribute('name', this.params.nome); this.contentHTML.setAttribute('data-content', this.id); this.addAction(_callbackSetCenter, { long: parseFloat(_param.longitude) || 0, lat: parseFloat(_param.latitude) || 0 }); this.element.onload = this.onLoad(); } addStyle(element, style) { Object.entries(style).forEach(function ([key, value]) { element.style[key] = value; }) } getContainer() { return this.container; } getElement() { return this.element; } setPosition(percent, pixel) { var style = { left: percent.x, bottom: percent.y, } this.actualPosition = { percent, pixel }; this.addStyle(this.container, style); this.addStyle(this.contentHTML, style); } buttonClose() { var contentHTMLButtonClose = document.createElement('button'); contentHTMLButtonClose.innerText = 'x'; var contentHTMLButtonCloseStyle = { position: 'absolute', 'pointer-events': 'initial', right: '0px', top: '0px', width: '20px', height: '20px', background: '#fff', border: 'none', outline: 'none', 'border-radius': '5px', } this.addStyle(contentHTMLButtonClose, contentHTMLButtonCloseStyle); contentHTMLButtonClose.addEventListener('click', (function () { var id = this.id; var modal = document.querySelector(`[data-content="${id}"]`); if (modal) { modal.remove(); } }).bind(this)); return contentHTMLButtonClose; } toFixed(value) { return parseFloat(parseFloat(value).toFixed(2)) } addAction(callbackSetCenter, coordinates) { /** * If you have content to display, include on modal the button close. */ if (this.contentHTML.children.length > 0) { this.contentHTML.appendChild(this.buttonClose()); } /** * @description when clicking on this pointer, close the previous open modal, and open this one (if it has content), and center this pointer on the map */ this.container.addEventListener('click', (function (self, content, callbackSetCenter, coordinates) { var previousOpenned = self.overlayElement.querySelector('[data-content]'); if (previousOpenned) { previousOpenned.remove(); } if (content.children.length > 0) { self.overlayElement.appendChild(content); /** * FIX to prevent propagation in doubleclick in content */ content.addEventListener('dblclick', function (e) { e.stopPropagation(); }) } var preferPosition = window.innerWidth <= 768 ? 'bottom' : null; callbackSetCenter([coordinates.long, coordinates.lat], preferPosition); }).bind(this.container, this, this.contentHTML, callbackSetCenter, coordinates)); var getMinAndMax = (function (yClicked, xClicked) { var leftPercent = parseFloat((this.actualPosition.percent.y).replace('%', '')); var rigthPercent = parseFloat((this.actualPosition.percent.x).replace('%', '')); var yPercentToPixel = (leftPercent * this.imageElement.height) / 100; var xPercentToPixel = (rigthPercent * this.imageElement.width) / 100; var maxTop = !this.flip180 ? (this.imageElement.height - yPercentToPixel) : (this.imageElement.height - yPercentToPixel + this.height); var minTop = !this.flip180 ? (maxTop - this.height) : (this.imageElement.height - yPercentToPixel); var minLeft = xPercentToPixel - (this.width / 2); var maxLeft = minLeft + (this.width); if (yClicked >= minTop && yClicked <= maxTop && xClicked >= minLeft && xClicked <= maxLeft) { this.container.dispatchEvent(new Event('click', { bubble: true, cancelable: true })); } if (this.debug && this.id === 102) { //debug in relation of the Castelo das Nações this.debugDiv.children[2].innerHTML = ` Touch vs Location of Castelo das Nações ->
${JSON.stringify({ yClicked: this.toFixed(yClicked), xClicked: this.toFixed(xClicked) })}
${JSON.stringify({ minTop: this.toFixed(minTop), maxTop: this.toFixed(maxTop), minLeft: this.toFixed(minLeft), maxLeft: this.toFixed(maxLeft) })}
` } }).bind(this); var clickOnPointer = function (e) { var xClicked = e.offsetX; var yClicked = e.offsetY; getMinAndMax(yClicked, xClicked); } var getOffsetParent = function (element) { var sum = 0; var act = element; while (act.tagName !== 'BODY' && !!act) { sum += act.offsetTop; act = act.parentElement; } return sum; } var recoverOffsetValues = (function (e) { var x = e.targetTouches[0].target.x; //position of image element on screen var y = e.targetTouches[0].target.y; //position of image element on screen var clientY = e.targetTouches[0].clientY; //position clicked on screen var clientX = e.targetTouches[0].clientX; //position clicked on screen // var offsetScrollTop = getOffsetParent(e.targetTouches[0].target) - window.scrollY; var offsetScrollTop = window.scrollY; var intoImage = { x: clientX - x, y: clientY - y - offsetScrollTop } if (this.flip180) { intoImage = { x: this.imageElement.width - intoImage.x, y: this.imageElement.height - intoImage.y, } } if (this.debug && this.id === 102) { console.log(e); this.debugDiv.children[3].innerHTML = ` recoverOffsetValues (on screen)->
${JSON.stringify({ clientY: this.toFixed(intoImage.y), clientX: this.toFixed(intoImage.x) })}
recoverOffsetValues (offset)->
${offsetScrollTop}
` } getMinAndMax(intoImage.y, intoImage.x); }).bind(this); // /** // * @description click only mouse don't move (move is considered drag of map) - DESKTOP // */ // this.imageElement.addEventListener('pointerdown', (function (e) { // if (e.pointerType !== 'touch') { // this.onMove = false; // } // }).bind(this)); // this.imageElement.addEventListener('pointermove', (function (e) { // if (e.pointerType !== 'touch') { // this.onMove = true; // } // }).bind(this)); // this.imageElement.addEventListener('pointerleave', (function (e) { // if (e.pointerType !== 'touch') { // this.onMove = false; // } // }).bind(this)); // this.imageElement.addEventListener('pointerup', (function (e) { // if (e.pointerType !== 'touch') { // if (!this.onMove) { // clickOnPointer(e); // } // this.onMove = false; // } // }).bind(this)); // /** // * @description click only touch don't move (move is considered drag of map) - MOBILE // */ // this.imageElement.addEventListener('touchstart', (function (e) { // this.onMove = false; // this.touch = e; // }).bind(this)); // this.imageElement.addEventListener('touchmove', (function () { // this.onMove = true; // this.touch = null; // }).bind(this)); // this.imageElement.addEventListener('touchleave', (function () { // this.onMove = false; // this.touch = null; // }).bind(this)); // this.imageElement.addEventListener('touchend', (function (e) { // if (!this.onMove) recoverOffsetValues(this.touch); // this.onMove = false; // this.touch = null; // }).bind(this)); } onLoad() { var walk = this.fnCallbackToCalculePositionOnMap([this.params.longitude, this.params.latitude], 'all'); this.setPosition({ x: `${walk.percent.walkLong}%`, y: `${walk.percent.walkLat}%` }, { x: walk.pixel.walkLong, y: walk.pixel.walkLat }); this.overlayElement.appendChild(this.container); this.observable() } recalculePosition() { var walk = this.fnCallbackToCalculePositionOnMap([this.params.longitude, this.params.latitude], 'all'); this.setPosition({ x: `${walk.percent.walkLong}%`, y: `${walk.percent.walkLat}%` }, { x: walk.pixel.walkLong, y: walk.pixel.walkLat }); } observable() { // config of observer var config = { attributes: true, childList: false, characterData: true }; // instance of mutation this.observerOverlay = new MutationObserver((function (mutations) { mutations.forEach((function (mutation) { switch (mutation.type) { case 'attributes': switch (mutation.attributeName) { case 'data-bounds': this.recalculePosition(); break; default: break; } break; default: break; } }).bind(this)); }).bind(this)); // target to mutation this.observerOverlay.observe(this.overlayElement, config); } } class Img { onDrag = false; left = 0; top = 0; flip180 = false; observer; observerOverlay; parent = null; debug = false; LONG_POS = 0; LAT_POS = 1; rangeTotalLong; rangeTotalLat; constructor(_src, _bounds, props, parent) { this.parent = parent; this.element = document.createElement('img'); this.overlay = document.createElement('div'); this.transformDefault = ''; this.corners = { bottomLeft: _bounds.bottomLeft, topRight: _bounds.topRight, } this.rangeTotalLong = Math.abs(_bounds.topRight[this.LONG_POS] - _bounds.bottomLeft[this.LONG_POS]); this.rangeTotalLat = Math.abs(_bounds.topRight[this.LAT_POS] - _bounds.bottomLeft[this.LAT_POS]); if (props.flip180) { this.flip180 = true; this.transformDefault = 'rotate(180deg)'; } if (props.debug) { var debugStyle = { width: '100%', position: 'fixed', backgroundColor: '#c57505', color: '#fff', top: 0, left: 0, zIndex: 9999, padding: '8px', textAlign: 'left', wordBreak: 'break-all', height: '100px', overflow: 'scroll', } this.debug = props.debug; var debugElement = document.createElement('div'); debugElement.id = 'debug'; debugElement.innerHTML = `
` this.parent.appendChild(debugElement); this.debugDiv = debugElement; this.addStyle(debugElement, debugStyle); } var style = { transform: this.transformDefault, width: `${window.innerWidth}px`, minWidth: '100%', // maxWidth: '150%', position: 'absolute', left: `${this.left}px`, top: `${this.top}px`, // transition: '100ms', } this.addStyle(this.element, style); this.addStyle(this.overlay, style); this.addStyle(this.overlay, { pointerEvents: 'none', position: 'relative', }); this.element.src = _src; this.overlay.setAttribute('data-bounds', JSON.stringify(this.corners)); this.addActions(); this.observable(); } getElement() { return this.element; } getOverlay() { this.addStyleToOverlay({ height: `${this.element.height}px` }); return this.overlay; } setPosition(_left, _top, increment = true) { //px var minLeft = (function () { if (increment) return this.left + _left <= 0; return _left <= 0; }).bind(this); var maxLeft = (function () { var widthOfImage = this.element.width; var actualLeft = increment ? this.left : 0; var leftToIncrement = _left; var widthOfContainer = this.parent.clientWidth; //the sum must be greater than zero //the calculation shows how much space there is on the right side return widthOfImage + (actualLeft + leftToIncrement) - widthOfContainer >= 0; }).bind(this); var leftInRange = (function () { return minLeft() && maxLeft() }).bind(this); var minTop = (function () { if (increment) return this.top + _top <= 0; return _top <= 0; }).bind(this); var maxTop = (function () { var heightOfImage = this.element.height; var actualTop = increment ? this.top : 0; var topToIncrement = _top; var heightOfContainer = this.parent.clientHeight; //the sum must be greater than zero //the calculation shows how much space there is on the right side return heightOfImage + (actualTop + topToIncrement) - heightOfContainer >= 0; }).bind(this); var topInRange = (function () { return minTop() && maxTop() }).bind(this); if (increment) { if (leftInRange()) { this.left += _left; } if (topInRange()) { this.top += _top; } } else if (!increment) { if (minLeft()) { //min left if (maxLeft()) { //max rigth this.left = _left; } else { this.left = -(this.element.width - this.parent.clientWidth); //fix max right } } else { this.left = 0;//fix min left } if (minTop()) { //min left if (maxTop()) { //max top this.top = _top; } else { this.top = _top + (this.element.height + _top - this.parent.clientHeight); //fix max top } } else { this.top = 0;//fix min top } } this.element.style.left = `${this.left}px`; this.element.style.top = `${this.top}px`; } addStyle(element, style) { Object.entries(style).forEach(function ([key, value]) { element.style[key] = value; }) } addStyleToElement(style) { this.addStyle(this.element, style); } addStyleToOverlay(style) { this.addStyle(this.overlay, style); } getQuantityToWalkFromTheLimits([long, lat], type) { var walkLong = parseFloat(long) - Math.min(this.corners.topRight[this.LONG_POS], this.corners.bottomLeft[this.LONG_POS]); var walkLat = parseFloat(lat) - Math.min(this.corners.topRight[this.LAT_POS], this.corners.bottomLeft[this.LAT_POS]); var wd = this.element.width; var hg = this.element.height; var wdPercent = (walkLong * 100) / this.rangeTotalLong; var hgPercent = (walkLat * 100) / this.rangeTotalLat; switch (type) { case '%': return { walkLong: parseFloat(parseFloat(wdPercent).toFixed(2)), walkLat: parseFloat(parseFloat(hgPercent).toFixed(2)), type: '%', description: 'walklong from left, walkLat from bottom' } case 'px': return { walkLong: parseFloat(parseFloat(((wdPercent) * wd) / 100).toFixed(2)), walkLat: parseFloat(parseFloat(((hgPercent) * hg) / 100).toFixed(2)), type: 'px', description: 'walklong from left, walkLat from bottom' } default: return { percent: { walkLong: parseFloat(parseFloat(wdPercent).toFixed(2)), walkLat: parseFloat(parseFloat(hgPercent).toFixed(2)), type: '%', description: 'walklong from left, walkLat from bottom' }, pixel: { walkLong: parseFloat(parseFloat(((wdPercent) * wd) / 100).toFixed(2)), walkLat: parseFloat(parseFloat(((hgPercent) * hg) / 100).toFixed(2)), type: 'px', description: 'walklong from left, walkLat from bottom' } } } } addActions() { this.element.addEventListener('mousedown', (function (event) { // if (event.pointerType !== 'touch') { this.onDrag = true; this.element.style.cursor = 'grabbing'; event.preventDefault(); // } }).bind(this)) this.element.addEventListener('mouseup', (function (event) { // if (event.pointerType !== 'touch') { this.onDrag = false; this.element.style.cursor = 'grab'; event.preventDefault(); // } }).bind(this)) this.element.addEventListener('mouseleave', (function (event) { // if (event.pointerType !== 'touch') { this.onDrag = false; this.element.style.cursor = 'grab'; event.preventDefault(); // } }).bind(this)) this.element.addEventListener('mousemove', (function (event) { // if (e.pointerType !== 'touch') { if (this.onDrag) { this.element.style.cursor = 'grabbing'; this.setPosition(event.movementX, event.movementY); } // } }).bind(this)); //TOUCH EVENTS this.element.addEventListener('touchstart', (function (event) { this.onDrag = true; this.actualTouchPosition = { x: event.targetTouches[0].clientX, y: event.targetTouches[0].clientY, } event.preventDefault(); }).bind(this)) this.element.addEventListener('touchend', (function (event) { this.onDrag = false; event.preventDefault(); }).bind(this)) this.element.addEventListener('touchleave', (function (event) { this.onDrag = false; event.preventDefault(); }).bind(this)) this.element.addEventListener('touchmove', (function (e) { if (e.targetTouches.length >= 2) { //if two fingers, disable move, this move include zoom actions. this.onDrag = false; return; } var x = e.targetTouches[0].clientX - this.actualTouchPosition.x; var y = e.targetTouches[0].clientY - this.actualTouchPosition.y; this.actualTouchPosition = { x: e.targetTouches[0].clientX, y: e.targetTouches[0].clientY, } if (this.onDrag) { this.setPosition(x, y); } }).bind(this)); if (this.debug) { var LONG_POS_ARR = 0; var LAT_POS_ARR = 1; var rangeTotalLong = Math.abs(this.corners.topRight[LONG_POS_ARR] - this.corners.bottomLeft[LONG_POS_ARR]); var rangeTotalLat = Math.abs(this.corners.topRight[LAT_POS_ARR] - this.corners.bottomLeft[LAT_POS_ARR]); this.element.addEventListener('mousemove', (function (e) { var wd = this.element.width; var hg = this.element.height; var wdPercent = parseFloat(parseFloat(((e.offsetX / wd) * 100)).toFixed(2)); //localX do mouse em porcentagem em relação a imagem var hgPercent = parseFloat(parseFloat(((e.offsetY / hg) * 100)).toFixed(2)); //localY do mouse em porcentagem em relação a imagem var long = ((wdPercent * rangeTotalLong) / 100) + Math.min(this.corners.topRight[LONG_POS_ARR], this.corners.bottomLeft[LONG_POS_ARR]); var lat = ((hgPercent * rangeTotalLat) / 100) + Math.min(this.corners.topRight[LAT_POS_ARR], this.corners.bottomLeft[LAT_POS_ARR]); this.debugDiv.children[0].innerText = 'Mouse move event ->' + JSON.stringify({ offsetY: e.offsetY, offsetX: e.offsetX, wdPercent, hgPercent, long, lat }); }).bind(this)); this.element.addEventListener('touchmove', (function (e) { if (e.targetTouches.length >= 2) { //if two fingers, disable move, this move include zoom actions. return; } var x = e.targetTouches[0].target.x; //position of image element on screen var y = e.targetTouches[0].target.y; //position of image element on screen var clientY = e.targetTouches[0].clientY; //position clicked on screen var clientX = e.targetTouches[0].clientX; //position clicked on screen var offsetScrollTop = window.innerWidth < 768 ? window.scrollY : 0 var intoImage = { x: parseFloat(parseFloat(clientX - x).toFixed(2)), y: parseFloat(parseFloat(clientY - y).toFixed(2)) } if (this.flip180) { intoImage = { x: parseFloat(parseFloat(this.element.width - intoImage.x).toFixed(2)), y: parseFloat(parseFloat(this.element.height - intoImage.y).toFixed(2)), } } this.debugDiv.children[0].innerHTML = ` location of touch in image ->
${JSON.stringify(intoImage)}
window scroll ->
${(offsetScrollTop)}
` }).bind(this)); } } addPointer(pointers, _width = 50, _height = 50, fnContent = function (pointer) { return pointer }) { pointers = Array.isArray(pointers) ? pointers : [pointers]; pointers.forEach((function (pointer) { var content = fnContent(pointer); var onClickInPointer = (function ([long, lat], preferPosition) { this.setCenter([long, lat], preferPosition); }).bind(this); var callbackGetQuantityToWalkFromTheLimits = this.getQuantityToWalkFromTheLimits.bind(this); new Pointer(pointer, _width, _height, content, this.flip180, onClickInPointer, this.element, this.overlay, callbackGetQuantityToWalkFromTheLimits, this.debug); }).bind(this)); } setCenter([long, lat], preferPosition = null) { var walk = this.getQuantityToWalkFromTheLimits([long, lat], 'px'); var midScreen = {}; midScreen.left = this.parent.clientWidth / 2; midScreen.top = this.parent.clientHeight / 2; var topPrefer; var leftPrefer; if (!this.flip180) { topPrefer = (this.element.height - walk.walkLat - midScreen.top - (preferPosition === 'bottom' ? midScreen.top / 2 : 0)) * -1; //px leftPrefer = (walk.walkLong - midScreen.left) * -1; //px } else { topPrefer = (walk.walkLat - midScreen.top - (preferPosition === 'bottom' ? midScreen.top / 2 : 0)) * -1; //px leftPrefer = (this.element.width - (walk.walkLong + midScreen.left)) * -1; //px } this.setPosition(parseFloat(parseFloat(leftPrefer).toFixed(2)), parseFloat(parseFloat(topPrefer).toFixed(2)), false); if (this.debug) { this.debugDiv.children[1].innerHTML = ` setCenter ->
${JSON.stringify({ walk, midScreen, ...{ topPrefer, leftPrefer } })}
`; } } generalEquationOfTheLineReduced(pointA, pointB) { /** * y = ax+b * find a -> a = Δy / Δx * find b -> get any point containing Y and X, get A and calculate B */ var a = (pointB.y - pointA.y) / (pointB.x - pointA.x); var b = (pointA.y) + ((a * pointA.x) * -1); return { a, b, expression: `y = (${a}*x)+(${b})` } } getPosOfGeneralEquationOfTheLine(x = null, equation) { if (x) { return { y: (equation.a * x) + (equation.b), } } throw new Error('Is espected x'); } getCoordsOfThisAxis({ x, y }) { /** * percentage obtained from the X and Y axes clicked on the image */ var longPercent = x * 100 / this.element.width; var latPercent = y * 100 / this.element.height; /** * longitude obtained based on total range + minimum longitude value * latitude obtained based on maximum latitude value - latitude value obtained based on range */ var long = (longPercent * this.rangeTotalLong / 100) + Math.min(this.corners.topRight[this.LONG_POS], this.corners.bottomLeft[this.LONG_POS]); var lat = Math.max(this.corners.topRight[this.LAT_POS], this.corners.bottomLeft[this.LAT_POS]) - (latPercent * this.rangeTotalLat / 100); return [long, lat]; } getCenter() { return new Promise((function (resolve, reject) { try { var percentLeft = ((this.left * -1) + (this.parent.clientWidth / 2)) * 100 / this.element.width; var percentHeight = ((this.top * -1) + (this.parent.clientHeight / 2)) * 100 / this.element.height; var walkLong, walkLat; if (this.flip180) { walkLong = Math.max(this.corners.topRight[this.LONG_POS], this.corners.bottomLeft[this.LONG_POS]) - (percentLeft * this.rangeTotalLong / 100); walkLat = (percentHeight * this.rangeTotalLat / 100) + Math.min(this.corners.topRight[this.LAT_POS], this.corners.bottomLeft[this.LAT_POS]); } else { walkLong = (percentLeft * this.rangeTotalLong / 100) + Math.min(this.corners.topRight[this.LONG_POS], this.corners.bottomLeft[this.LONG_POS]); walkLat = Math.max(this.corners.topRight[this.LAT_POS], this.corners.bottomLeft[this.LAT_POS]) - (percentHeight * this.rangeTotalLat / 100); } resolve([walkLong, walkLat]); } catch (e) { reject(e); } }).bind(this)) } getLeft() { return this.left; } setLeft(_left) { this.left = _left; this.element.style.left = `${this.left}px`; } getTop() { return this.top; } setTop(_top) { this.top = _top; this.element.style.top = `${this.top}px`; } observable() { // instance of mutation this.observer = new MutationObserver((function (mutations) { mutations.forEach((function (mutation) { var maxLeft = (this.element.width + this.left) - this.parent.clientWidth; var maxTop = (this.element.height + this.top) - this.parent.clientHeight; var minLeft = this.left; var minTop = this.top; switch (mutation.type) { case 'attributes': this.overlay.style.width = `${this.element.width}px`; this.overlay.style.height = `${this.element.height}px`; if (maxLeft < 0) { //overflow width this.setPosition(-(maxLeft), this.top); } if (maxTop < 0) { //overflow height this.setPosition(this.left, -(maxTop)); } if (minLeft > 0) { //overflow width this.setPosition(0, this.top, false); } if (minTop > 0) { //overflow height this.setPosition(this.left, 0, false); } this.overlay.style.top = `${this.top}px`; this.overlay.style.left = `${this.left}px`; break; default: break; } }).bind(this)); }).bind(this)); // config of observer var config = { attributes: true, childList: false, characterData: true }; // target to mutation this.observer.observe(this.element, config); // instance of mutation this.observerOverlay = new MutationObserver((function (mutations) { mutations.forEach((function (mutation) { switch (mutation.type) { case 'attributes': switch (mutation.attributeName) { case 'data-bounds': var newBounds = mutation.target.getAttribute('data-bounds'); if (newBounds) { newBounds = JSON.parse(newBounds); this.corners = { bottomLeft: newBounds.bottomLeft, topRight: newBounds.topRight, } this.rangeTotalLong = Math.abs(newBounds.topRight[this.LONG_POS] - newBounds.bottomLeft[this.LONG_POS]); this.rangeTotalLat = Math.abs(newBounds.topRight[this.LAT_POS] - newBounds.bottomLeft[this.LAT_POS]); } break; default: break; } break; default: break; } }).bind(this)); }).bind(this)); // target to mutation this.observerOverlay.observe(this.overlay, config); } } class Container { constructor(_width, _height) { this.element = document.createElement("div"); this.element.style.height = `${_height}px`; this.element.style.maxHeight = `${_height}px`; this.element.style.minHeight = `${_height}px`; this.element.style.width = `${_width}px`; this.element.style.maxWidth = `${_width}px`; this.element.style.overflow = 'hidden'; this.element.style.position = 'relative'; this.element.style.boxSizing = 'border-box'; } getElement() { return this.element; } appendContent(content) { this.element.appendChild(content); } } class ZoomActions { zoomIncrement = 0; maxZoom = 200; isMobile = window.innerWidth <= 768; incrementer = this.isMobile ? 50 : 10; imageInstance; mobilePinchZoom = false; fingers; constructor(imgClass, maxZoom) { this.element = document.createElement('div'); this.zoomIn = document.createElement('button'); // this.zoomIn.innerText = '+'; this.zoomOut = document.createElement('button'); // this.zoomOut.innerText = '-'; this.maxZoom = maxZoom; this.imageInstance = imgClass; this.addActions(); this.element.appendChild(this.zoomIn); this.element.appendChild(this.zoomOut); var style = { position: 'absolute', right: '10px', top: '10px', display: 'flex', 'flex-direction': 'column', borderRadius: '4px', overflow: 'hidden', background: '#c2c2c2', rowGap: '1px', } this.addStyle(this.element, style); var buttonStyle = { width: '30px', height: '30px', background: '#fff', border: 'none', outline: 'none', cursor: 'pointer', }; this.addStyle(this.zoomIn, buttonStyle); this.addStyle(this.zoomOut, buttonStyle); this.addStyle(this.zoomIn, { 'backgroundImage': `url("data:image/svg+xml,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10 6c-.554 0-1 .446-1 1v2H7c-.554 0-1 .446-1 1s.446 1 1 1h2v2c0 .554.446 1 1 1s1-.446 1-1v-2h2c.554 0 1-.446 1-1s-.446-1-1-1h-2V7c0-.554-.446-1-1-1z' fill='%23333'/%3E%3C/svg%3E")`, }); this.addStyle(this.zoomOut, { 'backgroundImage': `url("data:image/svg+xml,%3Csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M7 9c-.554 0-1 .446-1 1s.446 1 1 1h6c.554 0 1-.446 1-1s-.446-1-1-1z' fill='%23333'/%3E%3C/svg%3E")`, }); } setZoom(percent, increment = true, hasAnimation = true) { return new Promise((function (resolve, reject) { try { var apply = false; var diffPercent = percent - 100; //150% -> 50% to increment or decrement var delay = this.isMobile ? 5 : 20; if (increment) { if (diffPercent <= this.maxZoom) { apply = true; } } else { if (diffPercent >= 0) { apply = true; } } if (apply) { if (hasAnimation) { var previousWidth = parseInt( this.imageInstance.getElement().style.width.indexOf('%') !== -1 ? this.imageInstance.getElement().style.width.replace('%', '') : 100 ); if (previousWidth) { var animation = window.setInterval((function () { if (previousWidth === percent) { window.clearInterval(animation); this.zoomIncrement = percent - 100; return resolve(); } if (previousWidth < percent) { if (((previousWidth + 1) | 0) === (percent | 0)) { previousWidth = percent; } else { previousWidth += 1; } } if (previousWidth > percent) { if (((previousWidth - 1) | 0) === (percent | 0)) { previousWidth = percent; } else { previousWidth -= 1; } } this.imageInstance.getCenter().then((function (coord) { this.imageInstance.getElement().style.width = `${previousWidth}%`; this.imageInstance.setCenter(coord, null); // resolve(); }).bind(this)) }).bind(this), delay); } else { reject('previousWidth is a NaN'); } } else { this.imageInstance.getCenter().then((function (coord) { this.imageInstance.getElement().style.width = `${percent}%`; this.imageInstance.setCenter(coord, null); this.zoomIncrement = percent - 100; resolve(); }).bind(this)) } } else { reject('not applyed'); } } catch (e) { reject(e); } }).bind(this)) } addActions() { this.zoomIn.addEventListener('click', (function (e) { e.stopPropagation(); if (this.zoomIncrement + this.incrementer <= this.maxZoom && !this.mobilePinchZoom) { this.zoomIncrement = this.zoomIncrement + this.incrementer; this.setZoom(this.zoomIncrement + 100); } }).bind(this)); this.zoomOut.addEventListener('click', (function (e) { e.stopPropagation(); if (this.zoomIncrement - this.incrementer >= 0 && !this.mobilePinchZoom) { this.zoomIncrement = this.zoomIncrement - this.incrementer; this.setZoom(this.zoomIncrement + 100, false); } }).bind(this)); /** * FIX: prevent to not propagate to the parent dblclick */ this.zoomIn.addEventListener('dblclick', function (e) { e.stopPropagation(); }); this.zoomOut.addEventListener('dblclick', function (e) { e.stopPropagation(); }); if (!this.imageInstance || !this.imageInstance.getElement().parentElement) return; var parent = this.imageInstance.getElement().parentElement; function pythagoras(a, b) { return Math.hypot(a, b); } var isNeedApplyZoom = (function (f1, f2, previousDist) { var firstHypot = pythagoras(this.fingers.finger2.x - this.fingers.finger1.x, this.fingers.finger2.y - this.fingers.finger1.y); var lastHypot = pythagoras(f2.clientX - f1.clientX, f2.clientY - f1.clientY); var dist = lastHypot - firstHypot; if (firstHypot !== lastHypot) { return { dist: (dist), difIncrement: previousDist ? (dist > previousDist) : (firstHypot < lastHypot), } } return null; }).bind(this); var isNeedSetPosition = (function () { return true; }).bind(this); parent.addEventListener('dblclick', (function (e) { var centerVisible = { x: !this.imageInstance.flip180 ? this.imageInstance.getLeft() * -1 + parent.clientWidth / 2 : this.imageInstance.getElement().width - (this.imageInstance.getLeft() * -1 + parent.clientWidth / 2), y: !this.imageInstance.flip180 ? this.imageInstance.getTop() * -1 + parent.clientHeight / 2 : this.imageInstance.getElement().height - (this.imageInstance.getTop() * -1 + parent.clientHeight / 2) }; var clicked = { x: e.offsetX, y: e.offsetY } var equation = this.imageInstance.generalEquationOfTheLineReduced(centerVisible, clicked); var maxRep = 3000; var count = 0; var actual; var coordMov; if (clicked.x > centerVisible.x) { while (clicked.x > centerVisible.x && count < maxRep) { actual = { x: centerVisible.x, y: this.imageInstance.getPosOfGeneralEquationOfTheLine(centerVisible.x, equation).y, } centerVisible.x += 10; count++; coordMov = this.imageInstance.getCoordsOfThisAxis(actual); window.setTimeout((function (_coordMov) { this.imageInstance.setCenter(_coordMov); }).bind(this, coordMov), count * 5); } } else if (clicked.x < centerVisible.x) { while (clicked.x < centerVisible.x && count < maxRep) { actual = { x: centerVisible.x, y: this.imageInstance.getPosOfGeneralEquationOfTheLine(centerVisible.x, equation).y, } centerVisible.x -= 10; count++; coordMov = this.imageInstance.getCoordsOfThisAxis(actual); window.setTimeout((function (_coordMov) { this.imageInstance.setCenter(_coordMov); }).bind(this, coordMov), count * 5); } } }).bind(this)) parent.addEventListener('touchstart', (function (e) { if (e.targetTouches.length === 2) { //pinch zoom var finger1 = e.targetTouches[0]; var finger2 = e.targetTouches[1]; this.fingers = { finger1: { x: finger1.clientX, y: finger1.clientY }, finger2: { x: finger2.clientX, y: finger2.clientY }, inMove: false, previousDist: null, actualZoom: this.zoomIncrement, }; this.mobilePinchZoom = true; } }).bind(this)) parent.addEventListener('touchleave', (function (e) { if (this.mobilePinchZoom) { this.mobilePinchZoom = false; } }).bind(this)) parent.addEventListener('touchend', (function (e) { if (this.mobilePinchZoom) { this.mobilePinchZoom = false; } }).bind(this)) parent.addEventListener('touchmove', (function (e) { if (this.mobilePinchZoom) { //pinch zoom var finger1 = e.targetTouches[0]; var finger2 = e.targetTouches[1]; var applyZoom = isNeedApplyZoom(finger1, finger2, this.fingers.previousDist); if (applyZoom) { var zoomIncrementTemp = parseFloat(parseFloat(((applyZoom.dist) * 100) / parent.clientWidth).toFixed(2)); this.fingers.previousDist = applyZoom.dist; if (zoomIncrementTemp) { /** * the map image is larger than the container * and how the distance to increment is based on the container * a calculation is made to check how much this distance has to be multiplied so that the image increases in the correct proportion * -> distance *= (image width) / (container width) */ zoomIncrementTemp *= (this.imageInstance.getElement().width / parent.clientWidth); this.setZoom( 100 + this.fingers.actualZoom + zoomIncrementTemp, // 100% + (incrementer initial: ex: 20%) + (the current incrementer. ex: 30%) -> set 150% zoom applyZoom.difIncrement, //info about the current zoom if is increment or decrement false //no animation to mobile ).then(function () { }).catch((function () { this.fingers.actualZoom = this.zoomIncrement; this.fingers.previousDist = null; }).bind(this)); } } if (isNeedSetPosition()) { // to-do } if (!this.fingers.inMove) { this.fingers.finger1 = { x: finger1.clientX, y: finger1.clientY }; this.fingers.finger2 = { x: finger2.clientX, y: finger2.clientY }; this.fingers.inMove = true; this.fingers.actualZoom = this.zoomIncrement; } } }).bind(this)) } getComponent() { return this.element; } addStyle(element, style) { Object.entries(style).forEach(function ([key, value]) { element.style[key] = value; }) } } /** * @description coordinates on the limits of the MAP */ var data = [ [-48.62494, -26.811114], //bottom left [-48.608, -26.797281] //top right ]; var init = function () { var mobile = window.innerWidth <= 768; var debug = window.location.search.indexOf('debug=true') !== -1; var divElement = new Container(mapContainer.clientWidth, !mobile ? 600 : 300); var mapaImg = new Img(imageOverlay, { bottomLeft: data[0], topRight: data[1] }, { flip180: true, debug }, divElement.getElement()); mapaImg.getElement().onload = function () { if (mapLoading) { mapLoading.remove(); } var imageElement = mapaImg.getElement(); //get image component divElement.appendContent(imageElement); //append in container var overlayElement = mapaImg.getOverlay(); //after get overlay component (get after inser image in container, to apply correctly heigth to overlay based in height of image) divElement.appendContent(overlayElement); //append in container var zoomActions = new ZoomActions(mapaImg, !mobile ? 200 : 900); //zom actions bar zoomActions.setZoom(mobile ? 500 : 150, true, false).then(function () { mapaImg.setCenter( atracoes.length > 1 ? [-48.613537, -26.80009] : [parseFloat(atracoes[0].longitude), parseFloat(atracoes[0].latitude)] ); //initial position of map in container , castelo das nações or the unique item in array }); var fnContentHTMLToPointer = function (atracao) { return (!(atracao.banner && atracao.nome && atracao.url)) ? null : `

${atracao.nome}

Para ver mais clique aqui
`; } mapaImg.addPointer(atracoes, 50, 50, fnContentHTMLToPointer); divElement.appendContent(zoomActions.getComponent()); } mapContainer.appendChild(divElement.getElement()); } init(); /** * @description On reize window in width, reload this map to prevent any bugs. * FIX if the width changes and not the height (the cell phone changes the height on the scroll screen, the url appears) */ var previousWidthOfScreen = window.innerWidth; window.addEventListener('resize', function () { if (previousWidthOfScreen !== window.innerWidth) { previousWidthOfScreen = window.innerWidth; while (mapContainer.children[0]) { mapContainer.removeChild(mapContainer.children[0]); } init(); } }) var prevHash = window.location.hash; window.setInterval(function () { if (window.location.hash != prevHash) { prevHash = window.location.hash; var attractionSelect = prevHash.replace('#', ''); if (attractionSelect && attractionSelect !== '#') { attractionSelect = attractionSelect.replace('atracao-', ''); var target = document.querySelector(`div[data-id="${attractionSelect}"]`); if (target) { target.click(); } } } }, 100); } });