/*jslint browser: true, confusion: true, sloppy: true, vars: true, nomen: false, plusplus: false, indent: 2 */ /*global window,google */ /** * @name MarkerClustererPlus for Google Maps V3 * @version 2.0.9 [February 20, 2012] * @author Gary Little * @fileoverview * The library creates and manages per-zoom-level clusters for large amounts of markers. *

* This is an enhanced V3 implementation of the * V2 MarkerClusterer by Xiaoxi Wu. It is based on the * V3 MarkerClusterer port by Luke Mahe. MarkerClustererPlus was created by Gary Little. *

* v2.0 release: MarkerClustererPlus v2.0 is backward compatible with MarkerClusterer v1.0. It * adds support for the ignoreHidden, title, printable, * batchSizeIE, and calculator properties as well as support for * four more events. It also allows greater control over the styling of the text that appears * on the cluster marker. The documentation has been significantly improved and the overall * code has been simplified and polished. Very large numbers of markers can now be managed * without causing Javascript timeout errors on Internet Explorer. Note that the name of the * clusterclick event has been deprecated. The new name is click, * so please change your application code now. */ /** * 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. */ /** * @name ClusterIconStyle * @class This class represents the object for values in the styles array passed * to the {@link MarkerClusterer} constructor. The element in this array that is used to * style the cluster icon is determined by calling the calculator function. * * @property {string} url The URL of the cluster icon image file. Required. * @property {number} height The height (in pixels) of the cluster icon. Required. * @property {number} width The width (in pixels) of the cluster icon. Required. * @property {Array} [anchor] The anchor position (in pixels) of the label text to be shown on * the cluster icon, relative to the top left corner of the icon. * The format is [yoffset, xoffset]. The yoffset must be positive * and less than height and the xoffset must be positive and less * than width. The default is to anchor the label text so that it is centered * on the icon. * @property {Array} [anchorIcon] The anchor position (in pixels) of the cluster icon. This is the * spot on the cluster icon that is to be aligned with the cluster position. The format is * [yoffset, xoffset] where yoffset increases as you go down and * xoffset increases to the right. The default anchor position is the center of the * cluster icon. * @property {string} [textColor="black"] The color of the label text shown on the * cluster icon. * @property {number} [textSize=11] The size (in pixels) of the label text shown on the * cluster icon. * @property {number} [textDecoration="none"] The value of the CSS text-decoration * property for the label text shown on the cluster icon. * @property {number} [fontWeight="bold"] The value of the CSS font-weight * property for the label text shown on the cluster icon. * @property {number} [fontStyle="normal"] The value of the CSS font-style * property for the label text shown on the cluster icon. * @property {number} [fontFamily="Arial,sans-serif"] The value of the CSS font-family * property for the label text shown on the cluster icon. * @property {string} [backgroundPosition="0 0"] The position of the cluster icon image * within the image defined by url. The format is "xpos ypos" * (the same format as for the CSS background-position property). You must set * this property appropriately when the image defined by url represents a sprite * containing multiple images. */ /** * @name ClusterIconInfo * @class This class is an object containing general information about a cluster icon. This is * the object that a calculator function returns. * * @property {string} text The text of the label to be shown on the cluster icon. * @property {number} index The index plus 1 of the element in the styles * array to be used to style the cluster icon. */ /** * A cluster icon. * * @constructor * @extends google.maps.OverlayView * @param {Cluster} cluster The cluster with which the icon is to be associated. * @param {Array} [styles] An array of {@link ClusterIconStyle} defining the cluster icons * to use for various cluster sizes. * @private */ function ClusterIcon(cluster, styles) { cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); this.cluster_ = cluster; this.styles_ = styles; this.center_ = null; this.div_ = null; this.sums_ = null; this.visible_ = false; this.setMap(cluster.getMap()); // Note: this causes onAdd to be called } /** * Adds the icon to the DOM. */ ClusterIcon.prototype.onAdd = function () { var cClusterIcon = this; var cMouseDownInCluster; var cDraggingMapByCluster; this.div_ = document.createElement("div"); if (this.visible_) { this.show(); } this.getPanes().overlayMouseTarget.appendChild(this.div_); // Fix for Issue 157 google.maps.event.addListener(this.getMap(), "bounds_changed", function () { cDraggingMapByCluster = cMouseDownInCluster; }); google.maps.event.addDomListener(this.div_, "mousedown", function () { cMouseDownInCluster = true; cDraggingMapByCluster = false; }); google.maps.event.addDomListener(this.div_, "click", function (e) { cMouseDownInCluster = false; if (!cDraggingMapByCluster) { var mz; var mc = cClusterIcon.cluster_.getMarkerClusterer(); /** * This event is fired when a cluster marker is clicked. * @name MarkerClusterer#click * @param {Cluster} c The cluster that was clicked. * @event */ google.maps.event.trigger(mc, "click", cClusterIcon.cluster_); google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name // The default click handler follows. Disable it by setting // the zoomOnClick property to false. if (mc.getZoomOnClick()) { // Zoom into the cluster. mz = mc.getMaxZoom(); mc.getMap().fitBounds(cClusterIcon.cluster_.getBounds()); // Don't zoom beyond the max zoom level if (mz !== null && (mc.getMap().getZoom() > mz)) { mc.getMap().setZoom(mz + 1); } } // Prevent event propagation to the map: e.cancelBubble = true; if (e.stopPropagation) { e.stopPropagation(); } } }); google.maps.event.addDomListener(this.div_, "mouseover", function () { var mc = cClusterIcon.cluster_.getMarkerClusterer(); /** * This event is fired when the mouse moves over a cluster marker. * @name MarkerClusterer#mouseover * @param {Cluster} c The cluster that the mouse moved over. * @event */ google.maps.event.trigger(mc, "mouseover", cClusterIcon.cluster_); }); google.maps.event.addDomListener(this.div_, "mouseout", function () { var mc = cClusterIcon.cluster_.getMarkerClusterer(); /** * This event is fired when the mouse moves out of a cluster marker. * @name MarkerClusterer#mouseout * @param {Cluster} c The cluster that the mouse moved out of. * @event */ google.maps.event.trigger(mc, "mouseout", cClusterIcon.cluster_); }); }; /** * Removes the icon from the DOM. */ ClusterIcon.prototype.onRemove = function () { if (this.div_ && this.div_.parentNode) { this.hide(); google.maps.event.clearInstanceListeners(this.div_); this.div_.parentNode.removeChild(this.div_); this.div_ = null; } }; /** * Draws the icon. */ ClusterIcon.prototype.draw = function () { if (this.visible_) { var pos = this.getPosFromLatLng_(this.center_); this.div_.style.top = pos.y + "px"; this.div_.style.left = pos.x + "px"; } }; /** * Hides the icon. */ ClusterIcon.prototype.hide = function () { if (this.div_) { this.div_.style.display = "none"; } this.visible_ = false; }; /** * Positions and shows the icon. */ ClusterIcon.prototype.show = function () { if (this.div_) { var pos = this.getPosFromLatLng_(this.center_); this.div_.style.cssText = this.createCss(pos); if (this.cluster_.printable_) { // (Would like to use "width: inherit;" below, but doesn't work with MSIE) this.div_.innerHTML = "

