/**
* GeometryControls Class v0.2
* Copyright (c) 2008, Google
* Author: Chris Marx and Pamela Fox and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This class lets you add a control to the map which mimics the MyMaps controls
* and allows for adding markers, polylines and polygons to the map and for uploading.
*/
/**
* Global wrapper function for getElementsById()
* @param {String} id Element's id
*/
function get$(id) {
return document.getElementById(id);
//TODO implement an element cache?
};
/**
* Creates the parent class for Geometry Controls
* @constructor
* @param {Object} opt_opts Named optional arguments:
* @param {Object} opt_opts.controlPositionFloat A GControlAnchor for positioning the parent control container (if used)
* @param {Object} opt_opts.controlPosition An array with pixel values for parent control position
* @param {String} opt_opts.buttonWidth Button width in pixels
* @param {String} opt_opts.buttonHeight Button height in pixels
* @param {String} opt_opts.buttonBorder Button border in pixels
* @param {String} opt_opts.infoWindowHtmlURL The url if the html template file, containing configurable html and json for control infowindows and options
* @param {Object} opt_opts.stylesheets An array of urls of stylesheets to be appended
* @param {Boolean} opt_opts.autoSave Determines whether the autoSave feature (via AOP) is turned on or off
* @param {String} opt_opts.cssId The base name for css styles
* @param {Boolean} opt_opts.debug Sets debug statements to GLog or turns them off for production
*/
function GeometryControls(opt_opts){
var me = this;
//self documenting object with default settings shared by geometry controls
me.Options = {
controlPostitionFloat:G_ANCHOR_TOP_RIGHT,
controlPosition:[0,0],
buttonWidth:'33',
buttonHeight:'33',
buttonBorder:'0',
buttonCursor:'pointer',
infoWindowHtmlURL:"data/geometry_html_template.html",
stylesheets:["styles/google.maps.base.css","styles/google.maps.ms_styles.css"],
autoSave:true, //TODO have option to turn on autoSave for individual controls?
cssId:"emmc-geom", //for generic components shared between multiple controls
debug:true
};
//overide the default Options
if(opt_opts){
for (var o in opt_opts) {
me.Options[o] = opt_opts[o];
}
} else {
//me.debug("??");
}
me.isIE = navigator.appName.indexOf('Explorer') > -1;
me.isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
me.map = null;
me.container = null;
me.controls = {};
me.buttons_ = {};
me.stopDigitizingFuncs_ = {};
me.infoWindowHtmlTemplates = {};
me.bounds = new GLatLngBounds(); //for setting bounds when loading data
me.autoSaveListener = null; //external handle for aop
//run functions that need to be initialized at startup
me.runInitFunctions_();
};
/**
* Inherits from GControl, makes it convenient to use map.addControl()
*/
GeometryControls.prototype = new GControl();
/**
* Run functions that need to load content when class is instantiated
*/
GeometryControls.prototype.runInitFunctions_ = function(){
var me = this;
me.getInfoWindowHtml_();
me.addGoogleMapsCSS_();
if(me.Options.autoSave){
me.addAutoSaveAspect();
};
};
/**
* Required by GMaps API for controls.
* @param {Object} opt_opts
* @param {Object} opt_opts.controlPositionFloat Constant for float position
* @param {Object} opt_opts.controlPosition An array with top/left offset for control
* @return {GControlPosition} Default location for control
*/
GeometryControls.prototype.getDefaultPosition = function(opt_opts) {
var me = this, opt = me.Options, ctrlPosition;
if (opt_opts) {
ctrlPosition = new GControlPosition(opt_opts.controlPositionFloat,new GSize(opt_opts.controlPosition[0],opt_opts.controlPosition[1]));
} else {
ctrlPosition = new GControlPosition(opt.controlPositionFloat, new GSize(opt.controlPosition[0],opt.controlPosition[1]));
}
return ctrlPosition;
};
/**
* Is called by GMap2's addOverlay method. Creates the button and appends to the map div.
* Since this is called after being added to map, we can access #addControl to add geometry controls and
* make them available here.
* @param {GMap2} map The map that has had this ExtMapTypeControl added to it.
* @return {DOM Object} Div that holds the control
*/
GeometryControls.prototype.initialize = function(map){
var me = this;
me.map = map;
//could be used to group all controls. currently controls are set to position themselves in their own containers
me.container = document.createElement("div");
map.getContainer().appendChild(me.container);
//initialize the controls added with #addControl
for(var name in me.controls){
map.addControl(me.controls[name]);
}
//initialize the maps's infowindow (it appears it takes longer the first time it is created, so avoid this timing issue)
map.getInfoWindow();
return me.container;
};
/**
* Creates a button, and attaches listeners
* @param {Object} required_opts All parameters are required!!
* @param {String} required_opts.controlName Name of control
* @param {Object} required_opts.button_opts
* @param {String} button_opts.img_up_url Url of up image
* @param {String} button_opts.img_down_url Url of down image
* @param {String} button_opts.tooltip Text of tooltip
* @param {Function} required_opts.startDigitizing Function for turning on this digitizer control
* @param {Function} required_opts.stopDigitizing Function for turnong off this digitizer control
*/
GeometryControls.prototype.createButton = function(required_opts){
var me = this, opts = required_opts, Options = me.Options;
//make sure a digitizing function is present
if(typeof(opts.startDigitizing) && typeof(opts.stopDigitizing) !== "function"){
me.debug("Digitizing functions for #createButton are required");
return;
}
var button = {};
button.opts = opts.button_opts;
var button_img = document.createElement('img');
button_img.style.cursor = button.opts.buttonCursor || Options.buttonCursor;
button_img.width = button.opts.buttonWidth || Options.buttonWidth;
button_img.height = button.opts.buttonHeight || Options.buttonHeight;
button_img.border = button.opts.buttonBorder || Options.buttonBorder;
button_img.src = button.opts.img_up_url;
button_img.title = button.opts.tooltip;
button.img = button_img;
//Button toggle. First click turns it on (and other buttons off), triggers bound events. Second click turns it off
GEvent.addDomListener(button.img, "click", function() {
if(button.img.getAttribute("src") === button.opts.img_up_url){
me.toggleButtons(opts.controlName);
opts.startDigitizing();
} else {
me.toggleButtons(opts.controlName);
opts.stopDigitizing();
}
});
me.buttons_[opts.controlName] = button;
me.stopDigitizingFuncs_[opts.controlName] = opts.stopDigitizing;
return button;
};
/**
* Turns on selected digitizer button, turns off the other buttons
* At the moment, name reference is passed rather than object, is this necessary?
* @param {String} button_name
*/
GeometryControls.prototype.toggleButtons = function(button_name){
var me = this;
//Calls with no name will turn everything off. Calls with a name will turn all off except the named button
for (var button in me.buttons_) {
me.buttons_[button].img.src = me.buttons_[button].opts.img_up_url;
}
if(button_name){
me.buttons_[button_name].img.src = me.buttons_[button_name].opts.img_down_url;
}
//turn off other digitizing listeners. Note: to avoid recursion, external calls to this function should always be made
//without parameters!!!
if (button_name) {
for (var func in me.stopDigitizingFuncs_) {
if (func != button_name) {
me.stopDigitizingFuncs_[func](false);
}
}
}
};
/**
* Adds a geometry control to this.controls, which are then added to the map
* Note: Would like to use the constructor name of control, so that name is not hard-coded
* but inheriting from GControl overrides the original constructor name :(
* @param {Object} control
* @see #initialize
*/
GeometryControls.prototype.addControl = function(control){
var me = this;
//thanks Ates Goral
//inheriting from GControl overrides original constructor so we use a final variable from the control(name)
/*var controlName = function getObjectClass(obj) {
if (obj && obj.constructor && obj.constructor.toString) {
var arr = obj.constructor.toString().match(/function\s*(\w+)/);
if (arr && arr.length == 2) {
return arr[1];
}
}
me.debug("Can't find constructor name of control");
return null;
}(control);*/
control.zuper = me;
me.controls[control.name] = control;
//TODO turn on auto-save?
};
/**
* Returns a custom tooltip function.
* Takes care of one time setup variables and functions and stores then in closure.
* @param {Object} tooltip_opts
* {Array} tooltip_opts.anchor The position offsets for tooltip anchor
* {String} tooltip_opts.cursor_on The url for a custom cursor
* {String} tooltip_opts.curson_off The url for a custom cursor
* {Object} titles
* {String} titles.start The text displayed at start of shape digitizing
* {String} titles.middle The text displayed at the middle of shape digitizing
* {String} titles.end The text displayed at the end of shape digitizing
* @return {Object} tooltipFunc
* {Object} me This
* {Object} tooltip_opts See tooltip_opts param
* {Object} tooltipHandler
* {DOM Object} tooltipContainer The div container for the tooltip
* {Function} on Turns the tooltip on
* {Function} off Turns the tooltip off
*/
GeometryControls.prototype.tooltipFactory = function(tooltip_opts){
var me = this, map = me.map;
//One time setup (memoization)
var tooltipContainer = document.createElement("div");
tooltipContainer.id = "tooltipContainer";
tooltipContainer.className = "emmc-tooltip";
map.getContainer().appendChild(tooltipContainer);
var calculatePosition = function(latlng,tooltipContainer) {
var offset=map.getCurrentMapType().getProjection().fromLatLngToPixel(map.getBounds().getSouthWest(),map.getZoom());
var point=map.getCurrentMapType().getProjection().fromLatLngToPixel(latlng,map.getZoom());
var anchor= new GPoint(tooltip_opts.anchor[0],tooltip_opts.anchor[1]);
var width = -12;
var position = new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(point.x - offset.x - anchor.x + width,- point.y + offset.y +anchor.y));
position.apply(tooltipContainer);
};
//TODO Chrome interprets hotspot on cursor incorrectly, so don't use custom cursor for now.
var customCursorFunc = function(){
if(tooltip_opts.cursor_on !== "" && !me.isChrome){
var dragObject = me.map.getDragObject();
return function(){
dragObject.setDraggableCursor(tooltip_opts.cursor_on);
};
} else {
return function(){};
}
}();
//Returns custom tooltip function/object
var tooltipFunc = {
me:me,
tooltip_opts:tooltip_opts,
tooltipHandler:null,
tooltipContainer:tooltipContainer,
on:function(message,callback){
var self_ = this;
tooltipContainer.innerHTML = message;
tooltipContainer.style.display = "block";
//TODO add listener for map drag (tooltip doesnt follow cursor during map drag)
//TODO execute less often (not every mousemove)?
this.tooltipHandler = GEvent.addListener(self_.me.map,"mousemove", function(latlng){ //really need local reference to me?
calculatePosition(latlng,tooltipContainer);
customCursorFunc();
if(typeof(callback)==="function"){
callback(latlng,tooltipContainer);
}
});
},
off:function(){
this.me.map.getDragObject().setDraggableCursor(this.tooltip_opts.cursor_off);
tooltipContainer.style.display = "none";
try{GEvent.removeListener(this.tooltipHandler);}catch(e){};
}
};
me.tooltip = function(tooltip_opts){
tooltipFunc.tooltip_opts = tooltip_opts;
return tooltipFunc;
};
return tooltipFunc;
};
/**
* Create, then store and show/hide an event-bound color picker
* @param opts
* @param {DOM Object} opts.target - element to receive the selected color
* @param {Function} opts. callback - callback function
*/
GeometryControls.prototype.showColorPicker = function(opts){
var me = this, row, cell;
//one time setup
//TODO could be moved to separate function
var colors = eval(me.infoWindowHtmlTemplates["colorTable"]);
var div = document.createElement("div");
document.getElementsByTagName("body")[0].appendChild(div);
div.innerHTML = me.infoWindowHtmlTemplates["colorTableHtml"];
var colorPicker = get$("emmc-menu-color");
var colorPickerTable = get$("emmc-color-table");
row = colorPickerTable.insertRow(0);
for(var i in colors){
if(i%7 === 0 && i!==0){ row = colorPickerTable.insertRow(i/7);}
cell = row.insertCell(i%7);
cell.innerHTML = '
';
}
//private scope variables for use by stored function
var target, color, callback;//DOM node
//hide the color picker. TODO : better way??
var colorPickerHandler = GEvent.addDomListener(colorPicker,"mouseover",function(){
var tempHandler = GEvent.addDomListener(me.map.getInfoWindow().getContentContainers()[0],"mouseover",function(){
colorPicker.style.display = "none";
GEvent.removeListener(tempHandler);
});
var tempHandler2 = GEvent.addListener(me.map,"infowindowclose",function(){
colorPicker.style.display = "none";
GEvent.removeListener(tempHandler2);
});
});
//attach listeners for color picker behaviors
var cells = colorPickerTable.getElementsByTagName("div");
for(var j=0; j= 1) {
for(var i=0;i)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'") +
"');}return p.join('');");
// Provide some basic currying to the user
return data ? fn( data ) : fn;
};
return this.parseMicroTmpl(str,data,node);
};
/**
* Binds elements shared between all geometry tools
* Assumes common id naming between different geometry infowindows
* Rich text editor might need to be some kind of pre-packaged javascript library
* @param geomInfo
* @param {Number} geomInfo.index The index of the geometry in the storage array
* @param {Array} geomInfo.storage The array used to store geometries
* @param {Function} geomInfo.geometryStyleFunc The function which will bind behaviors for the style pane in the infowindow
* @param {Function} geomInfo.undoStyling A function to undo unstored style changes
* @param {Function} geomInfo.commitStyling A function to commit any unstored style changes
*/
GeometryControls.prototype.bindInfoWindow = function(geomInfo){
var me = this, map = me.map, index = geomInfo.index, cssId = me.Options.cssId;
var record = geomInfo.storage[index];
var geometry = record.geometry;
var title = record.title;
var description = record.description;
//store references to id's that can be called multiple times
var geomStyleLink = get$("msiwsi");
var geomStyleDiv = get$(cssId + "-style");
var titleInput = get$(cssId + "-title");
var descriptionInput = get$(cssId + "-description");
//flag - indicates whether the styleInfoWindow window has already been bound
var isStyleInfoWindowBound = false;
//bind geometry style links and "back" link, using function supplied in parameters
GEvent.addDomListener(geomStyleLink,"click", function(){
geomStyleDiv.style.display = "block";
if(isStyleInfoWindowBound === false){
//call controls's custom styling functions, and update the flag
geomInfo.geometryStyleFunc();
isStyleInfoWindowBound = true;
}
});
GEvent.addDomListener(get$("emmc-geom-style-back"),"click",function(){
geomStyleDiv.style.display = "none";
});
//Update & Bind Text Entry
titleInput.value = (title[1]) ? ((title[0] === title[1]) ? title[0] : title[1]) : ""; //return the saved data or most recent edit
descriptionInput.value = (description[1]) ? ((description[0] === description[1]) ? description[0] : description[1]) : "";
GEvent.addDomListener(titleInput,"change", function(){
title[1] = titleInput.value;
});
GEvent.addDomListener(descriptionInput,"change", function(){
description[1] = descriptionInput.value;
});
//onchange doesnt fire when infowindow is closed by clicking on the map, so add a listener for that too
//listener has to be added to map, because polys dont have the event, so make it garbage collect itself
var windowOnCloseHandler = GEvent.addListener(map,"infowindowbeforeclose",function(){
title[1] = titleInput.value;
description[1] = descriptionInput.value;
GEvent.removeListener(windowOnCloseHandler);
});
//Bind Delete/Cancel/OK buttons
GEvent.addDomListener(get$(cssId + "-delete"),"click",function(){
if(confirm("Are you sure you want to delete this?")){
map.removeOverlay(geometry);
geomInfo.storage[index] = null;
map.closeInfoWindow();
}
});
GEvent.addDomListener(get$(cssId + "-cancel"),"click", function(){
geomInfo.undoStyling();
title[1] = title[0];
description[1] = description[0];
GEvent.removeListener(windowOnCloseHandler); //dont save on cancel
map.closeInfoWindow();
});
GEvent.addDomListener(get$(cssId + "-ok"),"click",function(){
//update permanent values
title[0] = title[1];
description[0] = description[1];
geomInfo.commitStyling();
map.closeInfoWindow();
});
//TODO callbacks? additional user provided methods?
};
/**
* Data Services
* @param {Object} opts
* @param {String} type Json or kml
* @param {String} url Url of the resource
*/
GeometryControls.prototype.loadData = function(opts){
var me = this;
GDownloadUrl(opts.url, function(data, responseCode){
(opts.type === "kml") ? me.handleKmlDataResponse_(data, responseCode) : me.handleJsonDataResponse_(data, responseCode);
});
};
/**
* EGeoXml Kml Processing (modified)
* http://econym.googlepages.com/egeoxml.htm (Thanks Mike!)
* TODO This code could be optimized, should I fix?
* @param {XMLDocument} data - data from #loadData
* @param {String} responseCode - response code from ajax GDownloadUrl
*/
GeometryControls.prototype.handleKmlDataResponse_ = function(data, responseCode){
var me = this;
//Helper functions
var EGeoXml = {
value:function(e){
a = GXml.value(e);
a = a.replace(/^\s*/, "");
a = a.replace(/\s*$/, "");
return a;
},
styles:{}
}
//called if there are errors in kml parsing
function error(e) {
me.debug("Looks like you provided an invalid URL or parameters or invalid xml. The URL is ____ ."+
"The error is:"+e+" at line "+e.lineNumber+" in file "+e.fileName); //TODO
}
if (responseCode == 200) {
try{
var xmlDoc = GXml.parse(data);
// Read through the Styles
var styles = xmlDoc.documentElement.getElementsByTagName("Style");
for (var i = 0; i < styles.length; i++) {
var styleID = styles[i].getAttribute("id");
//is it a marker style? (the following logic corresponds to predefined icons in the mymaps_html_template)
//TODO move these to individual overridable functions?
var icons = styles[i].getElementsByTagName("Icon");
if (icons.length > 0) {
var href=EGeoXml.value(icons[0].getElementsByTagName("href")[0]);
if (!!href) {
var icon = {name:styleID};
//get names of defined icons
var markerIcons = {};
for(var j in me.infoWindowHtmlTemplates["markerIcons"]){
markerIcons[me.infoWindowHtmlTemplates["markerIcons"][j].name] = me.infoWindowHtmlTemplates["markerIcons"][j];
}
//can't use href[index] in ie :(
switch(true){
case(href.indexOf("kml") > -1):
icon.y = parseInt(href.charAt(href.indexOf("pal")+3))+1; //TODO to match with MarkerControl.checkIconStatus
icon.x = parseInt(href.substring(href.indexOf("icon")+4,href.indexOf(".png")));
break;
case(href.indexOf("dot") > -1):
icon.y = 0;
if (markerIcons["dot"]) {
var images = markerIcons["dot"].images;
var image = href.split("/").pop();
for (var k in images) {
if (image === images[k]) {
icon.x = k;
}
}
} else {
me.debug("Cannot Load Kml - There is no icon defined for markers with images like *-dot.png");
}
break;
}
//TODO add rest of markers or improve marker detection
/*if (me.opts.printgif) {
var bits = href.split("/");
var gif = bits[bits.length-1];
gif = me.opts.printgifpath + gif.replace(/.png/i,".gif");
me.styles["#"+styleID].printImage = gif;
me.styles["#"+styleID].mozPrintImage = gif;
}*/
}
EGeoXml.styles["#"+icon.name] = icon;
}
// is it a LineStyle ?
var linestyles=styles[i].getElementsByTagName("LineStyle");
if (linestyles.length > 0) {
var width = parseInt(GXml.value(linestyles[0].getElementsByTagName("width")[0]));
if (width < 1) {width = 5;}
var color = EGeoXml.value(linestyles[0].getElementsByTagName("color")[0]);
var aa = color.substr(0,2);
var bb = color.substr(2,2);
var gg = color.substr(4,2);
var rr = color.substr(6,2);
color = "#" + rr + gg + bb;
var opacity = parseInt(aa,16)/256;
if (!EGeoXml.styles["#"+styleID]) {
EGeoXml.styles["#"+styleID] = {};
}
EGeoXml.styles["#"+styleID].color=color;
EGeoXml.styles["#"+styleID].width=width;
EGeoXml.styles["#"+styleID].opacity=opacity;
}
// is it a PolyStyle ?
var polystyles=styles[i].getElementsByTagName("PolyStyle");
if (polystyles.length > 0) {
var fill = parseInt(GXml.value(polystyles[0].getElementsByTagName("fill")[0]));
var outline = parseInt(GXml.value(polystyles[0].getElementsByTagName("outline")[0]));
var color = EGeoXml.value(polystyles[0].getElementsByTagName("color")[0]);
if (polystyles[0].getElementsByTagName("fill").length === 0) {fill = 1;}
if (polystyles[0].getElementsByTagName("outline").length === 0) {outline = 1;}
var aa = color.substr(0,2);
var bb = color.substr(2,2);
var gg = color.substr(4,2);
var rr = color.substr(6,2);
color = "#" + rr + gg + bb;
var opacity = Math.round((parseInt(aa,16)/256)*100)/100; //round to 2 decimals
if (!EGeoXml.styles["#"+styleID]) {
EGeoXml.styles["#"+styleID] = {};
}
EGeoXml.styles["#"+styleID].fillcolor=color;
EGeoXml.styles["#"+styleID].fillopacity=opacity;
if (!fill) {
EGeoXml.styles["#" + styleID].fillopacity = 0;
}
if (!outline) {
EGeoXml.styles["#" + styleID].opacity = 0;
}
}
}
// Read through the Placemarks
var placemarks = xmlDoc.documentElement.getElementsByTagName("Placemark");
for (var i = 0; i < placemarks.length; i++) {
var name = EGeoXml.value(placemarks[i].getElementsByTagName("name")[0]);
var desc = EGeoXml.value(placemarks[i].getElementsByTagName("description")[0]);
if (desc.match(/^http:\/\//i)) {
desc = '' + desc + '';
}
if (desc.match(/^https:\/\//i)) {
desc = '' + desc + '';
}
var style = EGeoXml.styles[EGeoXml.value(placemarks[i].getElementsByTagName("styleUrl")[0])] || {};
var coords=GXml.value(placemarks[i].getElementsByTagName("coordinates")[0]); //TODO what about inner boundaries?
coords=coords.replace(/\s+/g," "); // tidy the whitespace
coords=coords.replace(/^ /,""); // remove possible leading whitespace
coords=coords.replace(/ $/,""); // remove possible trailing whitespace
coords=coords.replace(/, /,","); // tidy the commas
var path = coords.split(" ");
// Is this a polyline/polygon?
if (path.length > 1) {
// Build the list of points
var points = [];
//var pbounds = new GLatLngBounds(); //TODO what does this do?
for (var p=0; p'
+ ''
+ me.side_bar_html
+ '';
}
//GEvent.trigger(me,"parsed");*/
//}
} catch(e){
error(e);
}
} else {
error();
}
};
/**
* Create geometries from JSON
* @param {Object} data
* @param {Integer} responseCode
*/
GeometryControls.prototype.handleJsonDataResponse_ = function(data, responseCode){
var me = this;
if (responseCode == 200) {
var json_data = eval('(' + data + ')');
json_data = json_data[0];
if (json_data.status != 'success') {
me.debug("The JSON was invalid");
return;
}
//TODO why is operation here?
switch (json_data.operation) {
case 'get':
var geometries = json_data.result.geometries;
for (var i=0; i < geometries.records.length; i++) {
var record = geometries.records[i];
if (record.type === 'point') {
var geometry = me.createGeometry_(record);
me.bounds.extend(geometry.getLatLng());
} else if (record.type === 'line' || record.type == 'poly') {
var latlng, latlngArray = [];
record.coordinates = function(){
for(var i in record.coordinates){
latlng = new GLatLng(record.coordinates[i].lat,record.coordinates[i].lng);
latlngArray[i] = latlng;
} return latlngArray;
}();
var geometry = me.createGeometry_(record);
me.bounds.extend(geometry.getBounds().getCenter()); //need to extend with all points?
}
}
me.zoomToBounds();
}
} else {
me.debug("Looks like you provided an invalid URL or parameters. The URL is ___"); //TODO
}
};
/**
* Set map center and zoom to a GBounds
* @param {Object} record - see #createGeometry_
*/
GeometryControls.prototype.zoomToBounds = function(record){
var me = this, bounds = me.bounds;
if (!bounds.isEmpty()) {
me.map.setCenter(bounds.getCenter());
me.map.setZoom(me.map.getBoundsZoomLevel(bounds));
}
};
/**
* Delegate object creation to appropriate geometry control
* TODO - If all controls come in with a standardized property (point,line,poly,etc),
* then you could replace the switch with a simple lookup, and a generic call to a loading method
* @param {Object} record
* @param {String} type The type of geometry
* @param {Object} coordinates An array of objects {lat,lng}
* @param {String} title The text used for geometry infowindow title
* @param {String} description The text used for geometry infowindow description
* @param {Object} style The full style definition for the geometry
*/
GeometryControls.prototype.createGeometry_ = function(record){
var me = this;
try {
switch (record.type) {
case "point":
return me.controls["markerControl"].loadMarkers(record);
case "line":
return me.controls["polylineControl"].loadPolylines(record);
case "poly":
return me.controls["polygonControl"].loadPolygons(record);
}
}
catch (e) {
me.debug("A geometry Control has not been added for the geometry type you are trying to load or there is an error." +
"Your error is: " + e + " at line " + e.lineNumber + " in file " + e.fileName);
}
};
/**
* Add aspects that listen for "Ok" button clicks, triggering an upload to the db
* TODO - need explicit extra variable (autoSaveListener) for passing references?
*/
GeometryControls.prototype.addAutoSaveAspect = function(){
var me = this;
me.aop.addBefore(me, 'bindInfoWindow', function(args){
var geomInfo = args[0];
//expose the function by passing reference to autoSaveListener variable
me.autoSaveListener = geomInfo.commitStyling;
geomInfo.commitStyling = function(){
me.autoSaveListener();
};
//attach the listener
me.aop.addAfter(me, 'autoSaveListener', function(){
if(me.Options.autoSave){
me.saveData({
allData:false,
geomInfo:geomInfo
});
}
});
return args;
});
};
/**
* Post data for storage to a db. Options to send all information or just one object?
* @see #addAutoSaveAspect
* @param {Object} opts
* @param {Object} geomInfo - @see #bindInfoWindow
*/
GeometryControls.prototype.saveData = function(opts){
var me = this;
if(opts.allData === true){
//me.saveAllData();
} else {
//construct a json data record
var geomInfo = opts.geomInfo, index = opts.geomInfo.index;
var record = geomInfo.storage[index];
var recordJSON = {};
recordJSON.type = record.type;
recordJSON.coordinates = [];
//determine geometry type, and copy geometry appropriately
if(record.type === "point"){
recordJSON.coordinates.push({lat:record.geometry.getLatLng().lat(),lng:record.geometry.getLatLng().lng()});
} else {
var vertex;
for(var i=0;i "process_", etc.)
*/
GeometryControls.prototype.aop = {
addBefore: function(obj, filter, before) {
var link = function(orig) {
return function() {
return orig.apply(this, before(arguments, orig, this));
};
};
this.process_(obj, filter, link);
},
addAfter: function(obj, filter, after) {
var link = function(orig) {
return function() {
return after(orig.apply(this, arguments), arguments, orig, this);
};
};
this.process_(obj, filter, link);
},
addAround: function(obj, filter, around) {
var link = function(orig) {
return function() {
return around(arguments, orig, this);
};
};
this.process_(obj, filter, link);
},
process_: function(obj, filter, link) {
var check;
if (filter.exec) {
check = function(str) { return filter.exec(str); };
} else if (filter.call) {
check = function(str) { return filter.call(this, str); };
}
if (check) {
for (var member in obj) {
if (check(member)) {
this.attach_(obj, member, link);
}
}
} else {
this.attach_(obj, filter, link);
}
},
attach_: function(obj, member, link) {
var orig = obj[member];
obj[member] = link(orig);
}
};