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
JavaScript
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;