123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- // Auto-load scripts
- //
- // specify which map providers to load by using
- // <script src="mxn.js?(provider1,provider2,[module1,module2])" ...
- // in your HTML
- //
- // for each provider mxn.provider.module.js and mxn.module.js will be loaded
- // module 'core' is always loaded
- //
- // NOTE: if you call without providers
- // <script src="mxn.js" ...
- // no scripts will be loaded at all and it is then up to you to load the scripts independently
- (function() {
- var providers = null;
- var modules = 'core';
- var scriptBase;
- var scripts = document.getElementsByTagName('script');
- // Determine which scripts we need to load
- for (var i = 0; i < scripts.length; i++) {
- var match = scripts[i].src.replace(/%20/g , '').match(/^(.*?)mxn\.js(\?\(\[?(.*?)\]?\))?$/);
- if (match != null) {
- scriptBase = match[1];
- if (match[3]) {
- var settings = match[3].split(',[');
- providers = settings[0].replace(']' , '');
- if (settings[1]) modules += ',' + settings[1];
- }
- break;
- }
- }
-
- if (providers == null || providers == 'none') return; // Bail out if no auto-load has been found
- providers = providers.replace(/ /g, '').split(',');
- modules = modules.replace(/ /g, '').split(',');
- // Actually load the scripts
- for (i = 0; i < modules.length; i++) {
- document.write("<script type='text/javascript' src='" + scriptBase + 'mxn.' + modules[i] + '.js' + "'></script>");
- for (var j = 0; j < providers.length; j++) document.write("<script type='text/javascript' src='" + scriptBase + 'mxn.' + providers[j] + '.' + modules[i] + '.js' + "'></script>");
- }
- })();
- (function(){
- // holds all our implementing functions
- var apis = {};
- // Our special private methods
- /**
- * Calls the API specific implementation of a particular method.
- * @private
- */
- var invoke = function(sApiId, sObjName, sFnName, oScope, args){
- if(!hasImplementation(sApiId, sObjName, sFnName)) {
- throw 'Method ' + sFnName + ' of object ' + sObjName + ' is not supported by API ' + sApiId + '. Are you missing a script tag?';
- }
- return apis[sApiId][sObjName][sFnName].apply(oScope, args);
- };
-
- /**
- * Determines whether the specified API provides an implementation for the
- * specified object and function name.
- * @private
- */
- var hasImplementation = function(sApiId, sObjName, sFnName){
- if(typeof(apis[sApiId]) == 'undefined') {
- throw 'API ' + sApiId + ' not loaded. Are you missing a script tag?';
- }
- if(typeof(apis[sApiId][sObjName]) == 'undefined') {
- throw 'Object definition ' + sObjName + ' in API ' + sApiId + ' not loaded. Are you missing a script tag?';
- }
- return typeof(apis[sApiId][sObjName][sFnName]) == 'function';
- };
- /**
- * @name mxn
- * @namespace
- */
- var mxn = window.mxn = /** @lends mxn */ {
-
- /**
- * Registers a set of provider specific implementation functions.
- * @function
- * @param {String} sApiId The API ID to register implementing functions for.
- * @param {Object} oApiImpl An object containing the API implementation.
- */
- register: function(sApiId, oApiImpl){
- if(!apis.hasOwnProperty(sApiId)){
- apis[sApiId] = {};
- }
- mxn.util.merge(apis[sApiId], oApiImpl);
- },
-
- /**
- * Adds a list of named proxy methods to the prototype of a
- * specified constructor function.
- * @function
- * @param {Function} func Constructor function to add methods to
- * @param {Array} aryMethods Array of method names to create
- * @param {Boolean} bWithApiArg Optional. Whether the proxy methods will use an API argument
- */
- addProxyMethods: function(func, aryMethods, bWithApiArg){
- for(var i = 0; i < aryMethods.length; i++) {
- var sMethodName = aryMethods[i];
- if(bWithApiArg){
- func.prototype[sMethodName] = new Function('return this.invoker.go(\'' + sMethodName + '\', arguments, { overrideApi: true } );');
- }
- else {
- func.prototype[sMethodName] = new Function('return this.invoker.go(\'' + sMethodName + '\', arguments);');
- }
- }
- },
-
- /*
- checkLoad: function(funcDetails){
- if(this.loaded[this.api] === false) {
- var scope = this;
- this.onload[this.api].push( function() { funcDetails.callee.apply(scope, funcDetails); } );
- return true;
- }
- return false;
- },
- */
-
- /**
- * Bulk add some named events to an object.
- * @function
- * @param {Object} oEvtSrc The event source object.
- * @param {String[]} aEvtNames Event names to add.
- */
- addEvents: function(oEvtSrc, aEvtNames){
- for(var i = 0; i < aEvtNames.length; i++){
- var sEvtName = aEvtNames[i];
- if(sEvtName in oEvtSrc){
- throw 'Event or method ' + sEvtName + ' already declared.';
- }
- oEvtSrc[sEvtName] = new mxn.Event(sEvtName, oEvtSrc);
- }
- }
-
- };
- /**
- * Instantiates a new Event
- * @constructor
- * @param {String} sEvtName The name of the event.
- * @param {Object} oEvtSource The source object of the event.
- */
- mxn.Event = function(sEvtName, oEvtSource){
- var handlers = [];
- if(!sEvtName){
- throw 'Event name must be provided';
- }
- /**
- * Add a handler to the Event.
- * @param {Function} fn The handler function.
- * @param {Object} ctx The context of the handler function.
- */
- this.addHandler = function(fn, ctx){
- handlers.push({context: ctx, handler: fn});
- };
- /**
- * Remove a handler from the Event.
- * @param {Function} fn The handler function.
- * @param {Object} ctx The context of the handler function.
- */
- this.removeHandler = function(fn, ctx){
- for(var i = 0; i < handlers.length; i++){
- if(handlers[i].handler == fn && handlers[i].context == ctx){
- handlers.splice(i, 1);
- }
- }
- };
- /**
- * Remove all handlers from the Event.
- */
- this.removeAllHandlers = function(){
- handlers = [];
- };
- /**
- * Fires the Event.
- * @param {Object} oEvtArgs Event arguments object to be passed to the handlers.
- */
- this.fire = function(oEvtArgs){
- var args = [sEvtName, oEvtSource, oEvtArgs];
- for(var i = 0; i < handlers.length; i++){
- handlers[i].handler.apply(handlers[i].context, args);
- }
- };
- };
- /**
- * Creates a new Invoker, a class which helps with on-the-fly
- * invocation of the correct API methods.
- * @constructor
- * @param {Object} aobj The core object whose methods will make cals to go()
- * @param {String} asClassName The name of the Mapstraction class to be invoked, normally the same name as aobj's constructor function
- * @param {Function} afnApiIdGetter The function on object aobj which will return the active API ID
- */
- mxn.Invoker = function(aobj, asClassName, afnApiIdGetter){
- var obj = aobj;
- var sClassName = asClassName;
- var fnApiIdGetter = afnApiIdGetter;
- var defOpts = {
- overrideApi: false, // {Boolean} API ID is overridden by value in first argument
- context: null, // {Object} Local vars can be passed from the body of the method to the API method within this object
- fallback: null // {Function} If an API implementation doesn't exist this function is run instead
- };
-
- /**
- * Invoke the API implementation of a specific method.
- * @param {String} sMethodName The method name to invoke
- * @param {Array} args Arguments to pass on
- * @param {Object} oOptions Optional. Extra options for invocation
- * @param {Boolean} oOptions.overrideApi When true the first argument is used as the API ID.
- * @param {Object} oOptions.context A context object for passing extra information on to the provider implementation.
- * @param {Function} oOptions.fallback A fallback function to run if the provider implementation is missing.
- */
- this.go = function(sMethodName, args, oOptions){
-
- if(typeof(oOptions) == 'undefined'){
- oOptions = defOpts;
- }
-
- var sApiId = oOptions.overrideApi ? args[0] : fnApiIdGetter.apply(obj);
-
- if(typeof(sApiId) != 'string'){
- throw 'API ID not available.';
- }
-
- if(typeof(oOptions.context) != 'undefined' && oOptions.context !== null){
- // make sure args is an array
- args = Array.prototype.slice.apply(args);
- args.push(oOptions.context);
- }
-
- if(typeof(oOptions.fallback) == 'function' && !hasImplementation(sApiId, sClassName, sMethodName)){
- // we've got no implementation but have got a fallback function
- return oOptions.fallback.apply(obj, args);
- }
- else {
- return invoke(sApiId, sClassName, sMethodName, obj, args);
- }
-
- };
-
- };
- /**
- * @namespace
- */
- mxn.util = {
-
- /**
- * Merges properties of one object into another recursively.
- * @param {Object} oRecv The object receiveing properties
- * @param {Object} oGive The object donating properties
- */
- merge: function(oRecv, oGive){
- for (var sPropName in oGive){
- if (oGive.hasOwnProperty(sPropName)) {
- if(!oRecv.hasOwnProperty(sPropName)){
- oRecv[sPropName] = oGive[sPropName];
- }
- else {
- mxn.util.merge(oRecv[sPropName], oGive[sPropName]);
- }
- }
- }
- },
-
- /**
- * $m, the dollar function, elegantising getElementById()
- * @return An HTML element or array of HTML elements
- */
- $m: function() {
- var elements = [];
- for (var i = 0; i < arguments.length; i++) {
- var element = arguments[i];
- if (typeof(element) == 'string') {
- element = document.getElementById(element);
- }
- if (arguments.length == 1) {
- return element;
- }
- elements.push(element);
- }
- return elements;
- },
- /**
- * loadScript is a JSON data fetcher
- * @param {String} src URL to JSON file
- * @param {Function} callback Callback function
- */
- loadScript: function(src, callback) {
- var script = document.createElement('script');
- script.type = 'text/javascript';
- script.src = src;
- if (callback) {
- if(script.addEventListener){
- script.addEventListener('load', callback, true);
- }
- else if(script.attachEvent){
- var done = false;
- script.attachEvent("onreadystatechange",function(){
- if ( !done && document.readyState === "complete" ) {
- done = true;
- callback();
- }
- });
- }
- }
- var h = document.getElementsByTagName('head')[0];
- h.appendChild( script );
- return;
- },
- /**
- *
- * @param {Object} point
- * @param {Object} level
- */
- convertLatLonXY_Yahoo: function(point, level) { //Mercator
- var size = 1 << (26 - level);
- var pixel_per_degree = size / 360.0;
- var pixel_per_radian = size / (2 * Math.PI);
- var origin = new YCoordPoint(size / 2 , size / 2);
- var answer = new YCoordPoint();
- answer.x = Math.floor(origin.x + point.lon * pixel_per_degree);
- var sin = Math.sin(point.lat * Math.PI / 180.0);
- answer.y = Math.floor(origin.y + 0.5 * Math.log((1 + sin) / (1 - sin)) * -pixel_per_radian);
- return answer;
- },
- /**
- * Load a stylesheet from a remote file.
- * @param {String} href URL to the CSS file
- */
- loadStyle: function(href) {
- var link = document.createElement('link');
- link.type = 'text/css';
- link.rel = 'stylesheet';
- link.href = href;
- document.getElementsByTagName('head')[0].appendChild(link);
- return;
- },
- /**
- * getStyle provides cross-browser access to css
- * @param {Object} el HTML Element
- * @param {String} prop Style property name
- */
- getStyle: function(el, prop) {
- var y;
- if (el.currentStyle) {
- y = el.currentStyle[prop];
- }
- else if (window.getComputedStyle) {
- y = window.getComputedStyle( el, '').getPropertyValue(prop);
- }
- return y;
- },
- /**
- * Convert longitude to metres
- * http://www.uwgb.edu/dutchs/UsefulData/UTMFormulas.HTM
- * "A degree of longitude at the equator is 111.2km... For other latitudes,
- * multiply by cos(lat)"
- * assumes the earth is a sphere but good enough for our purposes
- * @param {Float} lon
- * @param {Float} lat
- */
- lonToMetres: function(lon, lat) {
- return lon * (111200 * Math.cos(lat * (Math.PI / 180)));
- },
- /**
- * Convert metres to longitude
- * @param {Object} m
- * @param {Object} lat
- */
- metresToLon: function(m, lat) {
- return m / (111200 * Math.cos(lat * (Math.PI / 180)));
- },
- /**
- * Convert kilometres to miles
- * @param {Float} km
- * @returns {Float} miles
- */
- KMToMiles: function(km) {
- return km / 1.609344;
- },
- /**
- * Convert miles to kilometres
- * @param {Float} miles
- * @returns {Float} km
- */
- milesToKM: function(miles) {
- return miles * 1.609344;
- },
- // stuff to convert google zoom levels to/from degrees
- // assumes zoom 0 = 256 pixels = 360 degrees
- // zoom 1 = 256 pixels = 180 degrees
- // etc.
- /**
- *
- * @param {Object} pixels
- * @param {Object} zoom
- */
- getDegreesFromGoogleZoomLevel: function(pixels, zoom) {
- return (360 * pixels) / (Math.pow(2, zoom + 8));
- },
- /**
- *
- * @param {Object} pixels
- * @param {Object} degrees
- */
- getGoogleZoomLevelFromDegrees: function(pixels, degrees) {
- return mxn.util.logN((360 * pixels) / degrees, 2) - 8;
- },
- /**
- *
- * @param {Object} number
- * @param {Object} base
- */
- logN: function(number, base) {
- return Math.log(number) / Math.log(base);
- },
-
- /**
- * Returns array of loaded provider apis
- * @returns {Array} providers
- */
- getAvailableProviders : function () {
- var providers = [];
- for (var propertyName in apis){
- if (apis.hasOwnProperty(propertyName)) {
- providers.push(propertyName);
- }
- }
- return providers;
- }
-
- };
- /**
- * Class for converting between HTML and RGB integer color formats.
- * Accepts either a HTML color string argument or three integers for R, G and B.
- * @constructor
- */
- mxn.util.Color = function() {
- if(arguments.length == 3) {
- this.red = arguments[0];
- this.green = arguments[1];
- this.blue = arguments[2];
- }
- else if(arguments.length == 1) {
- this.setHexColor(arguments[0]);
- }
- };
- mxn.util.Color.prototype.reHex = /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
- /**
- * Set the color from the supplied HTML hex string.
- * @param {String} strHexColor A HTML hex color string e.g. '#00FF88'.
- */
- mxn.util.Color.prototype.setHexColor = function(strHexColor) {
- var match = strHexColor.match(this.reHex);
- if(match) {
- strHexColor = match[1];
- }
- else {
- throw 'Invalid HEX color format, expected #000, 000, #000000 or 000000';
- }
- if(strHexColor.length == 3) {
- strHexColor = strHexColor.replace(/\w/g, function(str){return str.concat(str);});
- }
- this.red = parseInt(strHexColor.substr(0,2), 16);
- this.green = parseInt(strHexColor.substr(2,2), 16);
- this.blue = parseInt(strHexColor.substr(4,2), 16);
- };
- /**
- * Retrieve the color value as an HTML hex string.
- * @returns {String} Format '00FF88' - note no preceding #.
- */
- mxn.util.Color.prototype.getHexColor = function() {
- var vals = [this.red.toString(16), this.green.toString(16), this.blue.toString(16)];
- for(var i = 0; i < vals.length; i++) {
- vals[i] = (vals[i].length == 1) ? '0' + vals[i] : vals[i];
- vals[i] = vals[i].toUpperCase();
- }
- return vals.join('');
- };
- })();
|