Custom modules
We'll add our own controls to the map; they will change the zoom level and the map type. For this, we will take advantage of the module structure of Bing Maps. The modules are just JavaScript functions that are loaded dynamically by Bing Maps AJAX Control. Apart from better code organization, the modules provide on-demand downloads, reducing the initial script load.
On the flip side, this means that the JavaScript files will be loaded in multiple HTTP requests. If you prefer your scripts merged into a single HTTP request, you can use tools that merge and minimize website assets, such as Asset Bundler in ASP.NET, Sprockets in Ruby, gzipit in PHP, and so forth. In this case, make sure that the custom modules are instantiated after the map is loaded (more information about the Bing Maps modules can be found at http://msdn.microsoft.com/en-us/library/hh125836.aspx).
We'll call our module LearningTheme
, and place it under a learningTheme
folder. Let's write our module within a self-calling, immediately invoked function in the learningTheme.js
file:
(function () { var lbm = window.lbm = {}; lbm.LearningTheme = function(map) { this._map = map; }; Microsoft.Maps.moduleLoaded('lbm.LearningTheme'); })(this);
We have placed our LearningTheme
module under the namespace lbm
, just as we did with the CSS styles. This way, we expose only one global variable, lbm
.
The module is just a normal JavaScript function with one argument, that is, map
. This is a reference to the Bing Maps map we instantiated earlier, which we'll store into a private variable named as _map
.
Note
We'll use the convention of starting the variable names with an underscore in this book, to indicate that they should be considered as private variables, and not be called outside our module. There are ways to achieve better access restriction for objects in JavaScript, but they are outside the scope of this book.
After the module body, we add the following line, which turns our function into a Bing Maps module:
Microsoft.Maps.moduleLoaded('lbm.LearningTheme');
We then load the module in our window.onload
function by first registering it:
Microsoft.Maps.registerModule('lbm.LearningTheme', 'learningTheme/learningTheme.js'); Microsoft.Maps.loadModule('lbm.LearningTheme', { callback: function() { var learningTheme = new lbm.LearningTheme(map); } });
The registration function accepts two arguments: the name of the module, and its relative or absolute (or remote) location. We then call Microsoft.Maps.loadModule
, which again needs the module name, and an options object. With the latter argument, we can specify what should happen when the module is loaded. In this case, we instantiate it by passing a reference to the map we created earlier.
Let's add the two buttons that will control the zoom level, one with a minus (-) sign that will decrease the zoom level, and one with a plus (+) sign that will increase it. We'll also add a third button that will toggle the map type between road and aerial.
A function within the module's prototype (a private instance method we can call later with the this
keyword) achieves the following:
lbm.LearningTheme.prototype = { _addHeader: function() { var header = document.createElement('div'); header.className = 'lbm-map-header'; header.innerHTML = [ '<button class="lbm-btn" id="lbm-map-btn-zoomin">-</button>', '<button class="lbm-btn" id="lbm-map-btn-zoomout">+</button>', '<button class="lbm-btn" id="lbm-map-btn-maptype">Aerial</button>' ].join(''); this._map.getRootElement().appendChild(header); } };
First, we create a DOM div
element and give it a class name OF lbm-map-header
, so that we can style it later. The markup for the buttons is simple, shown as follows:
<button class="lbm-btn" id="lbm-map-btn-zoomin">-</button>
Again we specify a class (lbm-btn
) for styling and an ID (lbm-map-btn-zoomin
) for controlling its behavior. We place the buttons in an array that we later join with an empty string. This is a common technique for building HTML strings in a JavaScript code, BECAUSE it improves the readability of the code.
At the end, we append the markup to the map's root element. Bing Maps provide a nice utility method to get the latter, getRootElement
. (The list of methods, properties, and events of the map can be found at http://msdn.microsoft.com/en-us/library/gg427609.aspx).
We want the header to be added to the map when the module is loaded; therefore, we call the _addHandler
method in the module's constructor, which now looks like this:
lbm.LearningTheme = function(map) {
this._map = map;
this._addHeader();
};
If you view the index.html
file on the browser, you will not see the default Bing Maps dashboard, but you won't see our controls either. This is because we have not added our styles yet. Let's place them in a learningTheme.css
file inside the learningTheme
folder, as shown in the following code:
.lbm-map-header { position: absolute; top: 1em; left: 1em; width: 100%; color: #fff; z-index: 100; } .lbm-map-header .lbm-btn { font-size: 1em; border: 0; background: #F04C40; color: #fff; display: inline-block; text-decoration: none; width: 2.5em; height: 2em; line-height: 2em; padding: 0; margin: 0 1px 0 0; outline: 0; text-align: center; cursor: pointer; opacity: .8; border-radius: .2em; -webkit-border-radius: .2em; -moz-border-radius: .2em; } .lbm-map-header #lbm-map-btn-zoomin { border-top-right-radius: 0; -webkit-border-top-right-radius: 0; -moz-border-top-right-radius: 0; border-bottom-right-radius: 0; -webkit-border-bottom-right-radius: 0; -moz-border-bottom-right-radius: 0; } .lbm-map-header #lbm-map-btn-zoomout { border-top-left-radius: 0; -webkit-border-top-left-radius: 0; -moz-border-top-left-radius: 0; border-bottom-left-radius: 0; -webkit-border-bottom-left-radius: 0; -moz-border-bottom-left-radius: 0; } .lbm-map-header .lbm-btn:hover { background: #cf343e; } .lbm-map-header .lbm-btn:active { background: #cc1d28; } .lbm-map-header #lbm-map-btn-maptype { margin-left: 2em; width: auto; padding: 0 .8em; }
We make sure that the buttons' container has an absolute position
and a high z-index
value, so that it appears above the map.
Now, we need to add a link to the CSS file to our HTML document, and we'll do this when the module is loaded. This way, if the module is never loaded, then the stylesheet is not loaded either. Again, loading our CSS file on demand means another HTTP request, and if you prefer to have the styles merged into a single file, then you can use the same technique as for the JavaScript files, and skip the following step:
We'll add a link to our CSS file to the head of the page as shown in the following code snippet:
_addCss: function() { var link = document.createElement('link'); link.setAttribute('rel', 'stylesheet'); link.setAttribute('href', 'learningTheme /learningTheme.css'); var head = document.getElementsByTagName('head')[0]; head.appendChild(link); }
First, the preceding function creates a link
element, with a href
attribute pointing at our learningTheme.css
file. Then, it gets a reference to the head
of the document, and it appends to it the newly created element.
As with the buttons markup, we need the stylesheet to be loaded with the module, so we'll call this function in the module's constructor, as shown in the following code:
lbm.LearningTheme = function(map) {
this._map = map;
this._addCss();
this._addHeader();
};
If we open the index.html
file on a browser now, we can finally see our controls, as shown in the following screenshot: