UNPKG

cordova-plugin-googlemaps

Version:

Google Maps native SDK for Android and iOS, and Google Maps JavaScript API v3 for browser.

1,027 lines (895 loc) 33 kB
if (!window.Promise) { window.Promise = require('./Promise'); } var utils = require('cordova/utils'), common = require('./Common'), cordova_exec = require('cordova/exec'), BaseClass = require('./BaseClass'), Map = require('./Map'), StreetViewPanorama = require('./StreetViewPanorama'); function CordovaGoogleMaps(execCmd) { var self = this; BaseClass.apply(this); // Ignore checking for thse tags. self.doNotTraceTags = [ 'svg', 'p', 'pre', 'script', 'style' ]; self.execCmd = execCmd; // random unique number self.saltHash = Math.floor(Math.random() * Date.now()); // Hold map instances. self.MAPS = {}; self.MAP_CNT = 0; // Hold the DOM hierarchy graph. self.domPositions = {}; // True while the putHtmlElements is working. self.isChecking = false; self.transforming = false; // True if the code requests to execute the putHtmlElements while working it. self.checkRequested = false; // True if some elements are changed, such as added an element. self.isThereAnyChange = false; // Indicate the native timer is stopped or not. self.set('isSuspended', false); // Cache for updateMapPositionOnly self.prevMapRects = {}; // Control variables for followMaps() function self.scrollEndTimer = null; self.transitionEndTimer = null; self.transformTargets = {}; self.transitionCnt = 0; //------------------------------------------------------------------------------ // Using MutationObserver, observe only added/removed or style changed elements //------------------------------------------------------------------------------ var observer = new MutationObserver(function(mutations) { common.nextTick(function() { var doTraceTree = true; Array.prototype.slice.call(mutations, 0).forEach(function(mutation) { if (mutation.type === 'childList') { // If some elements are added, check them. if (mutation.addedNodes) { Array.prototype.slice.call(mutation.addedNodes, 0).forEach(function(node) { if (node.nodeType !== Node.ELEMENT_NODE) { return; } self.setDomId.call(self, node); }); } // If some elements are removed from the DOM tree, remove their information. if (mutation.removedNodes) { Array.prototype.slice.call(mutation.removedNodes, 0).forEach(function(node) { if (node.nodeType !== Node.ELEMENT_NODE || !node.hasAttribute('__pluginDomId')) { return; } node._isRemoved = true; self.removeDomTree.call(self, node); }); } } else { // Some attributes are changed. // If the element has __pluginDomId, check it. if (mutation.target.nodeType !== Node.ELEMENT_NODE) { return; } if (mutation.target.hasAttribute('__pluginDomId')) { // elemId = mutation.target.getAttribute('__pluginDomId'); var transformCSS = common.getStyle(mutation.target, 'transform') || common.getStyle(mutation.target, '-webkit-transform') || common.getStyle(mutation.target, 'transition') || common.getStyle(mutation.target, '-webkit-transition'); if (transformCSS !== 'none') { mutation.target.dispatchEvent(common.createEvent('transitionstart')); // Omit executing the putHtmlElements() at this time, // because it is executed at `transitionend`. doTraceTree = false; } } } }); if (doTraceTree) { self.isThereAnyChange = true; common.nextTick(self.putHtmlElements.bind(self)); } }); }); var attachObserver = function() { observer.observe(document.body.parentElement, { attributes : true, childList: true, subtree: true, attributeFilter: ['style', 'class'] }); }; self.one('start', attachObserver); self.on('isSuspended_changed', function(oldValue, newValue) { if (newValue) { cordova_exec(null, null, 'CordovaGoogleMaps', 'pause', []); } else { cordova_exec(null, null, 'CordovaGoogleMaps', 'resume', []); } }); } utils.extend(CordovaGoogleMaps, BaseClass); CordovaGoogleMaps.prototype.traceDomTree = function(element, elemId, isMapChild) { //------------------------------------------ // Create the DOM hierarchy graph //------------------------------------------ var self = this; // If the root DOM element should be ignored, // remove it from the tree graph. if (self.doNotTraceTags.indexOf(element.tagName.toLowerCase()) > -1 || !common.shouldWatchByNative(element)) { self.removeDomTree.call(self, element); return; } // Get the z-index CSS var zIndexProp = common.getZIndex(element); // Stores dom information var isCached = elemId in self.domPositions; self.domPositions[elemId] = { // If `pointer-events:none`, continue the hitTest process pointerEvents: common.getStyle(element, 'pointer-events'), // Only true if element is mapDiv isMap: element.hasAttribute('__pluginMapId'), // Calculate dom clickable region size: common.getDivRect(element), // Calculate actual z-index value zIndex: zIndexProp, // If `overflow: hidden or scroll`, the native side needs to recalculate actual size overflowX: common.getStyle(element, 'overflow-x'), overflowY: common.getStyle(element, 'overflow-y'), // Hold the elementId of child elements children: [], // Hold the list of map id. containMapIDs: (isCached ? self.domPositions[elemId].containMapIDs : {}) }; // Should this process continue to child elements? // - condition 1: // If the child element contains map, should continue. // // - condition 2: // If the element is one of the children of map div, // check all elements, especially for HtmlInfoWindow. // // - condition 3: // If the pointer-css is 'none', continue the proces, // because the element might be a container of the map. // // - condition 4: // If the z-index is 'inherit', there might be some elements. var containMapCnt = (Object.keys(self.domPositions[elemId].containMapIDs)).length; isMapChild = isMapChild || self.domPositions[elemId].isMap; if ((containMapCnt > 0 || isMapChild || self.domPositions[elemId].pointerEvents === 'none' || zIndexProp.isInherit) && element.children.length > 0) { Array.prototype.slice.call(element.children, 0).forEach(function functionName(child) { if (self.doNotTraceTags.indexOf(child.tagName.toLowerCase()) > -1 || !common.shouldWatchByNative(child)) { self.removeDomTree.call(self, child); return; } var childId = common.getPluginDomId(child); self.domPositions[elemId].children.push(childId); self.traceDomTree.call(self, child, childId, isMapChild); }); } }; CordovaGoogleMaps.prototype.setDomId = function(element) { //---------------------------------------------------------------------- // This procedure generates unique ID // for all elements under the element, and the element itself. //---------------------------------------------------------------------- common.getPluginDomId(element); if (element.children) { Array.prototype.slice.call(element.children, 0).forEach(common.getPluginDomId.bind(self)); } }; CordovaGoogleMaps.prototype.putHtmlElements = function() { //---------------------------------------------------------------------- // This procedure generates a DOM hierarchy tree graph from <BODY>. //---------------------------------------------------------------------- var self = this; // If this process is working, just checkRequested = true. // The putHtmlElements will execute itself if the flag is true. if (self.isChecking || self.transforming) { self.checkRequested = true; return; } self.checkRequested = false; // If no elements are changed after the last checking, // stop the native timer in order to save the battery usage. if (!self.isThereAnyChange) { self.pause(); return; } self.isChecking = true; // If there is no visible or clickable map, // stop checking var mapIDs = Object.keys(self.MAPS); var touchableMapList = mapIDs.filter(function(mapId) { var map = self.MAPS[mapId]; var isTouchable = false; if (map) { var mapDiv = map.getDiv(); isTouchable = ( map.getVisible() && // map.getClickable() && <-- don't consider this. mapDiv && common.shouldWatchByNative(mapDiv)); if (isTouchable) { var elemId = common.getPluginDomId(mapDiv); var domInfo = self.domPositions[elemId]; if (domInfo) { self.domPositions[elemId].size = common.getDivRect(mapDiv); isTouchable = domInfo.size.width * domInfo.size.height > 0; } else { isTouchable = false; } } map.set('__isAttached', isTouchable); } return isTouchable; }); if (self._prevMapCnt === 0 && touchableMapList.length === 0) { self.pause(); return; } self._prevMapCnt = touchableMapList.length; // If there is another check request, // DOM tree might be changed. // So let's start again. if (self.checkRequested || self.transforming) { setTimeout(function() { self.isChecking = false; common.nextTick(self.putHtmlElements.bind(self)); }, 50); return; } // Since the native side needs to know the 'latest' DOM information, // clear the DOM cache. common._clearInternalCache(); // Generate the DOM hierarchy tree from <body> tag. common.getPluginDomId(document.body); self.traceDomTree.call(self, document.body, 'root', false); // If the map div is not displayed (such as display='none'), // ignore the map temporally. var stopFlag = false; mapIDs.forEach(function(mapId) { var div = self.MAPS[mapId].getDiv(); if (!div || stopFlag) { return; } // Does this dom is really existed? var elemId = div.getAttribute('__pluginDomId'); if (!elemId) { // The map div is removed if (mapId in self.MAPS) { self.MAPS[mapId].remove(); } stopFlag = true; return; } if (!(elemId in self.domPositions)) { // Is the map div removed? var ele = document.querySelector('[__pluginMapId="' + mapId + '"]'); if (!ele) { // If no div element, remove the map. if (mapId in self.MAPS) { self.MAPS[mapId].remove(); } stopFlag = true; } return; } }); if (stopFlag || self.transforming) { // There is no map information (maybe timining?), // or the another check request is waiting, // start again. self.isThereAnyChange = true; setTimeout(function() { self.isChecking = false; common.nextTick(self.putHtmlElements.bind(self)); }, 50); return; } //----------------------------------------------------------------- // Pass the DOM hierarchy tree graph to native side //----------------------------------------------------------------- self.resume(); //console.log('--->putHtmlElements to native (start)', JSON.parse(JSON.stringify(self.domPositions))); cordova_exec(function() { //console.log('--->putHtmlElements to native (done)'); // If there is another checking request, try again. if (self.checkRequested) { setTimeout(function() { self.isChecking = false; common.nextTick(self.putHtmlElements.bind(self)); }, 50); return; } setTimeout(function() { if (!self.isChecking && !self.transforming) { common.nextTick(self.followMapDivPositionOnly.bind(self)); } }, 50); self.isChecking = false; self.pause(); }, null, 'CordovaGoogleMaps', 'putHtmlElements', [self.domPositions]); }; CordovaGoogleMaps.prototype.pause = function() { var self = this; self.set('isSuspended', true); self.isThereAnyChange = false; self.isChecking = false; }; CordovaGoogleMaps.prototype.resume = function() { var self = this; self.set('isSuspended', false); }; // If the `transitionend` event is ocurred on the observed element, // adjust the position and size of the map view CordovaGoogleMaps.prototype.followMaps = function(evt) { var self = this; if (self.MAP_CNT === 0) { return; } self.transitionCnt++; self.transforming = true; var changes = self.followMapDivPositionOnly.call(self); if (self.scrollEndTimer) { clearTimeout(self.scrollEndTimer); self.scrollEndTimer = null; } if (changes) { self.scrollEndTimer = setTimeout(self.followMaps.bind(self, evt), 100); } else { setTimeout(self.onTransitionEnd.bind(self, evt), 100); } }; // CSS event `transitionend` is fired even the target dom element is still moving. // In order to detect 'correct demention after the transform', wait until stable. CordovaGoogleMaps.prototype.onTransitionEnd = function(evt) { var self = this; if (self.MAP_CNT === 0 || !evt) { return; } var target = evt.target; if (!target) { target = document.body; } target = target.getAttribute === 'function' ? target : document.body; var elemId = target.getAttribute('__pluginDomId'); self.transformTargets[elemId] = {left: -1, top: -1, right: -1, bottom: -1, finish: false, target: target}; if (!self.transitionEndTimer) { self.transitionEndTimer = setTimeout(self.detectTransitionFinish.bind(self), 100); } }; CordovaGoogleMaps.prototype.detectTransitionFinish = function() { var self = this; var keys = Object.keys(self.transformTargets); var onFilter = function(elemId) { if (self.transformTargets[elemId].finish) { return false; } var target = self.transformTargets[elemId].target; var divRect = common.getDivRect(target); var prevRect = self.transformTargets[elemId]; if (!prevRect || divRect.left === prevRect.left && divRect.top === prevRect.top && divRect.right === prevRect.right && divRect.bottom === prevRect.bottom) { self.transformTargets[elemId].finish = true; } self.transformTargets[elemId].left = divRect.left; self.transformTargets[elemId].top = divRect.top; self.transformTargets[elemId].right = divRect.right; self.transformTargets[elemId].bottom = divRect.bottom; return !self.transformTargets[elemId].finish; }; var notYetTargets = keys.filter(onFilter); onFilter = null; if (self.transitionEndTimer) { clearTimeout(self.transitionEndTimer); } self.transitionCnt -= notYetTargets.length > 1 ? 1 : 0; if (notYetTargets.length > 0 && self.transitionCnt == 0) { self.transitionEndTimer = setTimeout(self.detectTransitionFinish.bind(self), 100); } else { clearTimeout(self.transitionEndTimer); self.transitionEndTimer = null; setTimeout(self.onTransitionFinish.bind(self), 100); } }; CordovaGoogleMaps.prototype.onTransitionFinish = function() { var self = this; if (self.MAP_CNT === 0) { self.transforming = false; return; } // Don't block by transform flag // because some ionic CSS technique can not trigger `transitionstart` event. // if (!self.transforming) { // return; // } self.transforming = false; var changes = self.followMapDivPositionOnly.call(self); if (changes) { self.scrollEndTimer = setTimeout(self.onTransitionFinish.bind(self), 100); } else { self.transformTargets = undefined; self.transformTargets = {}; self.isThereAnyChange = true; self.checkRequested = false; self.putHtmlElements.call(self); //self.pause(); self.scrollEndTimer = null; } }; CordovaGoogleMaps.prototype.removeDomTree = function(node) { //---------------------------------------------------------------------------- // This procedure removes the DOM tree graph from the specified element(node) //---------------------------------------------------------------------------- var self = this; if (!node || !node.querySelectorAll) { return; } // Remove the information of children // which have the `__pluginDomId` attribute. var nodeList = node.querySelectorAll('[__pluginDomId]'); var children = []; for (var i = 0; i < nodeList.length; i++) { children.push(nodeList[i]); } children.push(node); var isRemoved = node._isRemoved; children.forEach(function(child) { var elemId = child.getAttribute('__pluginDomId'); // If the DOM is removed from the DOM tree, // remove the attribute. // (Note that the `_isRemoved` flag is set in MutationObserver.) if (isRemoved) { child.removeAttribute('__pluginDomId'); // If map div, remove the map also. if (child.hasAttribute('__pluginMapId')) { var mapId = child.getAttribute('__pluginMapId'); if (mapId in self.MAPS) { self.MAPS[mapId].remove(); } } delete self.domPositions[elemId]; } common._removeCacheById(elemId); }); }; CordovaGoogleMaps.prototype.invalidate = function(opts) { //------------------------------- // Recheck the DOM positions //------------------------------- var self = this; opts = opts || {}; if (opts.force) { self.isThereAnyChange = true; } self.followMapDivPositionOnly.call(self, opts); common.nextTick(function() { self.resume.call(self); self.putHtmlElements.call(self); }); }; CordovaGoogleMaps.prototype.followMapDivPositionOnly = function(opts) { //---------------------------------------------------------------------------- // Follow the map div position and size only without the DOM check. // This is designed for scrolling. //---------------------------------------------------------------------------- var self = this; opts = opts || {}; var mapRects = {}; var mapIDs = Object.keys(self.MAPS); var changed = false; mapIDs.forEach(function(mapId) { var map = self.MAPS[mapId]; if (map && map.getVisible() && map.getDiv() && common.shouldWatchByNative(map.getDiv())) { // Obtain only minimum information var mapDiv = map.getDiv(); var divId = mapDiv.getAttribute('__pluginDomId'); mapRects[divId] = { size: common.getDivRect(mapDiv), zIndex: common.getZIndex(mapDiv) }; // Is the map moved? if (!changed && self.prevMapRects && divId in self.prevMapRects) { if (self.prevMapRects[divId].size.left !== mapRects[divId].size.left) { //console.log('changed(left)', divId, self.prevMapRects[divId].size.left, mapRects[divId].size.left); changed = true; } if (!changed && self.prevMapRects[divId].size.top !== mapRects[divId].size.top) { //console.log('changed(top)', divId, self.prevMapRects[divId].size.top, mapRects[divId].size.top); changed = true; } if (!changed && self.prevMapRects[divId].size.width !== mapRects[divId].size.width) { //console.log('changed(width)', divId, self.prevMapRects[divId].size.width, mapRects[divId].size.width); changed = true; } if (!changed && self.prevMapRects[divId].size.height !== mapRects[divId].size.height) { //console.log('changed(height)', divId, self.prevMapRects[divId].size.height, mapRects[divId].size.height); changed = true; } if (!changed && self.prevMapRects[divId].zIndex.z !== mapRects[divId].zIndex.z) { //console.log('changed(zIndex.z)', divId, self.prevMapRects[divId].zIndex.z, mapRects[divId].zIndex.z); changed = true; } } } }); self.prevMapRects = mapRects; // If changed, move the map views. if (changed || opts.force) { cordova_exec(null, null, 'CordovaGoogleMaps', 'updateMapPositionOnly', [mapRects]); return changed; } return false; }; CordovaGoogleMaps.prototype.invalidateN = function(cnt) { var self = this; if (self.cnt > 0) { return; } self.cnt = cnt; var timer = setInterval(function() { common.nextTick(function() { self.followMapDivPositionOnly.call(self); self.cnt--; if (self.cnt === 0) { clearInterval(timer); self.invalidate.call(self, {force: true}); } }); }, 50); }; CordovaGoogleMaps.prototype.getMap = function(div, mapOptions) { //---------------------------------------------------------------------------- // This procedure return a map instance. // - usage 1 // plugin.google.maps.Map.getMap(options?) returns a map instance. // // - usage 2 // plugin.google.maps.Map.getMap(mapDiv, options?) returns a map instance. // The generated map follows the mapDiv position and size automatically. // // - usage 3 (not good way) // In order to keep the backward compatibility for v1, // if the mapDiv has already a map, returns the map instance for the map div. //---------------------------------------------------------------------------- var self = this, mapId, elem, elemId; if (common.isDom(div)) { mapId = div.getAttribute('__pluginMapId'); // Wow, the app specifies the map div that has already another map, // but the app try to create new map. // In this case, remove the old map instance automatically. if (mapId && self.MAPS[mapId].getDiv() !== div) { elem = self.MAPS[mapId].getDiv(); while(elem && elem.nodeType === Node.ELEMENT_NODE) { elemId = elem.getAttribute('__pluginDomId'); if (elemId && elemId in self.domPositions) { self.domPositions[elemId].containMapIDs = self.domPositions[elemId].containMapIDs || {}; delete self.domPositions[elemId].containMapIDs[mapId]; if ((Object.keys(self.domPositions[elemId].containMapIDs).length) < 1) { delete self.domPositions[elemId]; } } elem = elem.parentNode; } self.MAPS[mapId].remove(); mapId = undefined; } if (mapId && mapId in self.MAPS) { // Usage 3 // If the map div has already a map, // return the map instance. return self.MAPS[mapId]; } } if (!mapId) { mapId = 'map_' + self.MAP_CNT + '_' + self.saltHash; } // Create a map instance. var map = new Map(mapId, self.execCmd); map.set('__isAttached', common.isDom(div)); // Catch all events for this map instance, then pass to the instance. // (Don't execute this native callback from your code) window.plugin.google.maps[mapId] = nativeCallback.bind(map); map.on('__isAttached_changed', function(oldValue, newValue) { if (newValue) { cordova_exec(null, null, map.__pgmId, 'attachToWebView', []); } else { cordova_exec(null, null, map.__pgmId, 'detachFromWebView', []); } }); // If the mapDiv is changed, clean up the information for old map div, // then add new information for new map div. map.on('div_changed', function(oldDiv, newDiv) { var elemId, ele; if (common.isDom(oldDiv)) { oldDiv.removeAttribute('__pluginMapId'); ele = oldDiv; while(ele && ele != document.body.parentNode) { elemId = ele.getAttribute('__pluginDomId'); if (elemId) { self.domPositions[elemId].containMapIDs = self.domPositions[elemId].containMapIDs || {}; delete self.domPositions[elemId].containMapIDs[mapId]; if ((Object.keys(self.domPositions[elemId].containMapIDs)).length < 1) { delete self.domPositions[elemId]; } } ele.removeAttribute('__pluginDomId'); if (ele.classList) { ele.classList.remove('_gmaps_cdv_'); } else if (ele.className) { ele.className = ele.className.replace(/_gmaps_cdv_/g, ''); ele.className = ele.className.replace(/\s+/g, ' '); } ele = ele.parentNode; } } if (common.isDom(newDiv)) { elemId = common.getPluginDomId(newDiv); elem = newDiv; var isCached; while(elem && elem.nodeType === Node.ELEMENT_NODE) { elemId = common.getPluginDomId(elem); if (common.shouldWatchByNative(elem)) { if (elem.shadowRoot) { elem.shadowRoot.addEventListener('transitionend', self.onTransitionEnd.bind(self), {capture: true}); elem.shadowRoot.addEventListener('scroll', self.followMaps.bind(self), {capture: true}); } isCached = elemId in self.domPositions; self.domPositions[elemId] = { pointerEvents: common.getStyle(elem, 'pointer-events'), isMap: false, size: common.getDivRect(elem), zIndex: common.getZIndex(elem), children: (elemId in self.domPositions ? self.domPositions[elemId].children : []), overflowX: common.getStyle(elem, 'overflow-x'), overflowY: common.getStyle(elem, 'overflow-y'), containMapIDs: (isCached ? self.domPositions[elemId].containMapIDs : {}) }; self.domPositions[elemId].containMapIDs[mapId] = 1; } else { self.removeDomTree.call(self, elem); } elem = elem.parentNode; } elemId = common.getPluginDomId(newDiv); self.domPositions[elemId].isMap = true; } }); // If the map is removed, clean up the information. map.one('remove', self._remove.bind(self, mapId)); self.MAP_CNT++; self.isThereAnyChange = true; if (div instanceof Promise) { // This hack code for @ionic-native/google-maps div.then(function(params) { self.MAPS[mapId] = map; params = params || []; params.unshift(map); postMapInit.apply(self, params); }); } else { // Normal code flow self.MAPS[mapId] = map; postMapInit.call(self, map, div, mapOptions); } return map; }; CordovaGoogleMaps.prototype.getPanorama = function(div, streetViewOptions) { var self = this; var mapId = 'streetview_' + self.MAP_CNT + '_' + self.saltHash; // Create a panorama instance. var panorama = new StreetViewPanorama(mapId, self.execCmd); // Catch all events for this map instance, then pass to the instance. // (Don't execute this native callback from your code) window.plugin.google.maps[mapId] = nativeCallback.bind(panorama); self.MAP_CNT++; panorama.one('remove', self._remove.bind(self, mapId)); if (div instanceof Promise) { // This hack code for @ionic-native/google-maps div.then(function(params) { self.MAPS[mapId] = panorama; params = params || []; params.unshift(panorama); postPanoramaInit.apply(self, params); }); } else { // Normal code flow self.MAPS[mapId] = panorama; postPanoramaInit.call(self, panorama, div, streetViewOptions); } return panorama; }; CordovaGoogleMaps.prototype._remove = function(mapId) { var self = this; delete window.plugin.google.maps[mapId]; var map = self.MAPS[mapId]; if (map) { var div = map.getDiv(); if (!div) { div = document.querySelector('[__pluginMapId="' + mapId + '"]'); } if (div) { div.removeAttribute('__pluginMapId'); } var keys = Object.keys(self.domPositions); keys.forEach(function(elemId) { self.domPositions[elemId].containMapIDs = self.domPositions[elemId].containMapIDs || {}; delete self.domPositions[elemId].containMapIDs[mapId]; if ((Object.keys(self.domPositions[elemId].containMapIDs)).length < 1) { delete self.domPositions[elemId]; } }); self.MAPS[mapId].destroy(); } delete self.MAPS[mapId]; map = undefined; // If the app have no map, stop the native timer. if ((Object.keys(self.MAPS)).length === 0) { common._clearInternalCache(); self.pause(); } }; function nativeCallback(params) { var args = params.args || []; args.unshift(params.evtName); this[params.callback].apply(this, args); } function postPanoramaInit(panorama, div, options) { var self = this; var mapId = panorama.getId(); self.isThereAnyChange = true; if (!common.isDom(div)) { console.error('[GoogleMaps] You need to specify a dom element(such as <div>) for this method', div); return; } // If the given div is not fully ready, wait a little if (!common.shouldWatchByNative(div)) { setTimeout(function() { common.nextTick(postPanoramaInit.bind(self, panorama, div, options)); }, 50); return; } if (div.offsetWidth < 100 || div.offsetHeight < 100) { console.error('[GoogleMaps] Minimum container dimention is 100x100 in pixels.', div); return; } // If the mapDiv is specified, // the native side needs to know the map div position // before creating the map view. div.setAttribute('__pluginMapId', mapId); var elemId = common.getPluginDomId(div); var elem = div; var isCached, zIndexList = []; while(elem && elem.nodeType === Node.ELEMENT_NODE) { elemId = common.getPluginDomId(elem); if (common.shouldWatchByNative(elem)) { isCached = elemId in self.domPositions; self.domPositions[elemId] = { pointerEvents: common.getStyle(elem, 'pointer-events'), isMap: false, size: common.getDivRect(elem), zIndex: common.getZIndex(elem), children: [], overflowX: common.getStyle(elem, 'overflow-x'), overflowY: common.getStyle(elem, 'overflow-y'), containMapIDs: (isCached ? self.domPositions[elemId].containMapIDs : {}) }; zIndexList.unshift(self.domPositions[elemId].zIndex); self.domPositions[elemId].containMapIDs[mapId] = 1; } else { self.removeDomTree.call(self, elem); } elem = elem.parentNode; } // Calculate the native view z-index var depth = 0; zIndexList.forEach(function(info, idx) { if (!info.isInherit && info.z === 0) { depth *= 10; } depth += (info.z + 1) / (1 << idx) + 0.01; }); depth = Math.floor(depth * 10000); elemId = common.getPluginDomId(div); self.domPositions[elemId].isMap = true; var args = Array.prototype.slice.call(arguments, 0); args.unshift({ __pgmId: mapId, depth: depth }); cordova_exec(function() { panorama.getPanorama.apply(panorama, args); }, null, 'CordovaGoogleMaps', 'putHtmlElements', [self.domPositions]); } function postMapInit(map, div, options) { var self = this; var mapId = map.getId(); var args = []; if (common.isDom(div)) { // If the given div is not fully ready, wait a little if (!common.shouldWatchByNative(div)) { setTimeout(function() { common.nextTick(postMapInit.bind(self, map, div, options)); }, 50); return; } if (div.offsetWidth < 100 || div.offsetHeight < 100) { console.error('[GoogleMaps] Minimum container dimention is 100x100 in pixels.', div); return; } // If the mapDiv is specified, // the native side needs to know the map div position // before creating the map view. div.setAttribute('__pluginMapId', mapId); var elemId = common.getPluginDomId(div); var elem = div; var isCached; var zIndexList = []; while(elem && elem.nodeType === Node.ELEMENT_NODE) { elemId = common.getPluginDomId(elem); if (common.shouldWatchByNative(elem)) { isCached = elemId in self.domPositions; self.domPositions[elemId] = { pointerEvents: common.getStyle(elem, 'pointer-events'), isMap: false, size: common.getDivRect(elem), zIndex: common.getZIndex(elem), children: [], overflowX: common.getStyle(elem, 'overflow-x'), overflowY: common.getStyle(elem, 'overflow-y'), containMapIDs: (isCached ? self.domPositions[elemId].containMapIDs : {}) }; zIndexList.unshift(self.domPositions[elemId].zIndex); self.domPositions[elemId].containMapIDs[mapId] = 1; } else { self.removeDomTree.call(self, elem); } elem = elem.parentNode; } // Calculate the native view z-index var depth = 0; zIndexList.forEach(function(info, idx) { if (!info.isInherit && info.z === 0) { depth *= 10; } depth += (info.z + 1) / (1 << idx) + 0.01; }); depth = Math.floor(depth * 10000); args.push({ __pgmId: mapId, depth: depth }); args.push(div); if (options) { args.push(options); } elemId = common.getPluginDomId(div); self.domPositions[elemId].isMap = true; cordova_exec(function() { map.getMap.apply(map, args); }, null, 'CordovaGoogleMaps', 'putHtmlElements', [self.domPositions]); } else { args.push({ __pgmId: mapId, depth: 0 }); args.push(null); if (options) { args.push(options); } map.getMap.apply(map, args); } } module.exports = CordovaGoogleMaps;