" + this.sums_.text + "
"; } else { this.div_.innerHTML = this.sums_.text; } this.div_.title = this.cluster_.getMarkerClusterer().getTitle(); this.div_.style.display = ""; } this.visible_ = true; }; /** * Sets the icon styles to the appropriate element in the styles array. * * @param {ClusterIconInfo} sums The icon label text and styles index. */ ClusterIcon.prototype.useStyle = function (sums) { this.sums_ = sums; var index = Math.max(0, sums.index - 1); index = Math.min(this.styles_.length - 1, index); var style = this.styles_[index]; this.url_ = style.url; this.height_ = style.height; this.width_ = style.width; this.anchor_ = style.anchor; this.anchorIcon_ = style.anchorIcon || [parseInt(this.height_ / 2, 10), parseInt(this.width_ / 2, 10)]; this.textColor_ = style.textColor || "black"; this.textSize_ = style.textSize || 11; this.textDecoration_ = style.textDecoration || "none"; this.fontWeight_ = style.fontWeight || "bold"; this.fontStyle_ = style.fontStyle || "normal"; this.fontFamily_ = style.fontFamily || "Arial,sans-serif"; this.backgroundPosition_ = style.backgroundPosition || "0 0"; }; /** * Sets the position at which to center the icon. * * @param {google.maps.LatLng} center The latlng to set as the center. */ ClusterIcon.prototype.setCenter = function (center) { this.center_ = center; }; /** * Creates the cssText style parameter based on the position of the icon. * * @param {google.maps.Point} pos The position of the icon. * @return {string} The CSS style text. */ ClusterIcon.prototype.createCss = function (pos) { var style = []; if (!this.cluster_.printable_) { style.push('background-image:url(' + this.url_ + ');'); style.push('background-position:' + this.backgroundPosition_ + ';'); } if (typeof this.anchor_ === 'object') { if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 && this.anchor_[0] < this.height_) { style.push('height:' + (this.height_ - this.anchor_[0]) + 'px; padding-top:' + this.anchor_[0] + 'px;'); } else { style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + 'px;'); } if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 && this.anchor_[1] < this.width_) { style.push('width:' + (this.width_ - this.anchor_[1]) + 'px; padding-left:' + this.anchor_[1] + 'px;'); } else { style.push('width:' + this.width_ + 'px; text-align:center;'); } } else { style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;'); } style.push('cursor:pointer; top:' + pos.y + 'px; left:' + pos.x + 'px; color:' + this.textColor_ + '; position:absolute; font-size:' + this.textSize_ + 'px; font-family:' + this.fontFamily_ + '; font-weight:' + this.fontWeight_ + '; font-style:' + this.fontStyle_ + '; text-decoration:' + this.textDecoration_ + ';'); return style.join(""); }; /** * Returns the position at which to place the DIV depending on the latlng. * * @param {google.maps.LatLng} latlng The position in latlng. * @return {google.maps.Point} The position in pixels. */ ClusterIcon.prototype.getPosFromLatLng_ = function (latlng) { var pos = this.getProjection().fromLatLngToDivPixel(latlng); pos.x -= this.anchorIcon_[1]; pos.y -= this.anchorIcon_[0]; return pos; }; /** * Creates a single cluster that manages a group of proximate markers. * Used internally, do not call this constructor directly. * @constructor * @param {MarkerClusterer} mc The MarkerClusterer object with which this * cluster is associated. */ function Cluster(mc) { this.markerClusterer_ = mc; this.map_ = mc.getMap(); this.gridSize_ = mc.getGridSize(); this.minClusterSize_ = mc.getMinimumClusterSize(); this.averageCenter_ = mc.getAverageCenter(); this.printable_ = mc.getPrintable(); this.markers_ = []; this.center_ = null; this.bounds_ = null; this.clusterIcon_ = new ClusterIcon(this, mc.getStyles()); } /** * Returns the number of markers managed by the cluster. You can call this from * a click, mouseover, or mouseout event handler * for the MarkerClusterer object. * * @return {number} The number of markers in the cluster. */ Cluster.prototype.getSize = function () { return this.markers_.length; }; /** * Returns the array of markers managed by the cluster. You can call this from * a click, mouseover, or mouseout event handler * for the MarkerClusterer object. * * @return {Array} The array of markers in the cluster. */ Cluster.prototype.getMarkers = function () { return this.markers_; }; /** * Returns the center of the cluster. You can call this from * a click, mouseover, or mouseout event handler * for the MarkerClusterer object. * * @return {google.maps.LatLng} The center of the cluster. */ Cluster.prototype.getCenter = function () { return this.center_; }; /** * Returns the map with which the cluster is associated. * * @return {google.maps.Map} The map. * @ignore */ Cluster.prototype.getMap = function () { return this.map_; }; /** * Returns the MarkerClusterer object with which the cluster is associated. * * @return {MarkerClusterer} The associated marker clusterer. * @ignore */ Cluster.prototype.getMarkerClusterer = function () { return this.markerClusterer_; }; /** * Returns the bounds of the cluster. * * @return {google.maps.LatLngBounds} the cluster bounds. * @ignore */ Cluster.prototype.getBounds = function () { var i; var bounds = new google.maps.LatLngBounds(this.center_, this.center_); var markers = this.getMarkers(); for (i = 0; i < markers.length; i++) { bounds.extend(markers[i].getPosition()); } return bounds; }; /** * Removes the cluster from the map. * * @ignore */ Cluster.prototype.remove = function () { this.clusterIcon_.setMap(null); this.markers_ = []; delete this.markers_; }; /** * Adds a marker to the cluster. * * @param {google.maps.Marker} marker The marker to be added. * @return {boolean} True if the marker was added. * @ignore */ Cluster.prototype.addMarker = function (marker) { var i; var mCount; var mz; if (this.isMarkerAlreadyAdded_(marker)) { return false; } if (!this.center_) { this.center_ = marker.getPosition(); this.calculateBounds_(); } else { if (this.averageCenter_) { var l = this.markers_.length + 1; var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l; var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l; this.center_ = new google.maps.LatLng(lat, lng); this.calculateBounds_(); } } marker.isAdded = true; this.markers_.push(marker); mCount = this.markers_.length; mz = this.markerClusterer_.getMaxZoom(); if (mz !== null && this.map_.getZoom() > mz) { // Zoomed in past max zoom, so show the marker. if (marker.getMap() !== this.map_) { marker.setMap(this.map_); } } else if (mCount < this.minClusterSize_) { // Min cluster size not reached so show the marker. if (marker.getMap() !== this.map_) { marker.setMap(this.map_); } } else if (mCount === this.minClusterSize_) { // Hide the markers that were showing. for (i = 0; i < mCount; i++) { this.markers_[i].setMap(null); } } else { marker.setMap(null); } this.updateIcon_(); return true; }; /** * Determines if a marker lies within the cluster's bounds. * * @param {google.maps.Marker} marker The marker to check. * @return {boolean} True if the marker lies in the bounds. * @ignore */ Cluster.prototype.isMarkerInClusterBounds = function (marker) { return this.bounds_.contains(marker.getPosition()); }; /** * Calculates the extended bounds of the cluster with the grid. */ Cluster.prototype.calculateBounds_ = function () { var bounds = new google.maps.LatLngBounds(this.center_, this.center_); this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); }; /** * Updates the cluster icon. */ Cluster.prototype.updateIcon_ = function () { var mCount = this.markers_.length; var mz = this.markerClusterer_.getMaxZoom(); if (mz !== null && this.map_.getZoom() > mz) { this.clusterIcon_.hide(); return; } if (mCount < this.minClusterSize_) { // Min cluster size not yet reached. this.clusterIcon_.hide(); return; } var numStyles = this.markerClusterer_.getStyles().length; var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); this.clusterIcon_.setCenter(this.center_); this.clusterIcon_.useStyle(sums); this.clusterIcon_.show(); }; /** * Determines if a marker has already been added to the cluster. * * @param {google.maps.Marker} marker The marker to check. * @return {boolean} True if the marker has already been added. */ Cluster.prototype.isMarkerAlreadyAdded_ = function (marker) { var i; if (this.markers_.indexOf) { return this.markers_.indexOf(marker) !== -1; } else { for (i = 0; i < this.markers_.length; i++) { if (marker === this.markers_[i]) { return true; } } } return false; }; /** * @name MarkerClustererOptions * @class This class represents the optional parameter passed to * the {@link MarkerClusterer} constructor. * @property {number} [gridSize=60] The grid size of a cluster in pixels. The grid is a square. * @property {number} [maxZoom=null] The maximum zoom level at which clustering is enabled or * null if clustering is to be enabled at all zoom levels. * @property {boolean} [zoomOnClick=true] Whether to zoom the map when a cluster marker is * clicked. You may want to set this to false if you have installed a handler * for the click event and it deals with zooming on its own. * @property {boolean} [averageCenter=false] Whether the position of a cluster marker should be * the average position of all markers in the cluster. If set to false, the * cluster marker is positioned at the location of the first marker added to the cluster. * @property {number} [minimumClusterSize=2] The minimum number of markers needed in a cluster * before the markers are hidden and a cluster marker appears. * @property {boolean} [ignoreHidden=false] Whether to ignore hidden markers in clusters. You * may want to set this to true to ensure that hidden markers are not included * in the marker count that appears on a cluster marker (this count is the value of the * text property of the result returned by the default calculator). * If set to true and you change the visibility of a marker being clustered, be * sure to also call MarkerClusterer.repaint(). * @property {boolean} [printable=false] Whether to make the cluster icons printable. Do not * set to true if the url fields in the styles array * refer to image sprite files. * @property {string} [title=""] The tooltip to display when the mouse moves over a cluster * marker. * @property {function} [calculator=MarkerClusterer.CALCULATOR] The function used to determine * the text to be displayed on a cluster marker and the index indicating which style to use * for the cluster marker. The input parameters for the function are (1) the array of markers * represented by a cluster marker and (2) the number of cluster icon styles. It returns a * {@link ClusterIconInfo} object. The default calculator returns a * text property which is the number of markers in the cluster and an * index property which is one higher than the lowest integer such that * 10^i exceeds the number of markers in the cluster, or the size of the styles * array, whichever is less. The styles array element used has an index of * index minus 1. For example, the default calculator returns a * text value of "125" and an index of 3 * for a cluster icon representing 125 markers so the element used in the styles * array is 2. * @property {Array} [styles] An array of {@link ClusterIconStyle} elements defining the styles * of the cluster markers to be used. The element to be used to style a given cluster marker * is determined by the function defined by the calculator property. * The default is an array of {@link ClusterIconStyle} elements whose properties are derived * from the values for imagePath, imageExtension, and * imageSizes. * @property {number} [batchSize=MarkerClusterer.BATCH_SIZE] Set this property to the * number of markers to be processed in a single batch when using a browser other than * Internet Explorer (for Internet Explorer, use the batchSizeIE property instead). * @property {number} [batchSizeIE=MarkerClusterer.BATCH_SIZE_IE] When Internet Explorer is * being used, markers are processed in several batches with a small delay inserted between * each batch in an attempt to avoid Javascript timeout errors. Set this property to the * number of markers to be processed in a single batch; select as high a number as you can * without causing a timeout error in the browser. This number might need to be as low as 100 * if 15,000 markers are being managed, for example. * @property {string} [imagePath=MarkerClusterer.IMAGE_PATH] * The full URL of the root name of the group of image files to use for cluster icons. * The complete file name is of the form imagePathn.imageExtension * where n is the image file number (1, 2, etc.). * @property {string} [imageExtension=MarkerClusterer.IMAGE_EXTENSION] * The extension name for the cluster icon image files (e.g., "png" or * "jpg"). * @property {Array} [imageSizes=MarkerClusterer.IMAGE_SIZES] * An array of numbers containing the widths of the group of * imagePathn.imageExtension image files. * (The images are assumed to be square.) */ /** * Creates a MarkerClusterer object with the options specified in {@link MarkerClustererOptions}. * @constructor * @extends google.maps.OverlayView * @param {google.maps.Map} map The Google map to attach to. * @param {Array.} [opt_markers] The markers to be added to the cluster. * @param {MarkerClustererOptions} [opt_options] The optional parameters. */ function MarkerClusterer(map, opt_markers, opt_options) { // MarkerClusterer implements google.maps.OverlayView interface. We use the // extend function to extend MarkerClusterer with google.maps.OverlayView // because it might not always be available when the code is defined so we // look for it at the last possible moment. If it doesn't exist now then // there is no point going ahead :) this.extend(MarkerClusterer, google.maps.OverlayView); opt_markers = opt_markers || []; opt_options = opt_options || {}; this.markers_ = []; this.clusters_ = []; this.listeners_ = []; this.activeMap_ = null; this.ready_ = false; this.gridSize_ = opt_options.gridSize || 60; this.minClusterSize_ = opt_options.minimumClusterSize || 2; this.maxZoom_ = opt_options.maxZoom || null; this.styles_ = opt_options.styles || []; this.title_ = opt_options.title || ""; this.zoomOnClick_ = true; if (opt_options.zoomOnClick !== undefined) { this.zoomOnClick_ = opt_options.zoomOnClick; } this.averageCenter_ = false; if (opt_options.averageCenter !== undefined) { this.averageCenter_ = opt_options.averageCenter; } this.ignoreHidden_ = false; if (opt_options.ignoreHidden !== undefined) { this.ignoreHidden_ = opt_options.ignoreHidden; } this.printable_ = false; if (opt_options.printable !== undefined) { this.printable_ = opt_options.printable; } this.imagePath_ = opt_options.imagePath || MarkerClusterer.IMAGE_PATH; this.imageExtension_ = opt_options.imageExtension || MarkerClusterer.IMAGE_EXTENSION; this.imageSizes_ = opt_options.imageSizes || MarkerClusterer.IMAGE_SIZES; this.calculator_ = opt_options.calculator || MarkerClusterer.CALCULATOR; this.batchSize_ = opt_options.batchSize || MarkerClusterer.BATCH_SIZE; this.batchSizeIE_ = opt_options.batchSizeIE || MarkerClusterer.BATCH_SIZE_IE; if (navigator.userAgent.toLowerCase().indexOf("msie") !== -1) { // Try to avoid IE timeout when processing a huge number of markers: this.batchSize_ = this.batchSizeIE_; } this.setupStyles_(); this.addMarkers(opt_markers, true); this.setMap(map); // Note: this causes onAdd to be called } /** * Implementation of the onAdd interface method. * @ignore */ MarkerClusterer.prototype.onAdd = function () { var cMarkerClusterer = this; this.activeMap_ = this.getMap(); this.ready_ = true; this.repaint(); // Add the map event listeners this.listeners_ = [ google.maps.event.addListener(this.getMap(), "zoom_changed", function () { cMarkerClusterer.resetViewport_(false); // Workaround for this Google bug: when map is at level 0 and "-" of // zoom slider is clicked, a "zoom_changed" event is fired even though // the map doesn't zoom out any further. In this situation, no "idle" // event is triggered so the cluster markers that have been removed // do not get redrawn. if (this.getZoom() === 0) { google.maps.event.trigger(this, "idle"); } }), google.maps.event.addListener(this.getMap(), "idle", function () { cMarkerClusterer.redraw_(); }) ]; }; /** * Implementation of the onRemove interface method. * Removes map event listeners and all cluster icons from the DOM. * All managed markers are also put back on the map. * @ignore */ MarkerClusterer.prototype.onRemove = function () { var i; // Put all the managed markers back on the map: for (i = 0; i < this.markers_.length; i++) { this.markers_[i].setMap(this.activeMap_); } // Remove all clusters: for (i = 0; i < this.clusters_.length; i++) { this.clusters_[i].remove(); } this.clusters_ = []; // Remove map event listeners: for (i = 0; i < this.listeners_.length; i++) { google.maps.event.removeListener(this.listeners_[i]); } this.listeners_ = []; this.activeMap_ = null; this.ready_ = false; }; /** * Implementation of the draw interface method. * @ignore */ MarkerClusterer.prototype.draw = function () {}; /** * Sets up the styles object. */ MarkerClusterer.prototype.setupStyles_ = function () { var i, size; if (this.styles_.length > 0) { return; } for (i = 0; i < this.imageSizes_.length; i++) { size = this.imageSizes_[i]; this.styles_.push({ url: this.imagePath_ + (i + 1) + "." + this.imageExtension_, height: size, width: size }); } }; /** * Fits the map to the bounds of the markers managed by the clusterer. */ MarkerClusterer.prototype.fitMapToMarkers = function () { var i; var markers = this.getMarkers(); var bounds = new google.maps.LatLngBounds(); for (i = 0; i < markers.length; i++) { bounds.extend(markers[i].getPosition()); } this.getMap().fitBounds(bounds); }; /** * Returns the value of the gridSize property. * * @return {number} The grid size. */ MarkerClusterer.prototype.getGridSize = function () { return this.gridSize_; }; /** * Sets the value of the gridSize property. * * @param {number} gridSize The grid size. */ MarkerClusterer.prototype.setGridSize = function (gridSize) { this.gridSize_ = gridSize; }; /** * Returns the value of the minimumClusterSize property. * * @return {number} The minimum cluster size. */ MarkerClusterer.prototype.getMinimumClusterSize = function () { return this.minClusterSize_; }; /** * Sets the value of the minimumClusterSize property. * * @param {number} minimumClusterSize The minimum cluster size. */ MarkerClusterer.prototype.setMinimumClusterSize = function (minimumClusterSize) { this.minClusterSize_ = minimumClusterSize; }; /** * Returns the value of the maxZoom property. * * @return {number} The maximum zoom level. */ MarkerClusterer.prototype.getMaxZoom = function () { return this.maxZoom_; }; /** * Sets the value of the maxZoom property. * * @param {number} maxZoom The maximum zoom level. */ MarkerClusterer.prototype.setMaxZoom = function (maxZoom) { this.maxZoom_ = maxZoom; }; /** * Returns the value of the styles property. * * @return {Array} The array of styles defining the cluster markers to be used. */ MarkerClusterer.prototype.getStyles = function () { return this.styles_; }; /** * Sets the value of the styles property. * * @param {Array.} styles The array of styles to use. */ MarkerClusterer.prototype.setStyles = function (styles) { this.styles_ = styles; }; /** * Returns the value of the title property. * * @return {string} The content of the title text. */ MarkerClusterer.prototype.getTitle = function () { return this.title_; }; /** * Sets the value of the title property. * * @param {string} title The value of the title property. */ MarkerClusterer.prototype.setTitle = function (title) { this.title_ = title; }; /** * Returns the value of the zoomOnClick property. * * @return {boolean} True if zoomOnClick property is set. */ MarkerClusterer.prototype.getZoomOnClick = function () { return this.zoomOnClick_; }; /** * Sets the value of the zoomOnClick property. * * @param {boolean} zoomOnClick The value of the zoomOnClick property. */ MarkerClusterer.prototype.setZoomOnClick = function (zoomOnClick) { this.zoomOnClick_ = zoomOnClick; }; /** * Returns the value of the averageCenter property. * * @return {boolean} True if averageCenter property is set. */ MarkerClusterer.prototype.getAverageCenter = function () { return this.averageCenter_; }; /** * Sets the value of the averageCenter property. * * @param {boolean} averageCenter The value of the averageCenter property. */ MarkerClusterer.prototype.setAverageCenter = function (averageCenter) { this.averageCenter_ = averageCenter; }; /** * Returns the value of the ignoreHidden property. * * @return {boolean} True if ignoreHidden property is set. */ MarkerClusterer.prototype.getIgnoreHidden = function () { return this.ignoreHidden_; }; /** * Sets the value of the ignoreHidden property. * * @param {boolean} ignoreHidden The value of the ignoreHidden property. */ MarkerClusterer.prototype.setIgnoreHidden = function (ignoreHidden) { this.ignoreHidden_ = ignoreHidden; }; /** * Returns the value of the imageExtension property. * * @return {string} The value of the imageExtension property. */ MarkerClusterer.prototype.getImageExtension = function () { return this.imageExtension_; }; /** * Sets the value of the imageExtension property. * * @param {string} imageExtension The value of the imageExtension property. */ MarkerClusterer.prototype.setImageExtension = function (imageExtension) { this.imageExtension_ = imageExtension; }; /** * Returns the value of the imagePath property. * * @return {string} The value of the imagePath property. */ MarkerClusterer.prototype.getImagePath = function () { return this.imagePath_; }; /** * Sets the value of the imagePath property. * * @param {string} imagePath The value of the imagePath property. */ MarkerClusterer.prototype.setImagePath = function (imagePath) { this.imagePath_ = imagePath; }; /** * Returns the value of the imageSizes property. * * @return {Array} The value of the imageSizes property. */ MarkerClusterer.prototype.getImageSizes = function () { return this.imageSizes_; }; /** * Sets the value of the imageSizes property. * * @param {Array} imageSizes The value of the imageSizes property. */ MarkerClusterer.prototype.setImageSizes = function (imageSizes) { this.imageSizes_ = imageSizes; }; /** * Returns the value of the calculator property. * * @return {function} the value of the calculator property. */ MarkerClusterer.prototype.getCalculator = function () { return this.calculator_; }; /** * Sets the value of the calculator property. * * @param {function(Array., number)} calculator The value * of the calculator property. */ MarkerClusterer.prototype.setCalculator = function (calculator) { this.calculator_ = calculator; }; /** * Returns the value of the printable property. * * @return {boolean} the value of the printable property. */ MarkerClusterer.prototype.getPrintable = function () { return this.printable_; }; /** * Sets the value of the printable property. * * @param {boolean} printable The value of the printable property. */ MarkerClusterer.prototype.setPrintable = function (printable) { this.printable_ = printable; }; /** * Returns the value of the batchSizeIE property. * * @return {number} the value of the batchSizeIE property. */ MarkerClusterer.prototype.getBatchSizeIE = function () { return this.batchSizeIE_; }; /** * Sets the value of the batchSizeIE property. * * @param {number} batchSizeIE The value of the batchSizeIE property. */ MarkerClusterer.prototype.setBatchSizeIE = function (batchSizeIE) { this.batchSizeIE_ = batchSizeIE; }; /** * Returns the array of markers managed by the clusterer. * * @return {Array} The array of markers managed by the clusterer. */ MarkerClusterer.prototype.getMarkers = function () { return this.markers_; }; /** * Returns the number of markers managed by the clusterer. * * @return {number} The number of markers. */ MarkerClusterer.prototype.getTotalMarkers = function () { return this.markers_.length; }; /** * Returns the current array of clusters formed by the clusterer. * * @return {Array} The array of clusters formed by the clusterer. */ MarkerClusterer.prototype.getClusters = function () { return this.clusters_; }; /** * Returns the number of clusters formed by the clusterer. * * @return {number} The number of clusters formed by the clusterer. */ MarkerClusterer.prototype.getTotalClusters = function () { return this.clusters_.length; }; /** * Adds a marker to the clusterer. The clusters are redrawn unless * opt_nodraw is set to true. * * @param {google.maps.Marker} marker The marker to add. * @param {boolean} [opt_nodraw] Set to true to prevent redrawing. */ MarkerClusterer.prototype.addMarker = function (marker, opt_nodraw) { this.pushMarkerTo_(marker); if (!opt_nodraw) { this.redraw_(); } }; /** * Adds an array of markers to the clusterer. The clusters are redrawn unless * opt_nodraw is set to true. * * @param {Array.} markers The markers to add. * @param {boolean} [opt_nodraw] Set to true to prevent redrawing. */ MarkerClusterer.prototype.addMarkers = function (markers, opt_nodraw) { var i; for (i = 0; i < markers.length; i++) { this.pushMarkerTo_(markers[i]); } if (!opt_nodraw) { this.redraw_(); } }; /** * Pushes a marker to the clusterer. * * @param {google.maps.Marker} marker The marker to add. */ MarkerClusterer.prototype.pushMarkerTo_ = function (marker) { // If the marker is draggable add a listener so we can update the clusters on the dragend: if (marker.getDraggable()) { var cMarkerClusterer = this; google.maps.event.addListener(marker, "dragend", function () { if (cMarkerClusterer.ready_) { this.isAdded = false; cMarkerClusterer.repaint(); } }); } marker.isAdded = false; this.markers_.push(marker); }; /** * Removes a marker from the cluster. The clusters are redrawn unless * opt_nodraw is set to true. Returns true if the * marker was removed from the clusterer. * * @param {google.maps.Marker} marker The marker to remove. * @param {boolean} [opt_nodraw] Set to true to prevent redrawing. * @return {boolean} True if the marker was removed from the clusterer. */ MarkerClusterer.prototype.removeMarker = function (marker, opt_nodraw) { var removed = this.removeMarker_(marker); if (!opt_nodraw && removed) { this.repaint(); } return removed; }; /** * Removes an array of markers from the cluster. The clusters are redrawn unless * opt_nodraw is set to true. Returns true if markers * were removed from the clusterer. * * @param {Array.} markers The markers to remove. * @param {boolean} [opt_nodraw] Set to true to prevent redrawing. * @return {boolean} True if markers were removed from the clusterer. */ MarkerClusterer.prototype.removeMarkers = function (markers, opt_nodraw) { var i, r; var removed = false; for (i = 0; i < markers.length; i++) { r = this.removeMarker_(markers[i]); removed = removed || r; } if (!opt_nodraw && removed) { this.repaint(); } return removed; }; /** * Removes a marker and returns true if removed, false if not. * * @param {google.maps.Marker} marker The marker to remove * @return {boolean} Whether the marker was removed or not */ MarkerClusterer.prototype.removeMarker_ = function (marker) { var i; var index = -1; if (this.markers_.indexOf) { index = this.markers_.indexOf(marker); } else { for (i = 0; i < this.markers_.length; i++) { if (marker === this.markers_[i]) { index = i; break; } } } if (index === -1) { // Marker is not in our list of markers, so do nothing: return false; } marker.setMap(null); this.markers_.splice(index, 1); // Remove the marker from the list of managed markers return true; }; /** * Removes all clusters and markers from the map and also removes all markers * managed by the clusterer. */ MarkerClusterer.prototype.clearMarkers = function () { this.resetViewport_(true); this.markers_ = []; }; /** * Recalculates and redraws all the marker clusters from scratch. * Call this after changing any properties. */ MarkerClusterer.prototype.repaint = function () { var oldClusters = this.clusters_.slice(); this.clusters_ = []; this.resetViewport_(false); this.redraw_(); // Remove the old clusters. // Do it in a timeout to prevent blinking effect. setTimeout(function () { var i; for (i = 0; i < oldClusters.length; i++) { oldClusters[i].remove(); } }, 0); }; /** * Returns the current bounds extended by the grid size. * * @param {google.maps.LatLngBounds} bounds The bounds to extend. * @return {google.maps.LatLngBounds} The extended bounds. * @ignore */ MarkerClusterer.prototype.getExtendedBounds = function (bounds) { var projection = this.getProjection(); // Turn the bounds into latlng. var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), bounds.getNorthEast().lng()); var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), bounds.getSouthWest().lng()); // Convert the points to pixels and the extend out by the grid size. var trPix = projection.fromLatLngToDivPixel(tr); trPix.x += this.gridSize_; trPix.y -= this.gridSize_; var blPix = projection.fromLatLngToDivPixel(bl); blPix.x -= this.gridSize_; blPix.y += this.gridSize_; // Convert the pixel points back to LatLng var ne = projection.fromDivPixelToLatLng(trPix); var sw = projection.fromDivPixelToLatLng(blPix); // Extend the bounds to contain the new bounds. bounds.extend(ne); bounds.extend(sw); return bounds; }; /** * Redraws all the clusters. */ MarkerClusterer.prototype.redraw_ = function () { this.createClusters_(0); }; /** * Removes all clusters from the map. The markers are also removed from the map * if opt_hide is set to true. * * @param {boolean} [opt_hide] Set to true to also remove the markers * from the map. */ MarkerClusterer.prototype.resetViewport_ = function (opt_hide) { var i, marker; // Remove all the clusters for (i = 0; i < this.clusters_.length; i++) { this.clusters_[i].remove(); } this.clusters_ = []; // Reset the markers to not be added and to be removed from the map. for (i = 0; i < this.markers_.length; i++) { marker = this.markers_[i]; marker.isAdded = false; if (opt_hide) { marker.setMap(null); } } }; /** * Calculates the distance between two latlng locations in km. * * @param {google.maps.LatLng} p1 The first lat lng point. * @param {google.maps.LatLng} p2 The second lat lng point. * @return {number} The distance between the two points in km. * @see http://www.movable-type.co.uk/scripts/latlong.html */ MarkerClusterer.prototype.distanceBetweenPoints_ = function (p1, p2) { var R = 6371; // Radius of the Earth in km var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var d = R * c; return d; }; /** * Determines if a marker is contained in a bounds. * * @param {google.maps.Marker} marker The marker to check. * @param {google.maps.LatLngBounds} bounds The bounds to check against. * @return {boolean} True if the marker is in the bounds. */ MarkerClusterer.prototype.isMarkerInBounds_ = function (marker, bounds) { return bounds.contains(marker.getPosition()); }; /** * Adds a marker to a cluster, or creates a new cluster. * * @param {google.maps.Marker} marker The marker to add. */ MarkerClusterer.prototype.addToClosestCluster_ = function (marker) { var i, d, cluster, center; var distance = 40000; // Some large number var clusterToAddTo = null; for (i = 0; i < this.clusters_.length; i++) { cluster = this.clusters_[i]; center = cluster.getCenter(); if (center) { d = this.distanceBetweenPoints_(center, marker.getPosition()); if (d < distance) { distance = d; clusterToAddTo = cluster; } } } if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { clusterToAddTo.addMarker(marker); } else { cluster = new Cluster(this); cluster.addMarker(marker); this.clusters_.push(cluster); } }; /** * Creates the clusters. This is done in batches to avoid timeout errors * in some browsers when there is a huge number of markers. * * @param {number} iFirst The index of the first marker in the batch of * markers to be added to clusters. */ MarkerClusterer.prototype.createClusters_ = function (iFirst) { var i, marker; var mapBounds; var cMarkerClusterer = this; if (!this.ready_) { return; } // Cancel previous batch processing if we're working on the first batch: if (iFirst === 0) { /** * This event is fired when the MarkerClusterer begins * clustering markers. * @name MarkerClusterer#clusteringbegin * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered. * @event */ google.maps.event.trigger(this, "clusteringbegin", this); if (typeof this.timerRefStatic !== "undefined") { clearTimeout(this.timerRefStatic); delete this.timerRefStatic; } } // Get our current map view bounds. // Create a new bounds object so we don't affect the map. // // See Comments 9 & 11 on Issue 3651 relating to this workaround for a Google Maps bug: if (this.getMap().getZoom() > 3) { mapBounds = new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(), this.getMap().getBounds().getNorthEast()); } else { mapBounds = new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472, -178.48388434375), new google.maps.LatLng(-85.08136444384544, 178.00048865625)); } var bounds = this.getExtendedBounds(mapBounds); var iLast = Math.min(iFirst + this.batchSize_, this.markers_.length); for (i = iFirst; i < iLast; i++) { marker = this.markers_[i]; if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { if (!this.ignoreHidden_ || (this.ignoreHidden_ && marker.getVisible())) { this.addToClosestCluster_(marker); } } } if (iLast < this.markers_.length) { this.timerRefStatic = setTimeout(function () { cMarkerClusterer.createClusters_(iLast); }, 0); } else { delete this.timerRefStatic; /** * This event is fired when the MarkerClusterer stops * clustering markers. * @name MarkerClusterer#clusteringend * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered. * @event */ google.maps.event.trigger(this, "clusteringend", this); } }; /** * Extends an object's prototype by another's. * * @param {Object} obj1 The object to be extended. * @param {Object} obj2 The object to extend with. * @return {Object} The new extended object. * @ignore */ MarkerClusterer.prototype.extend = function (obj1, obj2) { return (function (object) { var property; for (property in object.prototype) { this.prototype[property] = object.prototype[property]; } return this; }).apply(obj1, [obj2]); }; /** * The default function for determining the label text and style * for a cluster icon. * * @param {Array.} markers The array of markers represented by the cluster. * @param {number} numStyles The number of marker styles available. * @return {ClusterIconInfo} The information resource for the cluster. * @constant * @ignore */ MarkerClusterer.CALCULATOR = function (markers, numStyles) { var index = 0; var count = markers.length.toString(); var dv = count; while (dv !== 0) { dv = parseInt(dv / 10, 10); index++; } index = Math.min(index, numStyles); return { text: count, index: index }; }; /** * The number of markers to process in one batch. * * @type {number} * @constant */ MarkerClusterer.BATCH_SIZE = 2000; /** * The number of markers to process in one batch (IE only). * * @type {number} * @constant */ MarkerClusterer.BATCH_SIZE_IE = 500; /** * The default root name for the marker cluster images. * * @type {string} * @constant */ MarkerClusterer.IMAGE_PATH = "http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/images/m"; /** * The default extension name for the marker cluster images. * * @type {string} * @constant */ MarkerClusterer.IMAGE_EXTENSION = "png"; /** * The default array of sizes for the marker cluster images. * * @type {Array.} * @constant */ MarkerClusterer.IMAGE_SIZES = [53, 56, 66, 78, 90];;/**/ /** * @name InfoBox http://google-maps-utility-library-v3.googlecode.com/ * @version 1.1.5 [March 1, 2011] * @author Gary Little (inspired by proof-of-concept code from Pamela Fox of Google) * @copyright Copyright 2010 Gary Little [gary at luxcentral.com] * @fileoverview InfoBox extends the Google Maps JavaScript API V3 OverlayView class. */ function InfoBox(a) { a = a || {}; google.maps.OverlayView.apply(this, arguments); this.content_ = a.content || ""; this.disableAutoPan_ = a.disableAutoPan || false; this.maxWidth_ = a.maxWidth || 0; this.pixelOffset_ = a.pixelOffset || new google.maps.Size(0, 0); this.position_ = a.position || new google.maps.LatLng(0, 0); this.zIndex_ = a.zIndex || null; this.boxClass_ = a.boxClass || "infoBox"; this.boxStyle_ = a.boxStyle || {}; this.closeBoxMargin_ = a.closeBoxMargin || "2px"; this.closeBoxURL_ = a.closeBoxURL || "http://www.google.com/intl/en_us/mapfiles/close.gif"; if (a.closeBoxURL === "") { this.closeBoxURL_ = "" } this.infoBoxClearance_ = a.infoBoxClearance || new google.maps.Size(1, 1); this.isHidden_ = a.isHidden || false; this.alignBottom_ = a.alignBottom || false; this.pane_ = a.pane || "floatPane"; this.enableEventPropagation_ = a.enableEventPropagation || false; this.div_ = null; this.closeListener_ = null; this.eventListener1_ = null; this.eventListener2_ = null; this.eventListener3_ = null; this.moveListener_ = null; this.contextListener_ = null; this.fixedWidthSet_ = null } InfoBox.prototype = new google.maps.OverlayView(); InfoBox.prototype.createInfoBoxDiv_ = function () { var a; var d = this; var c = function (e) { e.cancelBubble = true; if (e.stopPropagation) { e.stopPropagation() } }; var b = function (e) { e.returnValue = false; if (e.preventDefault) { e.preventDefault() } if (!d.enableEventPropagation_) { c(e) } }; if (!this.div_) { this.div_ = document.createElement("div"); this.setBoxStyle_(); // if (typeof this.content_.nodeType === "undefined") { // //this.div_.innerHTML = this.getCloseBoxImg_() + this.content_ // } else { // this.div_.innerHTML = this.getCloseBoxImg_(); // this.div_.appendChild(this.content_) // } //this.getPanes()[this.pane_].appendChild(this.div_); // Prepend infobox to the a-feature instead of inside the map $mapContainer = jQuery('div.tns-worldwide.a-feature'); $mapContainer.prepend(this.div_); if ($.isFunction(jScrollPane)) { $mapContainer.find('div.scroll-pane').jScrollPane(); } this.addClickHandler_(); if (this.div_.style.width) { this.fixedWidthSet_ = true } else { if (this.maxWidth_ !== 0 && this.div_.offsetWidth > this.maxWidth_) { this.div_.style.width = this.maxWidth_; this.div_.style.overflow = "auto"; this.fixedWidthSet_ = true } else { a = this.getBoxWidths_(); this.div_.style.width = (this.div_.offsetWidth - a.left - a.right) + "px"; this.fixedWidthSet_ = false } } this.panBox_(this.disableAutoPan_); if (!this.enableEventPropagation_) { this.eventListener1_ = google.maps.event.addDomListener(this.div_, "mousedown", c); this.eventListener2_ = google.maps.event.addDomListener(this.div_, "click", c); this.eventListener3_ = google.maps.event.addDomListener(this.div_, "dblclick", c) } this.contextListener_ = google.maps.event.addDomListener(this.div_, "contextmenu", b); google.maps.event.trigger(this, "domready") } }; InfoBox.prototype.getCloseBoxImg_ = function () { var a = ""; if (this.closeBoxURL_ !== "") { /* a = " h) { e = o.x + g + k + i - h } if (this.alignBottom_) { if (o.y < (-l + j + b)) { yOffset = o.y + l - j - b } else if ((o.y + l + j) > f) { yOffset = o.y + l + j - f } } else { if (o.y < (-l + j)) { yOffset = o.y + l - j } else if ((o.y + b + l + j) > f) { yOffset = o.y + b + l + j - f } } if (!(e === 0 && yOffset === 0)) { var c = m.getCenter(); m.panBy(e, yOffset) } } } }; InfoBox.prototype.setBoxStyle_ = function () { var i, boxStyle; if (this.div_) { this.div_.className = this.boxClass_; this.div_.style.cssText = ""; boxStyle = this.boxStyle_; for (i in boxStyle) { if (boxStyle.hasOwnProperty(i)) { this.div_.style[i] = boxStyle[i] } } if (typeof this.div_.style.opacity !== "undefined" && this.div_.style.opacity !== "") { this.div_.style.filter = "alpha(opacity=" + (this.div_.style.opacity * 100) + ")" } this.div_.style.position = "absolute"; this.div_.style.visibility = 'hidden'; if (this.zIndex_ !== null) { this.div_.style.zIndex = this.zIndex_ } } }; InfoBox.prototype.getBoxWidths_ = function () { var c; var a = { top: 0, bottom: 0, left: 0, right: 0 }; var b = this.div_; if (document.defaultView && document.defaultView.getComputedStyle) { c = b.ownerDocument.defaultView.getComputedStyle(b, ""); if (c) { a.top = parseInt(c.borderTopWidth, 10) || 0; a.bottom = parseInt(c.borderBottomWidth, 10) || 0; a.left = parseInt(c.borderLeftWidth, 10) || 0; a.right = parseInt(c.borderRightWidth, 10) || 0 } } else if (document.documentElement.currentStyle) { if (b.currentStyle) { a.top = parseInt(b.currentStyle.borderTopWidth, 10) || 0; a.bottom = parseInt(b.currentStyle.borderBottomWidth, 10) || 0; a.left = parseInt(b.currentStyle.borderLeftWidth, 10) || 0; a.right = parseInt(b.currentStyle.borderRightWidth, 10) || 0 } } return a }; InfoBox.prototype.onRemove = function () { if (this.div_) { //this.div_.parentNode.removeChild(this.div_); jQuery('div.tns-worldwide.a-feature').find('div.infoBox').remove(); this.div_ = null } }; InfoBox.prototype.draw = function () { this.createInfoBoxDiv_(); // Don't pan the infobox /* var a = this.getProjection().fromLatLngToDivPixel(this.position_); this.div_.style.left = (a.x + this.pixelOffset_.width) + "px"; if (this.alignBottom_) { this.div_.style.bottom = -(a.y + this.pixelOffset_.height) + "px" } else { this.div_.style.top = (a.y + this.pixelOffset_.height) + "px" } */ if (this.isHidden_) { this.div_.style.visibility = 'hidden' } else { this.div_.style.visibility = "visible" } }; InfoBox.prototype.setOptions = function (a) { if (typeof a.boxClass !== "undefined") { this.boxClass_ = a.boxClass; this.setBoxStyle_() } if (typeof a.boxStyle !== "undefined") { this.boxStyle_ = a.boxStyle; this.setBoxStyle_() } if (typeof a.content !== "undefined") { this.setContent(a.content) } if (typeof a.disableAutoPan !== "undefined") { this.disableAutoPan_ = a.disableAutoPan } if (typeof a.maxWidth !== "undefined") { this.maxWidth_ = a.maxWidth } if (typeof a.pixelOffset !== "undefined") { this.pixelOffset_ = a.pixelOffset } if (typeof a.position !== "undefined") { this.setPosition(a.position) } if (typeof a.zIndex !== "undefined") { this.setZIndex(a.zIndex) } if (typeof a.closeBoxMargin !== "undefined") { this.closeBoxMargin_ = a.closeBoxMargin } if (typeof a.closeBoxURL !== "undefined") { this.closeBoxURL_ = a.closeBoxURL } if (typeof a.infoBoxClearance !== "undefined") { this.infoBoxClearance_ = a.infoBoxClearance } if (typeof a.isHidden !== "undefined") { this.isHidden_ = a.isHidden } if (typeof a.enableEventPropagation !== "undefined") { this.enableEventPropagation_ = a.enableEventPropagation } if (this.div_) { this.draw() } }; InfoBox.prototype.setContent = function (a) { this.content_ = a; if (this.div_) { if (this.closeListener_) { google.maps.event.removeListener(this.closeListener_); this.closeListener_ = null } if (!this.fixedWidthSet_) { this.div_.style.width = "" } if (typeof a.nodeType === "undefined") { this.div_.innerHTML = this.getCloseBoxImg_() + a } else { this.div_.innerHTML = this.getCloseBoxImg_(); this.div_.appendChild(a) } if (!this.fixedWidthSet_) { this.div_.style.width = this.div_.offsetWidth + "px"; this.div_.innerHTML = this.getCloseBoxImg_() + a } this.addClickHandler_() } google.maps.event.trigger(this, "content_changed") }; InfoBox.prototype.setPosition = function (a) { this.position_ = a; if (this.div_) { this.draw() } google.maps.event.trigger(this, "position_changed") }; InfoBox.prototype.setZIndex = function (a) { this.zIndex_ = a; if (this.div_) { this.div_.style.zIndex = a } google.maps.event.trigger(this, "zindex_changed") }; InfoBox.prototype.getContent = function () { return this.content_ }; InfoBox.prototype.getPosition = function () { return this.position_ }; InfoBox.prototype.getZIndex = function () { return this.zIndex_ }; InfoBox.prototype.show = function () { this.isHidden_ = false; if (this.div_) { this.div_.style.visibility = "visible" } }; InfoBox.prototype.hide = function () { this.isHidden_ = true; if (this.div_) { this.div_.style.visibility = "hidden" } }; InfoBox.prototype.open = function (c, b) { var a = this; if (b) { this.position_ = b.getPosition(); this.moveListener_ = google.maps.event.addListener(b, "position_changed", function () { a.setPosition(this.getPosition()) }) } this.setMap(c); if (this.div_) { this.panBox_() } }; InfoBox.prototype.close = function () { if (this.closeListener_) { google.maps.event.removeListener(this.closeListener_); this.closeListener_ = null } if (this.eventListener1_) { google.maps.event.removeListener(this.eventListener1_); google.maps.event.removeListener(this.eventListener2_); google.maps.event.removeListener(this.eventListener3_); this.eventListener1_ = null; this.eventListener2_ = null; this.eventListener3_ = null } if (this.moveListener_) { google.maps.event.removeListener(this.moveListener_); this.moveListener_ = null } if (this.contextListener_) { google.maps.event.removeListener(this.contextListener_); this.contextListener_ = null } this.setMap(null) }; ;/**/ /*! * MOSNE MAP / jQuery Plugin v0.9 * markerClusterer + InfoBox + Geocoder + Styled Google Maps API v3 * http://www.mosne.it/playground/mosne_map/ * * Require jQuery 1.5+, Google Maps API v3, markerClusterer, InfoBox * * Copyright 2011, Paolo Mosne * Licensed under GPL Version 3 licenses. * http://www.gnu.org/licenses/gpl.html * * Date: 2011-03-20 11:19 AM */ (function ($) { $('.azlink').click(function(e) { var office_class = ".office" + $(this).data('office'); $("html, body").animate({ scrollTop: 0 }, "slow"); $(office_class).trigger('click'); e.preventDefault(); }); $.fn.mosne_map = function (options) { var baseconf = { zoom: 6, mapTypeId: google.maps.MapTypeId.ROADMAP }; var s_infobox = { content: "", disableAutoPan: true, maxWidth: 824, pixelOffset: new google.maps.Size(-440, -210), zIndex: 2, boxStyle: { opacity: 1, color:'#000', padding: '20', width: "824px" }, closeBoxMargin: "9px -59px", infoBoxClearance: new google.maps.Size(1, 1), isHidden: false, pane: "floatPane", enableEventPropagation: false }; defaults = { elements: '#list .maplocation', //links selector map_opt: baseconf, // custom map options object clat: 41.895466, // set the lat default map center clng: 12.482324, // set the lng default map center mapstyle_name: '', // custom map style label and id mapstyle: '', // mapstyle object cluster_styles: {}, // custom cluster icons object marker_icon: '', // custom marker icon url infowindows: false, // shows infoWindows grabing html from the .infobox element infobox: false, // enable custom infoWindows using infobox class infobox_s: s_infobox, // default color scheme for custom infobox container trigger: 'map_open', // you can set a event trigger for each link/marker clickedzoom: 15, // set the zoom level when you click the single marker clickdirect: true, // redirect the page to data-path attibute link if true timeout: 100, // delay between click and zoom on the single marker mode: 'latlng', // switch mode wait: 500, // timeout between geocode requests maxtry:10, // limit of time to bypass query overlimit cat_style: {}, // costum icons and click zoom level fitbounds: true, // on|off fit bounds defzoom : 20, // default zoom level if fitbounds is off showzoom: false, // bind current map zoom level event before: function () {}, // before create map callback after: function () {}, // after create map callback afterUpdate: function () {} // after update map callback }; var settings = $.extend({}, defaults, options); this.each(function () { var map_el = $(this); var the_map_el = $(this).get(0); //on before settings.before.apply(map_el); //init map var center = new google.maps.LatLng(settings.clat, settings.clng); var map = new google.maps.Map(the_map_el, settings.map_opt); var bounds = new google.maps.LatLngBounds(); var markerCluster = new MarkerClusterer(map, null, settings.cluster_styles); map.setCenter(center); //apply map style if (settings.mapstyle_name != '') { var styledMapOptions = { name: settings.mapstyle_name }; var m_MapType = new google.maps.StyledMapType(settings.mapstyle, styledMapOptions); map.mapTypes.set(settings.mapstyle_name, m_MapType); map.setMapTypeId(settings.mapstyle_name); } // set markers icons if (settings.marker_icon != '') { var markerIcon = new google.maps.MarkerImage(settings.marker_icon, new google.maps.Size(21, 34)); } // init infowindow if (settings.infowindows) { var infowindow = new google.maps.InfoWindow({ maxWidth: 80 }); } // init infobox if (settings.infobox) { var boxText = document.createElement("div"); boxText.style.cssText = settings.infobox_s_txt boxText.innerHTML = "hello world"; var m_box = new InfoBox(settings.infobox_s); } // function create marker var _createMarker = function(el,latLng,markerIcon,m_name,cat){ if (cat){ var c_icon = settings.cat_style[cat]['icon']; if (c_icon){ var markerIcon = c_icon; } } var marker = new google.maps.Marker({ position: latLng, icon: markerIcon, title: m_name }); //extend bounds bounds.extend(latLng); // bind click on map trigger event fill infowindow / infobox content on demand if (settings.infowindows || settings.infobox ) { var content = el.find('.infobox').html(); } google.maps.event.addListener(marker, 'click', function () { if (settings.infowindows) { infowindow.close(); infowindow.setContent(content); infowindow.open(map, marker); } if (settings.infobox) { m_box.close(); $(boxText).hide(); m_box.setContent(content); m_box.open(map, marker); $(boxText).show("slow"); } totel1 = $(settings.elements).length; if (settings.clickdirect) { if(totel1 != 1) { window.location.href = el.attr('data-path'); } } el.trigger(settings.trigger); $(el).parents().find('.active').removeClass('active'); $(el).addClass('active'); setTimeout(function () { map.setZoom(settings.clickedzoom); map.panTo(latLng); if(totel1 != 1) { marker.setAnimation(google.maps.Animation.DROP); } }, settings.timeout); }); // trigger click on list $(el).find('.maplink').unbind("click").bind("click", function (e) { $("html, body").animate({ scrollTop: 0 }, "slow"); var country = jQuery.trim($(this).text()); var geocoder = new google.maps.Geocoder(); geocoder.geocode( {'address' : country}, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { //map.setCenter(results[0].geometry.location); var large_countries = ['India','United States', 'Canada', 'Russia', 'China']; var really_large_countries = ['Russia']; if($.inArray(country, large_countries) != -1) { map.setZoom(4); } else if($.inArray(country, really_large_countries) != -1) { map.setZoom(3); } else if(country == "Belgium") { map.setZoom(10); } else { map.setZoom(8); } } }); e.preventDefault(); if(!$(this).hasClass('multi')) { google.maps.event.trigger(marker, "click"); // pierre } else { map.setZoom(13); map.panTo(latLng); } return false; }); markerCluster.addMarker(marker); }; var _m_geocode = function (el,geocoder,address,name,cat,j){ geocoder.geocode({ 'address': address }, function (results, status) { if (status == google.maps.GeocoderStatus.OK) { latLng = results[0].geometry.location; _createMarker (el,latLng,markerIcon,name,cat); if ( settings.fitbounds === true){ map.fitBounds(bounds); } } else { if (status==="OVER_QUERY_LIMIT"){ setTimeout(function(){ //console.log("trying again "+g_address); j++; if (j<= settings.maxtry){ _m_geocode(el,geocoder,address,name,cat,j); }else{ $(el).css({opacity: .35});} },settings.wait); }else if (status==="ZERO_RESULTS"){ $(el).css({opacity: .35}); } } }); } // $(map_el).bind('update', function () { //reset cluster and bounds markerCluster.clearMarkers(); bounds = null; bounds = new google.maps.LatLngBounds(); totel = $(settings.elements).length; // markers loop var markers = []; var w_delay = 0; if (settings.mode === 'address') { var geocoder = new google.maps.Geocoder(); } $(settings.elements).each(function (i) { // create marker var el = $(this); //mode geocoding if (settings.mode === 'address') { var mkr = el.data(); var name = $(this).find(".name").text(); var address = $(this).find(".address").text(); setTimeout(function () { _m_geocode(el,geocoder,address,name,mkr.cat,0); }, settings.wait*i); } else { // mode latlng var mkr = el.data(); var latLng = new google.maps.LatLng(mkr.lat, mkr.lng); _createMarker (el,latLng,markerIcon,mkr.name,mkr.cat); } //end of the elements loop }); if ( settings.fitbounds === true){ map.fitBounds(bounds); if(totel==1){ map.setZoom(settings.clickedzoom); } if(totel==0){ map.setZoom(settings.defzoom); map.setCenter(center); } }else{ if(totel==1){ map.setCenter(bounds.getCenter()); map.setZoom(10); } else{ map.setZoom(settings.defzoom); } }; //callbak afterUpdate settings.afterUpdate.apply(map_el); }).trigger('update'); $(map_el).bind('bounds', function () { map.fitBounds(bounds); }); // nice zoom status if (settings.showzoom) { google.maps.event.addListener(map, 'zoom_changed', function () { $(map_el).trigger("showzoom", [map.getZoom()]); }); }; //on after settings.after.apply(map_el); return true; }); }; })(jQuery); ;/**/ window.log = function(){ log.history = log.history || []; log.history.push(arguments); arguments.callee = arguments.callee.caller; if(this.console) console.log( Array.prototype.slice.call(arguments) ); }; (function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();)b[a]=b[a]||c})(window.console=window.console||{}); /* * Snippet :: jQuery Syntax Highlighter v2.0.0 * http://steamdev.com/snippet * * Copyright 2011, SteamDev * Released under the MIT license. * http://www.opensource.org/licenses/mit-license.php * * Date: Wed Jan 19, 2011 */ (function(a){window.log=function(){log.history=log.history||[];log.history.push(arguments);if(this.console){console.log(Array.prototype.slice.call(arguments))}};a.fn.snippet=function(e,c){if(typeof e=="object"){c=e}if(typeof e=="string"){e=e.toLowerCase()}var d={style:"random",showNum:true,transparent:false,collapse:false,menu:true,showMsg:"Expand Code",hideMsg:"Collapse Code",clipboard:"",startCollapsed:true,startText:false,box:"",boxColor:"",boxFill:""};var b=["acid","berries-dark","berries-light","bipolar","blacknblue","bright","contrast","darkblue","darkness","desert","dull","easter","emacs","golden","greenlcd","ide-anjuta","ide-codewarrior","ide-devcpp","ide-eclipse","ide-kdev","ide-msvcpp","kwrite","matlab","navy","nedit","neon","night","pablo","peachpuff","print","rand01","the","typical","vampire","vim","vim-dark","whatis","whitengrey","zellner"];if(c){a.extend(d,c)}return this.each(function(){var H=d.style.toLowerCase();if(d.style=="random"){var D=Math.floor(Math.random()*(b.length));H=b[D]}var u=a(this);var y=this.nodeName.toLowerCase();if(y=="pre"){if(u.data("orgHtml")==undefined||u.data("orgHtml")==null){var f=u.html();u.data("orgHtml",f)}if(!u.parent().hasClass("snippet-wrap")){if(typeof e!="string"){if(u.attr("class").length>0){var t=' class="'+u.attr("class")+'"'}else{var t=""}if(u.attr("id").length>0){var J=' id="'+u.attr("id")+'"'}else{var J=""}var A="Snippet Error: You must specify a language on inital usage of Snippet. Reference ";console.log(A);return false}u.addClass("sh_"+e).addClass("snippet-formatted").wrap("
");u.removeAttr("style");sh_highlightDocument();if(d.showNum){var v=u.html();v=v.replace(/\n/g,"
  • ");v="
    1. "+v+"
    ";while(v.indexOf("
  • ")!=-1){v=v.replace("
  • ","")}}else{var v=u.html();v=v.replace(/\n/g,"
  • ");v="
    • "+v+"
    ";while(v.indexOf("
  • ")!=-1){v=v.replace("
  • ","")}}v=v.replace(/\t/g,"    ");u.html(v);while(u.find("li").eq(0).html()==""){u.find("li").eq(0).remove()}u.find("li").each(function(){if(a(this).html().length<2){var i=(a(this).html()).replace(/\s/g,"");if(i==""){if(a.browser.opera){a(this).html(" ")}else{a(this).html(" ")}}}});var w="";var r="";u.parent().append(w);u.parent().prepend(r);u.parent().hover(function(){a(this).find(".snippet-menu").fadeIn("fast")},function(){a(this).find(".snippet-menu").fadeOut("fast")});if(d.clipboard!=""&&d.clipboard!=false){var j=u.parent().find("a.snippet-copy");j.show();j.parents(".snippet-menu").show();var s=u.parents(".snippet-wrap").find(".snippet-textonly").text();ZeroClipboard.setMoviePath(d.clipboard);var G=new ZeroClipboard.Client();G.setText(s);G.glue(j[0],j.parents(".snippet-menu")[0]);G.addEventListener("complete",function(i,o){if(o.length>500){o=o.substr(0,500)+"...\n\n("+(o.length-500)+" characters not shown)"}alert("Copied text to clipboard:\n\n "+o)});j.parents(".snippet-menu").hide()}else{u.parent().find("a.snippet-copy").hide()}u.parent().find("a.snippet-text").click(function(){var o=a(this).parents(".snippet-wrap").find(".snippet-formatted");var i=a(this).parents(".snippet-wrap").find(".snippet-textonly");o.toggle();i.toggle();if(i.is(":visible")){a(this).html("html")}else{a(this).html("text")}a(this).blur();return false});u.parent().find("a.snippet-window").click(function(){var i=a(this).parents(".snippet-wrap").find(".snippet-textonly").html();snippetPopup(i);a(this).blur();return false});if(!d.menu){u.prev(".snippet-menu").find("pre,.snippet-clipboard").hide()}if(d.collapse){var n=u.parent().attr("class");var h="";var E="";u.parents(".snippet-container").append(h);u.parent().append(E);var z=u.parents(".snippet-container");if(d.startCollapsed){z.find(".snippet-reveal").show();z.find(".snippet-wrap").eq(0).hide()}else{z.find(".snippet-reveal").hide();z.find(".snippet-wrap").eq(0).show()}z.find("a.snippet-toggle").click(function(){z.find(".snippet-wrap").toggle();return false})}if(d.transparent){var k={"background-color":"transparent","box-shadow":"none","-moz-box-shadow":"none","-webkit-box-shadow":"none"};u.css(k);u.next(".snippet-textonly").css(k);u.parents(".snippet-container").find(".snippet-reveal pre").css(k)}if(d.startText){u.hide();u.next(".snippet-textonly").show();u.parent().find(".snippet-text").html("html")}if(d.box!=""){var m=" ";var C=d.box.split(",");for(var B=0;B");var q=u.find("li").eq(0);q.unwrap()}}else{var F=u.find("li").eq(0).parent();if(F.hasClass("snippet-num")){F.wrap("
      ");var q=u.find("li").eq(0);q.unwrap()}}if(d.box!=""){var m=" ";var C=d.box.split(",");for(var B=0;B' elements are currently unsupported.";console.log(A);return false}})}})(jQuery);function snippetPopup(a){top.consoleRef=window.open("","myconsole","width=600,height=300,left=50,top=50,menubar=0,toolbar=0,location=0,status=0,scrollbars=1,resizable=1");top.consoleRef.document.writeln("Snippet :: Code View :: "+location.href+'
      '+a+"
      ");top.consoleRef.document.close()}var ZeroClipboard={version:"1.0.7",clients:{},moviePath:"ZeroClipboard.swf",nextId:1,$:function(a){if(typeof(a)=="string"){a=document.getElementById(a)}if(!a.addClass){a.hide=function(){this.style.display="none"};a.show=function(){this.style.display=""};a.addClass=function(b){this.removeClass(b);this.className+=" "+b};a.removeClass=function(d){var e=this.className.split(/\s+/);var b=-1;for(var c=0;c-1){e.splice(b,1);this.className=e.join(" ")}return this};a.hasClass=function(b){return !!this.className.match(new RegExp("\\s*"+b+"\\s*"))}}return a},setMoviePath:function(a){this.moviePath=a},dispatch:function(d,b,c){var a=this.clients[d];if(a){a.receiveEvent(b,c)}},register:function(b,a){this.clients[b]=a},getDOMObjectPosition:function(c,a){var b={left:0,top:0,width:c.width?c.width:c.offsetWidth,height:c.height?c.height:c.offsetHeight};while(c&&(c!=a)){b.left+=c.offsetLeft;b.top+=c.offsetTop;c=c.offsetParent}return b},Client:function(a){this.handlers={};this.id=ZeroClipboard.nextId++;this.movieId="ZeroClipboardMovie_"+this.id;ZeroClipboard.register(this.id,this);if(a){this.glue(a)}}};ZeroClipboard.Client.prototype={id:0,ready:false,movie:null,clipText:"",handCursorEnabled:true,cssEffects:true,handlers:null,glue:function(d,b,e){this.domElement=ZeroClipboard.$(d);var f=99;if(this.domElement.style.zIndex){f=parseInt(this.domElement.style.zIndex,10)+1}if(typeof(b)=="string"){b=ZeroClipboard.$(b)}else{if(typeof(b)=="undefined"){b=document.getElementsByTagName("body")[0]}}var c=ZeroClipboard.getDOMObjectPosition(this.domElement,b);this.div=document.createElement("div");this.div.className="snippet-clipboard";var a=this.div.style;a.position="absolute";a.left=""+c.left+"px";a.top=""+c.top+"px";a.width=""+c.width+"px";a.height=""+c.height+"px";a.zIndex=f;if(typeof(e)=="object"){for(addedStyle in e){a[addedStyle]=e[addedStyle]}}b.appendChild(this.div);this.div.innerHTML=this.getHTML(c.width,c.height)},getHTML:function(d,a){var c="";var b="id="+this.id+"&width="+d+"&height="+a;if(navigator.userAgent.match(/MSIE/)){var e=location.href.match(/^https/i)?"https://":"http://";c+=''}else{c+=''}return c},hide:function(){if(this.div){this.div.style.left="-2000px"}},show:function(){this.reposition()},destroy:function(){if(this.domElement&&this.div){this.hide();this.div.innerHTML="";var a=document.getElementsByTagName("body")[0];try{a.removeChild(this.div)}catch(b){}this.domElement=null;this.div=null}},reposition:function(c){if(c){this.domElement=ZeroClipboard.$(c);if(!this.domElement){this.hide()}}if(this.domElement&&this.div){var b=ZeroClipboard.getDOMObjectPosition(this.domElement);var a=this.div.style;a.left=""+b.left+"px";a.top=""+b.top+"px"}},setText:function(a){this.clipText=a;if(this.ready){this.movie.setText(a)}},addEventListener:function(a,b){a=a.toString().toLowerCase().replace(/^on/,"");if(!this.handlers[a]){this.handlers[a]=[]}this.handlers[a].push(b)},setHandCursor:function(a){this.handCursorEnabled=a;if(this.ready){this.movie.setHandCursor(a)}},setCSSEffects:function(a){this.cssEffects=!!a},receiveEvent:function(d,f){d=d.toString().toLowerCase().replace(/^on/,"");switch(d){case"load":this.movie=document.getElementById(this.movieId);if(!this.movie){var c=this;setTimeout(function(){c.receiveEvent("load",null)},1);return}if(!this.ready&&navigator.userAgent.match(/Firefox/)&&navigator.userAgent.match(/Windows/)){var c=this;setTimeout(function(){c.receiveEvent("load",null)},100);this.ready=true;return}this.ready=true;try{this.movie.setText(this.clipText)}catch(h){}try{this.movie.setHandCursor(this.handCursorEnabled)}catch(h){}break;case"mouseover":if(this.domElement&&this.cssEffects){this.domElement.addClass("hover");if(this.recoverActive){this.domElement.addClass("active")}}break;case"mouseout":if(this.domElement&&this.cssEffects){this.recoverActive=false;if(this.domElement.hasClass("active")){this.domElement.removeClass("active");this.recoverActive=true}this.domElement.removeClass("hover")}break;case"mousedown":if(this.domElement&&this.cssEffects){this.domElement.addClass("active")}break;case"mouseup":if(this.domElement&&this.cssEffects){this.domElement.removeClass("active");this.recoverActive=false}break}if(this.handlers[d]){for(var b=0,a=this.handlers[d].length;b=2&&f.charAt(0)==="<"&&f.charAt(f.length-1)===">"){f=f.substr(1,f.length-2)}if(sh_isEmailAddress(f)){f="mailto:"+f}e[h-2].node.href=f}function sh_konquerorExec(c){var d=[""];d.index=c.length;d.input=c;return d}function sh_highlightString(X,ah){if(/Konqueror/.test(navigator.userAgent)){if(!ah.konquered){for(var T=0;TQ){ab(ao.substring(Q,U.index),null)}var aq=a[ae];var P=aq[1];var au;if(P instanceof Array){for(var r=0;r0){var h=f.split(" ");for(var j=0;j0){g.push(h[j])}}}return g}function sh_addClass(h,f){var g=sh_getClasses(h);for(var e=0;e element with class="'+a+'", but no such language exists');continue}}break}}}if(!this.sh_languages){this.sh_languages={}}sh_languages.c=[[[/\/\/\//g,"sh_comment",1],[/\/\//g,"sh_comment",7],[/\/\*\*/g,"sh_comment",8],[/\/\*/g,"sh_comment",9],[/(\bstruct)([ \t]+)([A-Za-z0-9_]+)/g,["sh_keyword","sh_normal","sh_classname"],-1],[/^[ \t]*#(?:[ \t]*include)/g,"sh_preproc",10,1],[/^[ \t]*#(?:[ \t]*[A-Za-z0-9_]*)/g,"sh_preproc",-1],[/\b[+-]?(?:(?:0x[A-Fa-f0-9]+)|(?:(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?))u?(?:(?:int(?:8|16|32|64))|L)?\b/g,"sh_number",-1],[/"/g,"sh_string",13],[/'/g,"sh_string",14],[/\b(?:__asm|__cdecl|__declspec|__export|__far16|__fastcall|__fortran|__import|__pascal|__rtti|__stdcall|_asm|_cdecl|__except|_export|_far16|_fastcall|__finally|_fortran|_import|_pascal|_stdcall|__thread|__try|asm|auto|break|case|catch|cdecl|const|continue|default|do|else|enum|extern|for|goto|if|pascal|register|return|sizeof|static|struct|switch|typedef|union|volatile|while)\b/g,"sh_keyword",-1],[/\b(?:bool|char|double|float|int|long|short|signed|unsigned|void|wchar_t)\b/g,"sh_type",-1],[/~|!|%|\^|\*|\(|\)|-|\+|=|\[|\]|\\|:|;|,|\.|\/|\?|&|<|>|\|/g,"sh_symbol",-1],[/\{|\}/g,"sh_cbracket",-1],[/(?:[A-Za-z]|_)[A-Za-z0-9_]*(?=[ \t]*\()/g,"sh_function",-1],[/([A-Za-z](?:[^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]|[_])*)((?:<.*>)?)(\s+(?=[*&]*[A-Za-z][^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]*\s*[`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\[\]]+))/g,["sh_usertype","sh_usertype","sh_normal"],-1]],[[/$/g,null,-2],[/(?:?)|(?:?)/g,"sh_url",-1],[/<\?xml/g,"sh_preproc",2,1],[//g,"sh_keyword",-1],[/<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)/g,"sh_keyword",6,1],[/&(?:[A-Za-z0-9]+);/g,"sh_preproc",-1],[/<(?:\/)?[A-Za-z][A-Za-z0-9]*(?:\/)?>/g,"sh_keyword",-1],[/<(?:\/)?[A-Za-z][A-Za-z0-9]*/g,"sh_keyword",6,1],[/@[A-Za-z]+/g,"sh_type",-1],[/(?:TODO|FIXME|BUG)(?:[:]?)/g,"sh_todo",-1]],[[/\?>/g,"sh_preproc",-2],[/([^=" \t>]+)([ \t]*)(=?)/g,["sh_type","sh_normal","sh_symbol"],-1],[/"/g,"sh_string",3]],[[/\\(?:\\|")/g,null,-1],[/"/g,"sh_string",-2]],[[/>/g,"sh_preproc",-2],[/([^=" \t>]+)([ \t]*)(=?)/g,["sh_type","sh_normal","sh_symbol"],-1],[/"/g,"sh_string",3]],[[/-->/g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[//g,"sh_comment",-2],[/