1/** 2 * This jQuery plugin displays pagination links inside the selected elements. 3 * 4 * @author Gabriel Birke (birke *at* d-scribe *dot* de) 5 * @version 1.2 6 * @param {int} maxentries Number of entries to paginate 7 * @param {Object} opts Several options (see README for documentation) 8 * @return {Object} jQuery Object 9 */ 10jQuery.fn.pagination = function(maxentries, opts) { 11 opts = jQuery.extend({ 12 items_per_page: 10, 13 num_display_entries: 10, 14 current_page: 0, 15 num_edge_entries: 0, 16 link_to: "#", 17 prev_text: "Prev", 18 next_text: "Next", 19 ellipse_text: "...", 20 prev_show_always: true, 21 next_show_always: true, 22 data_source: '', 23 listing_container: '', 24 25 callback: function() { 26 return false; 27 } 28 }, opts || {}); 29 30 return this.each(function() { 31 /** 32 * Calculate the maximum number of pages 33 */ 34 function numPages() { 35 return Math.ceil(maxentries / opts.items_per_page); 36 } 37 38 /** 39 * Calculate start and end point of pagination links depending on 40 * current_page and num_display_entries. 41 * @return {Array} 42 */ 43 function getInterval() { 44 var ne_half = Math.ceil(opts.num_display_entries / 2); 45 var np = numPages(); 46 var upper_limit = np - opts.num_display_entries; 47 var start = current_page > ne_half ? Math.max(Math.min(current_page - ne_half, upper_limit), 0) : 0; 48 var end = current_page > ne_half ? Math.min(current_page + ne_half, np) : Math.min(opts.num_display_entries, np); 49 return [start, end]; 50 } 51 52 /** 53 * This is the event handling function for the pagination links. 54 * @param {int} page_id The new page number 55 */ 56 function pageSelected(page_id, evt) { 57 current_page = page_id; 58 drawLinks(); 59 var continuePropagation = opts.callback(page_id, panel); 60 if (!continuePropagation) { 61 if (evt.stopPropagation) { 62 evt.stopPropagation(); 63 } else { 64 evt.cancelBubble = true; 65 } 66 } 67 return continuePropagation; 68 } 69 70 /** 71 * This function inserts the pagination links into the container element 72 */ 73 function drawLinks() { 74 panel.empty(); 75 var interval = getInterval(); 76 var np = numPages(); 77 // This helper function returns a handler function that calls pageSelected with the right page_id 78 var getClickHandler = function(page_id) { 79 return function(evt) { 80 return pageSelected(page_id, evt); 81 } 82 } 83 // Helper function for generating a single link (or a span tag if it's the current page) 84 var appendItem = function(page_id, appendopts) { 85 page_id = page_id < 0 ? 0 : (page_id < np ? page_id : np - 1); // Normalize page id to sane value 86 appendopts = jQuery.extend({ 87 text: page_id + 1, 88 classes: "" 89 }, appendopts || {}); 90 if (page_id == current_page) { 91 var lnk = jQuery("<span class='current'>" + (appendopts.text) + "</span>"); 92 } else { 93 var lnk = jQuery("<a>" + (appendopts.text) + "</a>") 94 .bind("click", getClickHandler(page_id)) 95 .attr('href', opts.link_to.replace(/__id__/, page_id)); 96 97 98 } 99 if (appendopts.classes) { 100 lnk.addClass(appendopts.classes); 101 } 102 panel.append(lnk); 103 } 104 // Generate "Previous"-Link 105 if (opts.prev_text && (current_page > 0 || opts.prev_show_always)) { 106 appendItem(current_page - 1, { 107 text: opts.prev_text, 108 classes: "prev" 109 }); 110 } 111 // Generate starting points 112 if (interval[0] >
112 0 && opts.num_edge_entries > 0) { 113 var end = Math.min(opts.num_edge_entries, interval[0]); 114 for (var i = 0; i < end; i++) { 115 appendItem(i); 116 } 117 if (opts.num_edge_entries < interval[0] && opts.ellipse_text) { 118 jQuery("<span>" + opts.ellipse_text + "</span>").appendTo(panel); 119 } 120 } 121 // Generate interval links 122 for (var i = interval[0]; i < interval[1]; i++) { 123 appendItem(i); 124 } 125 // Generate ending points 126 if (interval[1] < np && opts.num_edge_entries > 0) { 127 if (np - opts.num_edge_entries > interval[1] && opts.ellipse_text) { 128 jQuery("<span>" + opts.ellipse_text + "</span>").appendTo(panel); 129 } 130 var begin = Math.max(np - opts.num_edge_entries, interval[1]); 131 for (var i = begin; i < np; i++) { 132 appendItem(i); 133 } 134 135 } 136 // Generate "Next"-Link 137 if (opts.next_text && (current_page < np - 1 || opts.next_show_always)) { 138 appendItem(current_page + 1, { 139 text: opts.next_text, 140 classes: "next" 141 }); 142 } 143 } 144 145 // Extract current_page from options 146 var current_page = opts.current_page; 147 // Create a sane value for maxentries and items_per_page 148 maxentries = (!maxentries || maxentries < 0) ? 1 : maxentries; 149 opts.items_per_page = (!opts.items_per_page || opts.items_per_page < 0) ? 1 : opts.items_per_page; 150 // Store DOM element for easy access from all inner functions 151 var panel = jQuery(this); 152 // Attach control functions to the DOM element 153 this.selectPage = function(page_id) { 154 pageSelected(page_id); 155 } 156 this.prevPage = function() { 157 if (current_page > 0) { 158 pageSelected(current_page - 1); 159 return true; 160 } else { 161 return false; 162 } 163 } 164 this.nextPage = function() { 165 if (current_page < numPages() - 1) { 166 pageSelected(current_page + 1); 167 return true; 168 } else { 169 return false; 170 } 171 } 172 // When all initialisation is done, draw the links 173 drawLinks(); 174 // call callback function 175 opts.callback(current_page, this); 176 }); 177} 178 179 180/** 181 * @name MarkerClustererPlus for Google Maps V3 182 * @version 2.1.2 [May 28, 2014] 183 * @author Gary Little 184 * @fileoverview 185 * The library creates and manages per-zoom-level clusters for large amounts of markers. 186 * <p> 187 * This is an enhanced V3 implementation of the 188 * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/" 189 * >V2 MarkerClusterer</a> by Xiaoxi Wu. It is based on the 190 * <a href="http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerclusterer/" 191 * >V3 MarkerClusterer</a> port by Luke Mahe. MarkerClustererPlus was created by Gary Little. 192 * <p> 193 * v2.0 release: MarkerClustererPlus v2.0 is backward compatible with MarkerClusterer v1.0. It 194 * adds support for the <code>ignoreHidden</code>, <code>title</code>, <code>batchSizeIE</code>, 195 * and <code>calculator</code> properties as well as support for four more events. It also allows 196 * greater control over the styling of the text that appears on the cluster marker. The 197 * documentation has been significantly improved and the overall code has been simplified and 198 * polished. Very large numbers of markers can now be managed without causing Javascript timeout 199 * errors on Internet Explorer. Note that the name of the <code>clusterclick</code> event has been 200 * deprecated. The new name is <code>click</code>, so please change your application code now. 201 */ 202 203/** 204 * Licensed under the Apache License, Version 2.0 (the "License"); 205 * you may not use this file except in compliance with the License. 206 * You may obtain a copy of the License at 207 * 208 * http://www.apache.org/licenses/LICENSE-2.0 209 * 210 * Unless required by applicable law or agreed to in writing, softw
210are 211 * distributed under the License is distributed on an "AS IS" BASIS, 212 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 213 * See the License for the specific language governing permissions and 214 * limitations under the License. 215 */ 216 217 218/** 219 * @name ClusterIconStyle 220 * @class This class represents the object for values in the <code>styles</code> array passed 221 * to the {@link MarkerClusterer} constructor. The element in this array that is used to 222 * style the cluster icon is determined by calling the <code>calculator</code> function. 223 * 224 * @property {string} url The URL of the cluster icon image file. Required. 225 * @property {number} height The display height (in pixels) of the cluster icon. Required. 226 * @property {number} width The display width (in pixels) of the cluster icon. Required. 227 * @property {Array} [anchorText] The position (in pixels) from the center of the cluster icon to 228 * where the text label is to be centered and drawn. The format is <code>[yoffset, xoffset]</code> 229 * where <code>yoffset</code> increases as you go down from center and <code>xoffset</code> 230 * increases to the right of center. The default is <code>[0, 0]</code>. 231 * @property {Array} [anchorIcon] The anchor position (in pixels) of the cluster icon. This is the 232 * spot on the cluster icon that is to be aligned with the cluster position. The format is 233 * <code>[yoffset, xoffset]</code> where <code>yoffset</code> increases as you go down and 234 * <code>xoffset</code> increases to the right of the top-left corner of the icon. The default 235 * anchor position is the center of the cluster icon. 236 * @property {string} [textColor="black"] The color of the label text shown on the 237 * cluster icon. 238 * @property {number} [textSize=11] The size (in pixels) of the label text shown on the 239 * cluster icon. 240 * @property {string} [textDecoration="none"] The value of the CSS <code>text-decoration</code> 241 * property for the label text shown on the cluster icon. 242 * @property {string} [fontWeight="bold"] The value of the CSS <code>font-weight</code> 243 * property for the label text shown on the cluster icon. 244 * @property {string} [fontStyle="normal"] The value of the CSS <code>font-style</code> 245 * property for the label text shown on the cluster icon. 246 * @property {string} [fontFamily="Arial,sans-serif"] The value of the CSS <code>font-family</code> 247 * property for the label text shown on the cluster icon. 248 * @property {string} [backgroundPosition="0 0"] The position of the cluster icon image 249 * within the image defined by <code>url</code>. The format is <code>"xpos ypos"</code> 250 * (the same format as for the CSS <code>background-position</code> property). You must set 251 * this property appropriately when the image defined by <code>url</code> represents a sprite 252 * containing multiple images. Note that the position <i>must</i> be specified in px units. 253 */ 254/** 255 * @name ClusterIconInfo
256 * @class This class is an object containing general information about a cluster icon. This is 257 * the object that a <code>calculator</code> function returns. 258 * 259 * @property {string} text The text of the label to be shown on the cluster icon. 260 * @property {number} index The index plus 1 of the element in the <code>styles</code> 261 * array to be used to style the cluster icon. 262 * @property {string} title The tooltip to display when the mouse moves over the cluster icon. 263 * If this value is <code>undefined</code> or <code>""</code>, <code>title</code> is set to the 264 * value of the <code>title</code> property passed to the MarkerClusterer. 265 */ 266/** 267 * A cluster icon. 268 * 269 * @constructor 270 * @extends google.maps.OverlayView 271 * @param {Cluster} cluster The cluster with which the icon is to be associated. 272 * @param {Array} [styles] An array of {@link ClusterIconStyle} defining the cluster icons 273 * to use for various cluster sizes. 274 * @private 275 */ 276function ClusterIcon(cluster, styles) { 277 cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); 278 279 this.cluster_ = cluster; 280 this.className_ = cluster.getMarkerClusterer().getClusterClass(); 281 this.styles_ = styles; 282 this.center_ = null; 283 this.div_ = null; 284 this.sums_ = null; 285 this.visible_ = false; 286 287 this.setMap(cluster.getMap()); // Note: this causes onAdd to be called 288} 289 290 291/** 292 * Adds the icon to the DOM. 293 */ 294ClusterIcon.prototype.onAdd = function() { 295 var cClusterIcon = this; 296 var cMouseDownInCluster; 297 var cDraggingMapByCluster; 298 299 this.div_ = document.createElement("div"); 300 this.div_.className = this.className_; 301 if (this.visible_) { 302 this.show(); 303 } 304 305 this.getPanes().overlayMouseTarget.appendChild(this.div_); 306 307 // Fix for Issue 157 308 this.boundsChangedListener_ = google.maps.event.addListener(this.getMap(), "bounds_changed", function() { 309 cDraggingMapByCluster = cMouseDownInCluster; 310 }); 311 312 google.maps.event.addDomListener(this.div_, "mousedown", function() { 313 cMouseDownInCluster = true; 314 cDraggingMapByCluster = false; 315 }); 316 317 google.maps.event.addDomListener(this.div_, "click", function(e) { 318 cMouseDownInCluster = false; 319 if (!cDraggingMapByCluster) { 320 var theBounds; 321 var mz; 322 var mc = cClusterIcon.cluster_.getMarkerClusterer(); 323 /** 324 * This event is fired when a cluster marker is clicked. 325 * @name MarkerClusterer#click 326 * @param {Cluster} c The cluster that was clicked. 327 * @event 328 */ 329 google.maps.event.trigger(mc, "click", cClusterIcon.cluster_); 330 google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name 331 332 // The default click handler follows. Disable it by setting 333 // the zoomOnClick property to false. 334 if (mc.getZoomOnClick()) { 335 // Zoom into the cluster. 336 mz = mc.getMaxZoom(); 337 theBounds = cClusterIcon.cluster_.getBounds(); 338 mc.getMap().fitBounds(theBounds); 339 // There is a fix for Issue 170 here: 340 setTimeout(function() { 341 mc.getMap().fitBounds(theBounds); 342 // Don't zoom beyond the max zoom level 343 if (mz !== null && (mc.getMap().getZoom() > mz)) { 344 mc.getMap().setZoom(mz + 1); 345 } 346 }, 100); 347 } 348 349 // Prevent event propagation to the map: 350 e.cancelBubble = true; 351 if (e.stopPropagation) { 352 e.stopPropagation(); 353 } 354 } 355 }); 356 357 google.maps.event.addDomListener(this.div_, "mouseover", function() { 358 var mc = cClusterIcon.cluster_.getMarkerClusterer(); 359 /** 360 * This event is fired when the mouse moves over a cluster marker. 361 * @name MarkerClusterer#mouseover 362 * @param {Cluster} c The cluster that the mouse moved over. 363 * @event 364 */ 365 google.maps.event.trigger(mc, "mouseover", cClusterIcon.cluster_); 366 }); 367 368 google.maps.event.addDomListener(this.div_, "mouseout", function() { 369 var mc = cClusterIcon.cluster_.getMarkerClusterer(); 370 /**
vendor: 14,931 bytes, lines 371-802
371 * This event is fired when the mouse moves out of a cluster marker. 372 * @name MarkerClusterer#mouseout 373 * @param {Cluster} c The cluster that the mouse moved out of. 374 * @event 375 */ 376 google.maps.event.trigger(mc, "mouseout", cClusterIcon.cluster_); 377 }); 378}; 379 380 381/** 382 * Removes the icon from the DOM. 383 */ 384ClusterIcon.prototype.onRemove = function() { 385 if (this.div_ && this.div_.parentNode) { 386 this.hide(); 387 google.maps.event.removeListener(this.boundsChangedListener_); 388 google.maps.event.clearInstanceListeners(this.div_); 389 this.div_.parentNode.removeChild(this.div_); 390 this.div_ = null; 391 } 392}; 393 394 395/** 396 * Draws the icon. 397 */ 398ClusterIcon.prototype.draw = function() { 399 if (this.visible_) { 400 var pos = this.getPosFromLatLng_(this.center_); 401 this.div_.style.top = pos.y + "px"; 402 this.div_.style.left = pos.x + "px"; 403 } 404}; 405 406 407/** 408 * Hides the icon. 409 */ 410ClusterIcon.prototype.hide = function() { 411 if (this.div_) { 412 this.div_.style.display = "none"; 413 } 414 this.visible_ = false; 415}; 416 417 418/** 419 * Positions and shows the icon. 420 */ 421ClusterIcon.prototype.show = function() { 422 if (this.div_) { 423 var img = ""; 424 // NOTE: values must be specified in px units 425 var bp = this.backgroundPosition_.split(" "); 426 var spriteH = parseInt(bp[0].replace(/^\s+|\s+$/g, ""), 10); 427 var spriteV = parseInt(bp[1].replace(/^\s+|\s+$/g, ""), 10); 428 var pos = this.getPosFromLatLng_(this.center_); 429 this.div_.style.cssText = this.createCss(pos); 430 img = "<img src='" + this.url_ + "' style='position: absolute; top: " + spriteV + "px; left: " + spriteH + "px; "; 431 if (!this.cluster_.getMarkerClusterer().enableRetinaIcons_) { 432 img += "clip: rect(" + (-1 * spriteV) + "px, " + ((-1 * spriteH) + this.width_) + "px, " + 433 ((-1 * spriteV) + this.height_) + "px, " + (-1 * spriteH) + "px);"; 434 } 435 img += "'>"; 436 this.div_.innerHTML = img + "<div style='" + 437 "position: absolute;" + 438 "top: " + this.anchorText_[0] + "px;" + 439 "left: " + this.anchorText_[1] + "px;" + 440 "color: " + this.textColor_ + ";" + 441 "font-size: " + this.textSize_ + "px;" + 442 "font-family: " + this.fontFamily_ + ";" + 443 "font-weight: " + this.fontWeight_ + ";" + 444 "font-style: " + this.fontStyle_ + ";" + 445 "text-decoration: " + this.textDecoration_ + ";" + 446 "text-align: center;" + 447 "width: " + this.width_ + "px;" + 448 "line-height:" + this.height_ + "px;" + 449 "'>" + this.sums_.text + "</div>"; 450 if (typeof this.sums_.title === "undefined" || this.sums_.title === "") { 451 this.div_.title = this.cluster_.getMarkerClusterer().getTitle(); 452 } else { 453 this.div_.title = this.sums_.title; 454 } 455 this.div_.style.display = ""; 456 } 457 this.visible_ = true; 458}; 459 460 461/** 462 * Sets the icon styles to the appropriate element in the styles array. 463 * 464 * @param {ClusterIconInfo} sums The icon label text and styles index. 465 */ 466ClusterIcon.prototype.useStyle = function(sums) { 467 this.sums_ = sums; 468 var index = Math.max(0, sums.index - 1); 469 index = Math.min(this.styles_.length - 1, index); 470 var style = this.styles_[index]; 471 this.url_ = style.url; 472 this.height_ = style.height; 473 this.width_ = style.width; 474 this.anchorText_ = style.anchorText || [0, 0]; 475 this.anchorIcon_ = style.anchorIcon || [parseInt(this.height_ / 2, 10), parseInt(this.width_ / 2, 10)]; 476 this.textColor_ = style.textColor || "black"; 477 this.textSize_ = style.textSize || 11; 478 this.textDecoration_ = style.textDecoration || "none"; 479 this.fontWeight_ = style.fontWeight || "bold"; 480 this.fontStyle_ = style.fontStyle || "normal"; 481 this.fontFamily_ = style.fontFamily || "Arial,sans-serif"; 482 this.backgroundPosition_ = style.backgroundPosition || "0 0"; 483}; 484 485 486/** 487 * Sets the position at which to center the icon. 488 * 489 * @param {google.maps.LatLng} center The latlng to set as the center. 490 */ 491ClusterIcon.prototype.setCenter = function(center) { 492 this.center_ = center; 493}; 494 495 496/** 497 * Creates the cssText style parameter based on the position of the icon. 498 * 499 * @param {google.maps.Point} pos The position of the icon. 500 * @return {string} The CSS style text. 501 */ 502ClusterIcon.prototype.createCss = function(pos) { 503 var style = []; 504 style.push("cursor: pointer;"); 505 style.push("position: absolute; top: " + pos.y + "px; left: " + pos.x + "px;"); 506 style.push("width: " + this.width_ + "px; height: " + this.height_ + "px;"); 507 return style.join(""); 508}; 509 510 511/** 512 * Returns the position at which to place the DIV depending on the latlng. 513 * 514 * @param {google.maps.LatLng} latlng The position in latlng. 515 * @return {google.maps.Point} The position in pixels. 516 */ 517ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) { 518 var pos = this.getProjection().fromLatLngToDivPixel(latlng); 519 pos.x -= this.anchorIcon_[1]; 520 pos.y -= this.anchorIcon_[0]; 521 pos.x = parseInt(pos.x, 10); 522 pos.y = parseInt(pos.y, 10); 523 return pos; 524}; 525 526 527/** 528 * Creates a single cluster that manages a group of proximate markers. 529 * Used internally, do not call this constructor directly. 530 * @constructor 531 * @param {MarkerClusterer} mc The <code>MarkerClusterer</code> object with which this 532 * cluster is associated. 533 */ 534function Cluster(mc) { 535 this.markerClusterer_ = mc; 536 this.map_ = mc.getMap(); 537 this.gridSize_ = mc.getGridSize(); 538 this.minClusterSize_ = mc.getMinimumClusterSize(); 539 this.averageCenter_ = mc.getAverageCenter(); 540 this.markers_ = []; 541 this.center_ = null; 542 this.bounds_ = null; 543 this.clusterIcon_ = new ClusterIcon(this, mc.getStyles()); 544} 545 546 547/** 548 * Returns the number of markers managed by the cluster. You can call this from 549 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler 550 * for the <code>MarkerClusterer</code> object. 551 * 552 * @return {number} The number of markers in the cluster. 553 */ 554Cluster.prototype.getSize = function() { 555 return this.markers_.length; 556}; 557 558 559/** 560 * Returns the array of markers managed by the cluster. You can call this from 561 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler 562 * for the <code>MarkerClusterer</code> object. 563 * 564 * @return {Array} The array of markers in the cluster. 565 */ 566Cluster.prototype.getMarkers = function() { 567 return this.markers_; 568}; 569 570 571/** 572 * Returns the center of the cluster. You can call this from 573 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler 574 * for the <code>MarkerClusterer</code> object. 575 * 576 * @return {google.maps.LatLng} The center of the cluster. 577 */ 578Cluster.prototype.getCenter = function() { 579 return this.center_; 580}; 581 582 583/** 584 * Returns the map with which the cluster is associated. 585 * 586 * @return {google.maps.Map} The map. 587 * @ignore 588 */ 589Cluster.prototype.getMap = function() { 590 return this.map_; 591}; 592 593 594/** 595 * Returns the <code>MarkerClusterer</code> object with which the cluster is associated. 596 * 597 * @return {MarkerClusterer} The associated marker clusterer. 598 * @ignore 599 */ 600Cluster.prototype.getMarkerClusterer = function() { 601 return this.markerClusterer_; 602}; 603 604 605/** 606 * Returns the bounds of the cluster. 607 * 608 * @return {google.maps.LatLngBounds} the cluster bounds. 609 * @ignore 610 */ 611Cluster.prototype.getBounds = function() { 612 var i; 613 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 614 var markers = this.getMarkers(); 615 for (i = 0; i < markers.length; i++) { 616 bounds.extend(markers[i].getPosition()); 617 } 618 return bounds; 619}; 620 621 622/** 623 * Removes the cluster from the map. 624 * 625 * @ignore 626 */ 627Cluster.prototype.remove = function() { 628 this.clusterIcon_.setMap(null); 629 this.markers_ = []; 630 delete this.markers_; 631}; 632 633 634/** 635 * Adds a marker to the cluster. 636 * 637 * @param {google.maps.Marker} marker The marker to be added. 638 * @return {boolean} True if the marker was added. 639 * @ignore 640 */ 641Cluster.prototype.addMarker = function(marker) { 642 var i; 643 var mCount; 644 var mz; 645 646 if (this.isMarkerAlreadyAdded_(marker)) { 647 return false; 648 } 649 650 if (!this.center_) { 651 this.center_ = marker.getPosition(); 652 this.calculateBounds_(); 653 } else { 654 if (this.averageCenter_) { 655 var l = this.markers_.length + 1; 656 var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l; 657 var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l; 658 this.center_ = new google.maps.LatLng(lat, lng); 659 this.calculateBounds_(); 660 } 661 } 662 663 marker.isAdded = true; 664 this.markers_.push(marker); 665 666 mCount = this.markers_.length; 667 mz = this.markerClusterer_.getMaxZoom(); 668 if (mz !== null && this.map_.getZoom() > mz) { 669 // Zoomed in past max zoom, so show the marker. 670 if (marker.getMap() !== this.map_) { 671 marker.setMap(this.map_); 672 } 673 } else if (mCount < this.minClusterSize_) { 674 // Min cluster size not reached so show the marker. 675 if (marker.getMap() !== this.map_) { 676 marker.setMap(this.map_); 677 } 678 } else if (mCount === this.minClusterSize_) { 679 // Hide the markers that were showing. 680 for (i = 0; i < mCount; i++) { 681 this.markers_[i].setMap(null); 682 } 683 } else { 684 marker.setMap(null); 685 } 686 687 this.updateIcon_(); 688 return true; 689}; 690 691 692/** 693 * Determines if a marker lies within the cluster's bounds. 694 * 695 * @param {google.maps.Marker} marker The marker to check. 696 * @return {boolean} True if the marker lies in the bounds. 697 * @ignore 698 */ 699Cluster.prototype.isMarkerInClusterBounds = function(marker) { 700 return this.bounds_.contains(marker.getPosition()); 701}; 702 703 704/** 705 * Calculates the extended bounds of the cluster with the grid. 706 */ 707Cluster.prototype.calculateBounds_ = function() { 708 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 709 this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); 710}; 711 712 713/** 714 * Updates the cluster icon. 715 */ 716Cluster.prototype.updateIcon_ = function() { 717 var mCount = this.markers_.length; 718 var mz = this.markerClusterer_.getMaxZoom(); 719 720 if (mz !== null && this.map_.getZoom() > mz) { 721 this.clusterIcon_.hide(); 722 return; 723 } 724 725 if (mCount < this.minClusterSize_) { 726 // Min cluster size not yet reached. 727 this.clusterIcon_.hide(); 728 return; 729 } 730 731 var numStyles = this.markerClusterer_.getStyles().length; 732 var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); 733 this.clusterIcon_.setCenter(this.center_); 734 this.clusterIcon_.useStyle(sums); 735 this.clusterIcon_.show(); 736}; 737 738 739/** 740 * Determines if a marker has already been added to the cluster. 741 * 742 * @param {google.maps.Marker} marker The marker to check. 743 * @return {boolean} True if the marker has already been added. 744 */ 745Cluster.prototype.isMarkerAlreadyAdded_ = function(marker) { 746 var i; 747 if (this.markers_.indexOf) { 748 return this.markers_.indexOf(marker) !== -1; 749 } else { 750 for (i = 0; i < this.markers_.length; i++) { 751 if (marker === this.markers_[i]) { 752 return true; 753 } 754 } 755 } 756 return false; 757}; 758 759 760/** 761 * @name MarkerClustererOptions 762 * @class This class represents the optional parameter passed to 763 * the {@link MarkerClusterer} constructor. 764 * @property {number} [gridSize=60] The grid size of a cluster in pixels. The grid is a square. 765 * @property {number} [maxZoom=null] The maximum zoom level at which clustering is enabled or 766 * <code>null</code> if clustering is to be enabled at all zoom levels. 767 * @property {boolean} [zoomOnClick=true] Whether to zoom the map when a cluster marker is 768 * clicked. You may want to set this to <code>false</code> if you have installed a handler 769 * for the <code>click</code> event and it deals with zooming on its own. 770 * @property {boolean} [averageCenter=false] Whether the position of a cluster marker should be 771 * the average position of all markers in the cluster. If set to <code>false</code>, the 772 * cluster marker is positioned at the location of the first marker added to the cluster. 773 * @property {number} [minimumClusterSize=2] The minimum number of markers needed in a cluster 774 * before the markers are hidden and a cluster marker appears. 775 * @property {boolean} [ignoreHidden=false] Whether to ignore hidden markers in clusters. You 776 * may want to set this to <code>true</code> to ensure that hidden markers are not included 777 * in the marker count that appears on a cluster marker (this count is the value of the 778 * <code>text</code> property of the result returned by the default <code>calculator</code>). 779 * If set to <code>true</code> and you change the visibility of a marker being clustered, be 780 * sure to also call <code>MarkerClusterer.repaint()</code>. 781 * @property {string} [title=""] The tooltip to display when the mouse moves over a cluster 782 * marker. (Alternatively, you can use a custom <code>calculator</code> function to specify a 783 * different tooltip for each cluster marker.) 784 * @property {function} [calculator=MarkerClusterer.CALCULATOR] The function used to determine 785 * the text to be displayed on a cluster marker and the index indicating which style to use 786 * for the cluster marker. The input parameters for the function are (1) the array of markers 787 * represented by a cluster marker and (2) the number of cluster icon styles. It returns a 788 * {@link ClusterIconInfo} object. The default <code>calculator</code> returns a 789 * <code>text</code> property which is the number of markers in the cluster and an 790 * <code>index</code> property which is one higher than the lowest integer such that 791 * <code>10^i</code> exceeds the number of markers in the cluster, or the size of the styles 792 * array, whichever is less. The <code>styles</code> array element used has an index of 793 * <code>index</code> minus 1. For example, the default <code>calculator</code> returns a 794 * <code>text</code> value of <code>"125"</code> and an <code>index</code> of <code>3</code> 795 * for a cluster icon representing 125 markers so the element used in the <code>styles</code> 796 * array is <code>2</code>. A <code>calculator</code> may also return a <code>title</code> 797 * property that contains the text of the tooltip to be used for the cluster marker. If 798 * <code>title</code> is not defined, the tooltip is set to the value of the <code>title</code> 799 * property for the MarkerClusterer. 800 * @property {string} [clusterClass="cluster"] The name of the CSS class defining general styles 801 * for the cluster markers. Use this class to define CSS styles that are not set up by the code 802 * that processes the <code>styles</code> array.
803 * @property {Array} [styles] An array of {@link ClusterIconStyle} elements defining the styles 804 * of the cluster markers to be used. The element to be used to style a given cluster marker 805 * is determined by the function defined by the <code>calculator</code> property. 806 * The default is an array of {@link ClusterIconStyle} elements whose properties are derived 807 * from the values for <code>imagePath</code>, <code>imageExtension</code>, and 808 * <code>imageSizes</code>. 809 * @property {boolean} [enableRetinaIcons=false] Whether to allow the use of cluster icons that 810 * have sizes that are some multiple (typically double) of their actual display size. Icons such 811 * as these look better when viewed on high-resolution monitors such as Apple's Retina displays. 812 * Note: if this property is <code>true</code>, sprites cannot be used as cluster icons. 813 * @property {number} [batchSize=MarkerClusterer.BATCH_SIZE] Set this property to the 814 * number of markers to be processed in a single batch when using a browser other than 815 * Internet Explorer (for Internet Explorer, use the batchSizeIE property instead). 816 * @property {number} [batchSizeIE=MarkerClusterer.BATCH_SIZE_IE] When Internet Explorer is 817 * being used, markers are processed in several batches with a small delay inserted between 818 * each batch in an attempt to avoid Javascript timeout errors. Set this property to the 819 * number of markers to be processed in a single batch; select as high a number as you can 820 * without causing a timeout error in the browser. This number might need to be as low as 100 821 * if 15,000 markers are being managed, for example. 822 * @property {string} [imagePath=MarkerClusterer.IMAGE_PATH] 823 * The full URL of the root name of the group of image files to use for cluster icons. 824 * The complete file name is of the form <code>imagePath</code>n.<code>imageExtension</code> 825 * where n is the image file number (1, 2, etc.). 826 * @property {string} [imageExtension=MarkerClusterer.IMAGE_EXTENSION] 827 * The extension name for the cluster icon image files (e.g., <code>"png"</code> or 828 * <code>"jpg"</code>). 829 * @property {Array} [imageSizes=MarkerClusterer.IMAGE_SIZES] 830 * An array of numbers containing the widths of the group of 831 * <code>imagePath</code>n.<code>imageExtension</code> image files. 832 * (The images are assumed to be square.) 833 */ 834/** 835 * Creates a MarkerClusterer object with the options specified in {@link MarkerClustererOptions}. 836 * @constructor 837 * @extends google.maps.OverlayView 838 * @param {google.maps.Map} map The Google map to attach to. 839 * @param {Array.<google.maps.Marker>} [opt_markers] The markers to be added to the cluster. 840 * @param {MarkerClustererOptions} [opt_options] The optional parameters. 841 */ 842function MarkerClusterer(map, opt_markers, opt_options) { 843 // MarkerClusterer implements google.maps.OverlayView interface. We use the 844 // extend function to extend MarkerClusterer with google.maps.OverlayView 845 // because it might not always be available when the code is defined so we 846 // look for it at the last possible moment. If it doesn't exist now then 847 // there is no point going ahead :) 848 this.extend(MarkerClusterer, google.maps.OverlayView); 849 850 opt_markers = opt_markers || []; 851 opt_options = opt_options || {}; 852 853 this.markers_ = []; 854 this.clusters_ = []; 855 this.listeners_ = []; 856 this.activeMap_ = null; 857 this.ready_ = false; 858 859 this.gridSize_ = opt_options.gridSize || 60; 860 this.minClusterSize_ = opt_options.minimumClusterSize || 2; 861 this.maxZoom_ = opt_options.maxZoom || null; 862 this.styles_ = opt_options.styles || []; 863 this.title_ = opt_options.title || ""; 864 this.zoomOnClick_ = true; 865 if (opt_options.zoomOnClick !== undefined) { 866 this.zoomOnClick_ = opt_options.zoomOnClick; 867 } 868 this.averageCenter_ = false; 869 if (opt_options.averageCenter !== undefined) { 870 this.averageCenter_ = opt_options.averageCenter; 871 } 872 this.ignoreHidden_ = false; 873 if (opt_options.ignoreHidden !== undefined) { 874 this.ignoreHidden_ = opt_options.ignoreHidden; 875 } 876 this.enableRetinaIcons_ = false;
vendor: 15,608 bytes, lines 877-1471
877 if (opt_options.enableRetinaIcons !== undefined) { 878 this.enableRetinaIcons_ = opt_options.enableRetinaIcons; 879 } 880 this.imagePath_ = opt_options.imagePath || MarkerClusterer.IMAGE_PATH; 881 this.imageExtension_ = opt_options.imageExtension || MarkerClusterer.IMAGE_EXTENSION; 882 this.imageSizes_ = opt_options.imageSizes || MarkerClusterer.IMAGE_SIZES; 883 this.calculator_ = opt_options.calculator || MarkerClusterer.CALCULATOR; 884 this.batchSize_ = opt_options.batchSize || MarkerClusterer.BATCH_SIZE; 885 this.batchSizeIE_ = opt_options.batchSizeIE || MarkerClusterer.BATCH_SIZE_IE; 886 this.clusterClass_ = opt_options.clusterClass || "cluster"; 887 888 if (navigator.userAgent.toLowerCase().indexOf("msie") !== -1) { 889 // Try to avoid IE timeout when processing a huge number of markers: 890 this.batchSize_ = this.batchSizeIE_; 891 } 892 893 this.setupStyles_(); 894 895 this.addMarkers(opt_markers, true); 896 this.setMap(map); // Note: this causes onAdd to be called 897} 898 899 900/** 901 * Implementation of the onAdd interface method. 902 * @ignore 903 */ 904MarkerClusterer.prototype.onAdd = function() { 905 var cMarkerClusterer = this; 906 907 this.activeMap_ = this.getMap(); 908 this.ready_ = true; 909 910 this.repaint(); 911 912 // Add the map event listeners 913 this.listeners_ = [ 914 google.maps.event.addListener(this.getMap(), "zoom_changed", function() { 915 cMarkerClusterer.resetViewport_(false); 916 // Workaround for this Google bug: when map is at level 0 and "-" of 917 // zoom slider is clicked, a "zoom_changed" event is fired even though 918 // the map doesn't zoom out any further. In this situation, no "idle" 919 // event is triggered so the cluster markers that have been removed 920 // do not get redrawn. Same goes for a zoom in at maxZoom. 921 if (this.getZoom() === (this.get("minZoom") || 0) || this.getZoom() === this.get("maxZoom")) { 922 google.maps.event.trigger(this, "idle"); 923 } 924 }), 925 google.maps.event.addListener(this.getMap(), "idle", function() { 926 cMarkerClusterer.redraw_(); 927 }) 928 ]; 929}; 930 931 932/** 933 * Implementation of the onRemove interface method. 934 * Removes map event listeners and all cluster icons from the DOM. 935 * All managed markers are also put back on the map. 936 * @ignore 937 */ 938MarkerClusterer.prototype.onRemove = function() { 939 var i; 940 941 // Put all the managed markers back on the map: 942 for (i = 0; i < this.markers_.length; i++) { 943 if (this.markers_[i].getMap() !== this.activeMap_) { 944 this.markers_[i].setMap(this.activeMap_); 945 } 946 } 947 948 // Remove all clusters: 949 for (i = 0; i < this.clusters_.length; i++) { 950 this.clusters_[i].remove(); 951 } 952 this.clusters_ = []; 953 954 // Remove map event listeners: 955 for (i = 0; i < this.listeners_.length; i++) { 956 google.maps.event.removeListener(this.listeners_[i]); 957 } 958 this.listeners_ = []; 959 960 this.activeMap_ = null; 961 this.ready_ = false; 962}; 963 964 965/** 966 * Implementation of the draw interface method. 967 * @ignore 968 */ 969MarkerClusterer.prototype.draw = function() {}; 970 971 972/** 973 * Sets up the styles object. 974 */ 975MarkerClusterer.prototype.setupStyles_ = function() { 976 var i, size; 977 if (this.styles_.length > 0) { 978 return; 979 } 980 981 for (i = 0; i < this.imageSizes_.length; i++) { 982 size = this.imageSizes_[i]; 983 this.styles_.push({ 984 url: this.imagePath_ + (i + 1) + "." + this.imageExtension_, 985 height: size, 986 width: size 987 }); 988 } 989}; 990 991 992/** 993 * Fits the map to the bounds of the markers managed by the clusterer. 994 */ 995MarkerClusterer.prototype.fitMapToMarkers = function() { 996 var i; 997 var markers = this.getMarkers(); 998 var bounds = new google.maps.LatLngBounds(); 999 for (i = 0; i < markers.length; i++) { 1000 bounds.extend(markers[i].getPosition()); 1001 } 1002 1003 this.getMap().fitBounds(bounds); 1004}; 1005 1006 1007/** 1008 * Returns the value of the <code>gridSize</code> property. 1009 * 1010 * @return {number} The grid size. 1011 */ 1012MarkerClusterer.prototype.getGridSize = function() { 1013 return this.gridSize_; 1014}; 1015 1016 1017/** 1018 * Sets the value of the <code>gridSize</code> property. 1019 * 1020 * @param {number} gridSize The grid size. 1021 */ 1022MarkerClusterer.prototype.setGridSize = function(gridSize) { 1023 this.gridSize_ = gridSize; 1024}; 1025 1026 1027/** 1028 * Returns the value of the <code>minimumClusterSize</code> property. 1029 * 1030 * @return {number} The minimum cluster size. 1031 */ 1032MarkerClusterer.prototype.getMinimumClusterSize = function() { 1033 return this.minClusterSize_; 1034}; 1035 1036/** 1037 * Sets the value of the <code>minimumClusterSize</code> property. 1038 * 1039 * @param {number} minimumClusterSize The minimum cluster size. 1040 */ 1041MarkerClusterer.prototype.setMinimumClusterSize = function(minimumClusterSize) { 1042 this.minClusterSize_ = minimumClusterSize; 1043}; 1044 1045 1046/** 1047 * Returns the value of the <code>maxZoom</code> property. 1048 * 1049 * @return {number} The maximum zoom level. 1050 */ 1051MarkerClusterer.prototype.getMaxZoom = function() { 1052 return this.maxZoom_; 1053}; 1054 1055 1056/** 1057 * Sets the value of the <code>maxZoom</code> property. 1058 * 1059 * @param {number} maxZoom The maximum zoom level. 1060 */ 1061MarkerClusterer.prototype.setMaxZoom = function(maxZoom) { 1062 this.maxZoom_ = maxZoom; 1063}; 1064 1065 1066/** 1067 * Returns the value of the <code>styles</code> property. 1068 * 1069 * @return {Array} The array of styles defining the cluster markers to be used. 1070 */ 1071MarkerClusterer.prototype.getStyles = function() { 1072 return this.styles_; 1073}; 1074 1075 1076/** 1077 * Sets the value of the <code>styles</code> property. 1078 * 1079 * @param {Array.<ClusterIconStyle>} styles The array of styles to use. 1080 */ 1081MarkerClusterer.prototype.setStyles = function(styles) { 1082 this.styles_ = styles; 1083}; 1084 1085 1086/** 1087 * Returns the value of the <code>title</code> property. 1088 * 1089 * @return {string} The content of the title text. 1090 */ 1091MarkerClusterer.prototype.getTitle = function() { 1092 return this.title_; 1093}; 1094 1095 1096/** 1097 * Sets the value of the <code>title</code> property. 1098 * 1099 * @param {string} title The value of the title property. 1100 */ 1101MarkerClusterer.prototype.setTitle = function(title) { 1102 this.title_ = title; 1103}; 1104 1105 1106/** 1107 * Returns the value of the <code>zoomOnClick</code> property. 1108 * 1109 * @return {boolean} True if zoomOnClick property is set. 1110 */ 1111MarkerClusterer.prototype.getZoomOnClick = function() { 1112 return this.zoomOnClick_; 1113}; 1114 1115 1116/** 1117 * Sets the value of the <code>zoomOnClick</code> property. 1118 * 1119 * @param {boolean} zoomOnClick The value of the zoomOnClick property. 1120 */ 1121MarkerClusterer.prototype.setZoomOnClick = function(zoomOnClick) { 1122 this.zoomOnClick_ = zoomOnClick; 1123}; 1124 1125 1126/** 1127 * Returns the value of the <code>averageCenter</code> property. 1128 * 1129 * @return {boolean} True if averageCenter property is set. 1130 */ 1131MarkerClusterer.prototype.getAverageCenter = function() { 1132 return this.averageCenter_; 1133}; 1134 1135 1136/** 1137 * Sets the value of the <code>averageCenter</code> property. 1138 * 1139 * @param {boolean} averageCenter The value of the averageCenter property. 1140 */ 1141MarkerClusterer.prototype.setAverageCenter = function(averageCenter) { 1142 this.averageCenter_ = averageCenter; 1143}; 1144 1145 1146/** 1147 * Returns the value of the <code>ignoreHidden</code> property. 1148 * 1149 * @return {boolean} True if ignoreHidden property is set. 1150 */ 1151MarkerClusterer.prototype.getIgnoreHidden = function() { 1152 return this.ignoreHidden_; 1153}; 1154 1155 1156/** 1157 * Sets the value of the <code>ignoreHidden</code> property. 1158 * 1159 * @param {boolean} ignoreHidden The value of the ignoreHidden property. 1160 */ 1161MarkerClusterer.prototype.setIgnoreHidden = function(ignoreHidden) { 1162 this.ignoreHidden_ = ignoreHidden; 1163}; 1164 1165 1166/** 1167 * Returns the value of the <code>enableRetinaIcons</code> property. 1168 * 1169 * @return {boolean} True if enableRetinaIcons property is set. 1170 */ 1171MarkerClusterer.prototype.getEnableRetinaIcons = function() { 1172 return this.enableRetinaIcons_; 1173}; 1174 1175 1176/** 1177 * Sets the value of the <code>enableRetinaIcons</code> property. 1178 * 1179 * @param {boolean} enableRetinaIcons The value of the enableRetinaIcons property. 1180 */ 1181MarkerClusterer.prototype.setEnableRetinaIcons = function(enableRetinaIcons) { 1182 this.enableRetinaIcons_ = enableRetinaIcons; 1183}; 1184 1185 1186/** 1187 * Returns the value of the <code>imageExtension</code> property. 1188 * 1189 * @return {string} The value of the imageExtension property. 1190 */ 1191MarkerClusterer.prototype.getImageExtension = function() { 1192 return this.imageExtension_; 1193}; 1194 1195 1196/** 1197 * Sets the value of the <code>imageExtension</code> property. 1198 * 1199 * @param {string} imageExtension The value of the imageExtension property. 1200 */ 1201MarkerClusterer.prototype.setImageExtension = function(imageExtension) { 1202 this.imageExtension_ = imageExtension; 1203}; 1204 1205 1206/** 1207 * Returns the value of the <code>imagePath</code> property. 1208 * 1209 * @return {string} The value of the imagePath property. 1210 */ 1211MarkerClusterer.prototype.getImagePath = function() { 1212 return this.imagePath_; 1213}; 1214 1215 1216/** 1217 * Sets the value of the <code>imagePath</code> property. 1218 * 1219 * @param {string} imagePath The value of the imagePath property. 1220 */ 1221MarkerClusterer.prototype.setImagePath = function(imagePath) { 1222 this.imagePath_ = imagePath; 1223}; 1224 1225 1226/** 1227 * Returns the value of the <code>imageSizes</code> property. 1228 * 1229 * @return {Array} The value of the imageSizes property. 1230 */ 1231MarkerClusterer.prototype.getImageSizes = function() { 1232 return this.imageSizes_; 1233}; 1234 1235 1236/** 1237 * Sets the value of the <code>imageSizes</code> property. 1238 * 1239 * @param {Array} imageSizes The value of the imageSizes property. 1240 */ 1241MarkerClusterer.prototype.setImageSizes = function(imageSizes) { 1242 this.imageSizes_ = imageSizes; 1243}; 1244 1245 1246/** 1247 * Returns the value of the <code>calculator</code> property. 1248 * 1249 * @return {function} the value of the calculator property. 1250 */ 1251MarkerClusterer.prototype.getCalculator = function() { 1252 return this.calculator_; 1253}; 1254 1255 1256/** 1257 * Sets the value of the <code>calculator</code> property. 1258 * 1259 * @param {function(Array.<google.maps.Marker>, number)} calculator The value 1260 * of the calculator property. 1261 */ 1262MarkerClusterer.prototype.setCalculator = function(calculator) { 1263 this.calculator_ = calculator; 1264}; 1265 1266 1267/** 1268 * Returns the value of the <code>batchSizeIE</code> property. 1269 * 1270 * @return {number} the value of the batchSizeIE property. 1271 */ 1272MarkerClusterer.prototype.getBatchSizeIE = function() { 1273 return this.batchSizeIE_; 1274}; 1275 1276 1277/** 1278 * Sets the value of the <code>batchSizeIE</code> property. 1279 * 1280 * @param {number} batchSizeIE The value of the batchSizeIE property. 1281 */ 1282MarkerClusterer.prototype.setBatchSizeIE = function(batchSizeIE) { 1283 this.batchSizeIE_ = batchSizeIE; 1284}; 1285 1286 1287/** 1288 * Returns the value of the <code>clusterClass</code> property. 1289 * 1290 * @return {string} the value of the clusterClass property. 1291 */ 1292MarkerClusterer.prototype.getClusterClass = function() { 1293 return this.clusterClass_; 1294}; 1295 1296 1297/** 1298 * Sets the value of the <code>clusterClass</code> property. 1299 * 1300 * @param {string} clusterClass The value of the clusterClass property. 1301 */ 1302MarkerClusterer.prototype.setClusterClass = function(clusterClass) { 1303 this.clusterClass_ = clusterClass; 1304}; 1305 1306 1307/** 1308 * Returns the array of markers managed by the clusterer. 1309 * 1310 * @return {Array} The array of markers managed by the clusterer. 1311 */ 1312MarkerClusterer.prototype.getMarkers = function() { 1313 return this.markers_; 1314}; 1315 1316 1317/** 1318 * Returns the number of markers managed by the clusterer. 1319 * 1320 * @return {number} The number of markers. 1321 */ 1322MarkerClusterer.prototype.getTotalMarkers = function() { 1323 return this.markers_.length; 1324}; 1325 1326 1327/** 1328 * Returns the current array of clusters formed by the clusterer. 1329 * 1330 * @return {Array} The array of clusters formed by the clusterer. 1331 */ 1332MarkerClusterer.prototype.getClusters = function() { 1333 return this.clusters_; 1334}; 1335 1336 1337/** 1338 * Returns the number of clusters formed by the clusterer. 1339 * 1340 * @return {number} The number of clusters formed by the clusterer. 1341 */ 1342MarkerClusterer.prototype.getTotalClusters = function() { 1343 return this.clusters_.length; 1344}; 1345 1346 1347/** 1348 * Adds a marker to the clusterer. The clusters are redrawn unless 1349 * <code>opt_nodraw</code> is set to <code>true</code>. 1350 * 1351 * @param {google.maps.Marker} marker The marker to add. 1352 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1353 */ 1354MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) { 1355 this.pushMarkerTo_(marker); 1356 if (!opt_nodraw) { 1357 this.redraw_(); 1358 } 1359}; 1360 1361 1362/** 1363 * Adds an array of markers to the clusterer. The clusters are redrawn unless 1364 * <code>opt_nodraw</code> is set to <code>true</code>. 1365 * 1366 * @param {Array.<google.maps.Marker>} markers The markers to add. 1367 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1368 */ 1369MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) { 1370 var key; 1371 for (key in markers) { 1372 if (markers.hasOwnProperty(key)) { 1373 this.pushMarkerTo_(markers[key]); 1374 } 1375 } 1376 if (!opt_nodraw) { 1377 this.redraw_(); 1378 } 1379}; 1380 1381 1382/** 1383 * Pushes a marker to the clusterer. 1384 * 1385 * @param {google.maps.Marker} marker The marker to add. 1386 */ 1387MarkerClusterer.prototype.pushMarkerTo_ = function(marker) { 1388 // If the marker is draggable add a listener so we can update the clusters on the dragend: 1389 if (marker.getDraggable()) { 1390 var cMarkerClusterer = this; 1391 google.maps.event.addListener(marker, "dragend", function() { 1392 if (cMarkerClusterer.ready_) { 1393 this.isAdded = false; 1394 cMarkerClusterer.repaint(); 1395 } 1396 }); 1397 } 1398 marker.isAdded = false; 1399 this.markers_.push(marker); 1400}; 1401 1402 1403/** 1404 * Removes a marker from the cluster. The clusters are redrawn unless 1405 * <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if the 1406 * marker was removed from the clusterer. 1407 * 1408 * @param {google.maps.Marker} marker The marker to remove. 1409 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1410 * @return {boolean} True if the marker was removed from the clusterer. 1411 */ 1412MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) { 1413 var removed = this.removeMarker_(marker); 1414 1415 if (!opt_nodraw && removed) { 1416 this.repaint(); 1417 } 1418 1419 return removed; 1420}; 1421 1422 1423/** 1424 * Removes an array of markers from the cluster. The clusters are redrawn unless 1425 * <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if markers 1426 * were removed from the clusterer. 1427 * 1428 * @param {Array.<google.maps.Marker>} markers The markers to remove. 1429 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1430 * @return {boolean} True if markers were removed from the clusterer. 1431 */ 1432MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) { 1433 var i, r; 1434 var removed = false; 1435 1436 for (i = 0; i < markers.length; i++) { 1437 r = this.removeMarker_(markers[i]); 1438 removed = removed || r; 1439 } 1440 1441 if (!opt_nodraw && removed) { 1442 this.repaint(); 1443 } 1444 1445 return removed; 1446}; 1447 1448 1449/** 1450 * Removes a marker and returns true if removed, false if not. 1451 * 1452 * @param {google.maps.Marker} marker The marker to remove 1453 * @return {boolean} Whether the marker was removed or not 1454 */ 1455MarkerClusterer.prototype.removeMarker_ = function(marker) { 1456 var i; 1457 var index = -1; 1458 if (this.markers_.indexOf) { 1459 index = this.markers_.indexOf(marker); 1460 } else { 1461 for (i = 0; i < this.markers_.length; i++) { 1462 if (marker === this.markers_[i]) { 1463 index = i; 1464 break; 1465 } 1466 } 1467 } 1468 1469 if (index === -1) { 1470 // Marker is not in our list of markers, so do nothing: 1471 return false;
1472 } 1473 1474 marker.setMap(null); 1475 this.markers_.splice(index, 1); // Remove the marker from the list of managed markers 1476 return true; 1477}; 1478 1479 1480/** 1481 * Removes all clusters and markers from the map and also removes all markers 1482 * managed by the clusterer. 1483 */ 1484MarkerClusterer.prototype.clearMarkers = function() { 1485 this.resetViewport_(true); 1486 this.markers_ = []; 1487}; 1488 1489 1490/** 1491 * Recalculates and redraws all the marker clusters from scratch. 1492 * Call this after changing any properties. 1493 */ 1494MarkerClusterer.prototype.repaint = function() { 1495 var oldClusters = this.clusters_.slice(); 1496 this.clusters_ = []; 1497 this.resetViewport_(false); 1498 this.redraw_(); 1499 1500 // Remove the old clusters. 1501 // Do it in a timeout to prevent blinking effect. 1502 setTimeout(function() { 1503 var i; 1504 for (i = 0; i < oldClusters.length; i++) { 1505 oldClusters[i].remove(); 1506 } 1507 }, 0); 1508}; 1509 1510 1511/** 1512 * Returns the current bounds extended by the grid size. 1513 * 1514 * @param {google.maps.LatLngBounds} bounds The bounds to extend. 1515 * @return {google.maps.LatLngBounds}
vendor: 5,340 bytes, lines 1515-1683
1515 The extended bounds. 1516 * @ignore 1517 */ 1518MarkerClusterer.prototype.getExtendedBounds = function(bounds) { 1519 var projection = this.getProjection(); 1520 1521 // Turn the bounds into latlng. 1522 var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), 1523 bounds.getNorthEast().lng()); 1524 var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), 1525 bounds.getSouthWest().lng()); 1526 1527 // Convert the points to pixels and the extend out by the grid size. 1528 var trPix = projection.fromLatLngToDivPixel(tr); 1529 trPix.x += this.gridSize_; 1530 trPix.y -= this.gridSize_; 1531 1532 var blPix = projection.fromLatLngToDivPixel(bl); 1533 blPix.x -= this.gridSize_; 1534 blPix.y += this.gridSize_; 1535 1536 // Convert the pixel points back to LatLng 1537 var ne = projection.fromDivPixelToLatLng(trPix); 1538 var sw = projection.fromDivPixelToLatLng(blPix); 1539 1540 // Extend the bounds to contain the new bounds. 1541 bounds.extend(ne); 1542 bounds.extend(sw); 1543 1544 return bounds; 1545}; 1546 1547 1548/** 1549 * Redraws all the clusters. 1550 */ 1551MarkerClusterer.prototype.redraw_ = function() { 1552 this.createClusters_(0); 1553}; 1554 1555 1556/** 1557 * Removes all clusters from the map. The markers are also removed from the map 1558 * if <code>opt_hide</code> is set to <code>true</code>. 1559 * 1560 * @param {boolean} [opt_hide] Set to <code>true</code> to also remove the markers 1561 * from the map. 1562 */ 1563MarkerClusterer.prototype.resetViewport_ = function(opt_hide) { 1564 var i, marker; 1565 // Remove all the clusters 1566 for (i = 0; i < this.clusters_.length; i++) { 1567 this.clusters_[i].remove(); 1568 } 1569 this.clusters_ = []; 1570 1571 // Reset the markers to not be added and to be removed from the map. 1572 for (i = 0; i < this.markers_.length; i++) { 1573 marker = this.markers_[i]; 1574 marker.isAdded = false; 1575 if (opt_hide) { 1576 marker.setMap(null); 1577 } 1578 } 1579}; 1580 1581 1582/** 1583 * Calculates the distance between two latlng locations in km. 1584 * 1585 * @param {google.maps.LatLng} p1 The first lat lng point. 1586 * @param {google.maps.LatLng} p2 The second lat lng point. 1587 * @return {number} The distance between the two points in km. 1588 * @see http://www.movable-type.co.uk/scripts/latlong.html 1589 */ 1590MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) { 1591 var R = 6371; // Radius of the Earth in km 1592 var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; 1593 var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; 1594 var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + 1595 Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * 1596 Math.sin(dLon / 2) * Math.sin(dLon / 2); 1597 var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 1598 var d = R * c; 1599 return d; 1600}; 1601 1602 1603/** 1604 * Determines if a marker is contained in a bounds. 1605 * 1606 * @param {google.maps.Marker} marker The marker to check. 1607 * @param {google.maps.LatLngBounds} bounds The bounds to check against. 1608 * @return {boolean} True if the marker is in the bounds. 1609 */ 1610MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) { 1611 return bounds.contains(marker.getPosition()); 1612}; 1613 1614 1615/** 1616 * Adds a marker to a cluster, or creates a new cluster. 1617 * 1618 * @param {google.maps.Marker} marker The marker to add. 1619 */ 1620MarkerClusterer.prototype.addToClosestCluster_ = function(marker) { 1621 var i, d, cluster, center; 1622 var distance = 40000; // Some large number 1623 var clusterToAddTo = null; 1624 for (i = 0; i < this.clusters_.length; i++) { 1625 cluster = this.clusters_[i]; 1626 center = cluster.getCenter(); 1627 if (center) { 1628 d = this.distanceBetweenPoints_(center, marker.getPosition()); 1629 if (d < distance) { 1630 distance = d; 1631 clusterToAddTo = cluster; 1632 } 1633 } 1634 } 1635 1636 if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { 1637 clusterToAddTo.addMarker(marker); 1638 } else { 1639 cluster = new Cluster(this); 1640 cluster.addMarker(marker); 1641 this.clusters_.push(cluster); 1642 } 1643}; 1644 1645 1646/** 1647 * Creates the clusters. This is done in batches to avoid timeout errors 1648 * in some browsers when there is a huge number of markers. 1649 * 1650 * @param {number} iFirst The index of the first marker in the batch of 1651 * markers to be added to clusters. 1652 */ 1653MarkerClusterer.prototype.createClusters_ = function(iFirst) { 1654 var i, marker; 1655 var mapBounds; 1656 var cMarkerClusterer = this; 1657 if (!this.ready_) { 1658 return; 1659 } 1660 1661 // Cancel previous batch processing if we're working on the first batch: 1662 if (iFirst === 0) { 1663 /** 1664 * This event is fired when the <code>MarkerClusterer</code> begins 1665 * clustering markers. 1666 * @name MarkerClusterer#clusteringbegin 1667 * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered. 1668 * @event 1669 */ 1670 google.maps.event.trigger(this, "clusteringbegin", this); 1671 1672 if (typeof this.timerRefStatic !== "undefined") { 1673 clearTimeout(this.timerRefStatic); 1674 delete this.timerRefStatic; 1675 } 1676 } 1677 1678 // Get our current map view bounds. 1679 // Create a new bounds object so we don't affect the map. 1680 // 1681 // See Comments 9 & 11 on Issue 3651 relating to this workaround for a Google Maps bug: 1682 if (this.getMap().getZoom() > 3) { 1683 mapBounds = new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest
1683(), 1684 this.getMap().getBounds().getNorthEast()); 1685 } else { 1686 mapBounds = new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472, -178.48388434375), new google.maps.LatLng(-85.08136444384544, 178.00048865625)); 1687 } 1688 var bounds = this.getExtendedBounds(mapBounds); 1689 1690 var iLast = Math.min(iFirst + this.batchSize_, this.markers_.length); 1691 1692 for (i = iFirst; i < iLast; i++) { 1693 marker = this.markers_[i]; 1694 if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { 1695 if (!this.ignoreHidden_ || (this.ignoreHidden_ && marker.getVisible())) { 1696 this.addToClosestCluster_(marker); 1697 } 1698 } 1699 } 1700 1701 if (iLast < this.markers_.length) { 1702 this.timerRefStatic = setTimeout(function() { 1703 cMarkerClusterer.createClusters_(iLast); 1704 }, 0); 1705 } else { 1706 delete this.timerRefStatic; 1707 1708 /** 1709 * This event is fired when the <code>MarkerClusterer</code> stops 1710 * clustering markers. 1711 * @name MarkerClusterer#clusteringend 1712 * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered. 1713 * @event 1714 */ 1715 google.maps.event.trigger(this, "clusteringend", this); 1716 } 1717}; 1718 1719 1720/** 1721 * Extends an object's prototype by another's. 1722 * 1723 * @param {Object} obj1 The object to be extended. 1724 * @param {Object} obj2 The object to extend with. 1725 * @return {Object} The new extended object. 1726 * @ignore 1727 */ 1728MarkerClusterer.prototype.extend = function(obj1, obj2) { 1729 return (function(object) { 1730 var property; 1731 for (property in object.prototype) { 1732 this.prototype[property] = object.prototype[property]; 1733 } 1734 return this; 1735 }).apply(obj1, [obj2]); 1736}; 1737 1738 1739/** 1740 * The default function for determining the label text and style 1741 * for a cluster icon. 1742 * 1743 * @param {Array.<google.maps.Marker>} markers The array of markers represented by the cluster. 1744 * @param {number} numStyles The number of marker styles available. 1745 * @return {ClusterIconInfo} The information resource for the cluster. 1746 * @constant 1747 * @ignore 1748 */ 1749MarkerClusterer.CALCULATOR = function(markers, numStyles) { 1750 var index = 0; 1751 var title = ""; 1752 var count = markers.length.toString(); 1753 1754 var dv = count; 1755 while (dv !== 0) { 1756 dv = parseInt(dv / 10, 10); 1757 index++; 1758 } 1759 1760 index = Math.min(index, numStyles); 1761 return { 1762 text: count, 1763 index: index, 1764 title: title 1765 }; 1766}; 1767 1768 1769/** 1770 * The number of markers to process in one batch. 1771 * 1772 * @type {number} 1773 * @constant 1774 */ 1775MarkerClusterer.BATCH_SIZE = 2000; 1776 1777 1778/** 1779 * The number of markers to process in one batch (IE only). 1780 * 1781 * @type {number} 1782 * @constant 1783 */ 1784MarkerClusterer.BATCH_SIZE_IE = 500; 1785 1786 1787/** 1788 * The default root name for the marker cluster images. 1789 * 1790 * @type {string} 1791 * @constant 1792 */ 1793MarkerClusterer.IMAGE_PATH = "http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/images/m"; 1794 1795 1796/** 1797 * The default extension name for the marker cluster images. 1798 * 1799 * @type {string} 1800 * @constant 1801 */ 1802MarkerClusterer.IMAGE_EXTENSION = "png"; 1803 1804 1805/** 1806 * The default array of sizes for the marker cluster images. 1807 * 1808 * @type {Array.<number>} 1809 * @constant 1810 */ 1811MarkerClusterer.IMAGE_SIZES = [53, 56, 66, 78, 90]; 1812 1813/** 1814 * Accordion, jQuery Plugin 1815 * 1816 * This plugin provides an accordion with cookie support. 1817 * 1818 * Copyright (c) 2011 John Snyder (snyderplace.com) 1819 * @license http://www.snyderplace.com/accordion/license.txt New BSD 1820 * @version 1.1 1821 */ 1822(function($) { 1823 $.fn.accordion = function(options) { 1824 1825 //firewalling 1826 if (!this || this.length < 1) { 1827 return this; 1828 } 1829 1830 initialize(this, options); 1831 1832 }; 1833 1834 //create the initial accordion 1835 function initialize(obj, options) { 1836 1837 //build main options before element iteration 1838 var opts = $.extend({}, $.fn.accordion.defaults, options); 1839 1840 //store any opened default values to set cookie later 1841 var opened = ''; 1842 1843 //iterate each matched object, bind, and open/close 1844 obj.each(function() { 1845 var $this = $(this); 1846 saveOpts($this, opts); 1847 1848 //bind it to the event 1849 if (opts.bind == 'mouseenter') { 1850 $this.bind('mouseenter', function(e) { 1851 e.preventDefault(); 1852 toggle($this, opts); 1853 }); 1854 } 1855 1856 //bind it to the event 1857 if (opts.bind == 'mouseover') { 1858 $this.bind('mouseover', function(e) { 1859 e.preventDefault(); 1860 toggle($this, opts); 1861 }); 1862 } 1863 1864 //bind it to the event 1865 if (opts.bind == 'click') { 1866 $this.bind('click', function(e) { 1867 e.preventDefault(); 1868 toggle($this, opts); 1869 }); 1870 } 1871 1872 //bind it to the event 1873 if (opts.bind == 'dblclick') { 1874 $this.bind('dblclick', function(e) { 1875 e.preventDefault(); 1876 toggle($this, opts); 1877 }); 1878 } 1879 1880 //initialize the panels 1881 //get the id for this element 1882 id = $this.attr('id'); 1883 1884 //if not using cookies, open defaults 1885 if (!useCookies(opts)) { 1886 //close it if not defaulted to open 1887 if (id != opts.defaultOpen) { 1888 $this.addClass(opts.cssClose); 1889 opts.loadClose($this, opts); 1890 } else { //its a default open, open it 1891 $this.addClass(opts.cssOpen); 1892 opts.loadOpen($this, opts); 1893 opened = id; 1894 } 1895 } else { //can use cookies, use them now 1896 //has a cookie been set, this overrides default open 1897 if (issetCookie(opts)) { 1898 if (inCookie(id, opts) === false) { 1899 $this.addClass(opts.cssClose); 1900 opts.loadClose($this, opts); 1901 } else { 1902 $this.addClass(opts.cssOpen); 1903 opts.loadOpen($this, opts); 1904 opened = id; 1905 } 1906 } else { //a cookie hasn't been set open defaults 1907 if (id != opts.defaultOpen) { 1908 $this.addClass(opts.cssClose); 1909 opts.loadClose($this, opts); 1910 } else { //its a default open, open it 1911 $this.addClass(opts.cssOpen); 1912 opts.loadOpen($this, opts); 1913 opened = id; 1914 } 1915 } 1916 } 1917 }); 1918 1919 //now that the loop is done, set the cookie 1920 if (opened.length > 0 && useCookies(opts)) {
1921 setCookie(opened, opts); 1922 } else { //there are none open, set cookie 1923 setCookie('', opts); 1924 } 1925 1926 return obj; 1927 }; 1928 1929 //load opts from object 1930 function loadOpts($this) { 1931 return $this.data('accordion-opts'); 1932 } 1933 1934 //save opts into object 1935 function saveOpts($this, opts) { 1936 return $this.data('accordion-opts', opts); 1937 } 1938 1939 //hides a accordion panel 1940 function close(opts) { 1941 opened = $(document).find('.' + opts.cssOpen); 1942 $.each(opened, function() { 1943 //give the proper class to the linked element 1944 $(this).addClass(opts.cssClose).removeClass(opts.cssOpen); 1945 opts.animateClose($(this), opts); 1946 }); 1947 } 1948 1949 //opens a accordion panel 1950 function open($this, opts) { 1951 close(opts); 1952 //give the proper class to the linked element 1953 $this.removeClass(opts.cssClose).addClass(opts.cssOpen); 1954 1955 //open the element 1956 opts.animateOpen($this, opts); 1957 1958 //do cookies if plugin available 1959 if (useCookies(opts)) { 1960 // split the cookieOpen string by "," 1961 id = $this.attr('id'); 1962 setCookie(id, opts); 1963 } 1964 } 1965 1966 //toggle a accordion on an event 1967 function toggle($this, opts) { 1968 // close the only open item 1969 if ($this.hasClass(opts.cssOpen)) { 1970 close(opts); 1971 //do cookies if plugin available 1972 if (useCookies(opts)) { 1973 // split the cookieOpen string by "," 1974 setCookie('', opts); 1975 } 1976 return false; 1977 } 1978 close(opts); 1979 //open a closed element 1980 open($this, opts); 1981 return false; 1982 } 1983 1984 //use cookies? 1985 function useCookies(opts) { 1986 //return false if cookie plugin not present or if a cookie name is not provided 1987 if (!$.cookie || opts.cookieName == '') { 1988 return false; 1989 } 1990 1991 //we can use cookies 1992 return true; 1993 } 1994 1995 //set a cookie 1996 function setCookie(value, opts) { 1997 //can use the cookie plugin 1998 if (!useCookies(opts)) { //no, quit here 1999 return false; 2000 } 2001 2002 //cookie plugin is available, lets set the cookie 2003 $.cookie(opts.cookieName, value, opts.cookieOptions); 2004 } 2005 2006 //check if a accordion is in the cookie 2007 function inCookie(value, opts) { 2008 //can use the cookie plugin 2009 if (!useCookies(opts)) { 2010 return false; 2011 } 2012 2013 //if its not there we don't need to remove from it 2014 if (!issetCookie(opts)) { //quit here, don't have a cookie 2015 return false; 2016 } 2017 2018 //unescape it 2019 cookie = unescape($.cookie(opts.cookieName)); 2020 2021 //is this value in the cookie arrray 2022 if (cookie != value) { //no, quit here 2023 return false; 2024 } 2025 2026 return true; 2027 } 2028 2029 //check if a cookie is set 2030 function issetCookie(opts) { 2031 //can we use the cookie plugin 2032 if (!useCookies(opts)) { //no, quit here 2033 return false; 2034 } 2035 2036 //is the cookie set 2037 if ($.cookie(opts.cookieName) == null) { //no, quit here 2038 return false; 2039 } 2040 2041 return true; 2042 } 2043 2044 // settings 2045 $.fn.accordion.defaults = { 2046 cssClose: 'accordion-close', //class you want to assign to a closed accordion header 2047 cssOpen: 'accordion-open', //class you want to assign an opened accordion header 2048 cookieName: 'accordion', //name of the cookie you want to set for this accordion 2049 cookieOptions: { //cookie options, see cookie plugin for details 2050 path: '/', 2051 expires: 7, 2052 domain: '', 2053 secure: '' 2054 }, 2055 defaultOpen: '', //id that you want opened by default 2056 speed: 'slow', //speed of the slide effect 2057 bind: 'click', //event to bind to, supports click, dblclick, mouseover and mouseenter 2058 animateOpen: function(elem, opts) { //replace the standard slideDown with custom function 2059 elem.next().stop(true, true).slideDown(opts.speed); 2060 }, 2061 animateClose: function(elem, opts) { //replace the standard slideUp with custom function
vendor: 6,719 bytes, lines 2062-2262
2062 elem.next().stop(true, true).slideUp(opts.speed); 2063 }, 2064 loadOpen: function(elem, opts) { //replace the default open state with custom function 2065 elem.next().show(); 2066 }, 2067 loadClose: function(elem, opts) { //replace the default close state with custom function 2068 elem.next().hide(); 2069 } 2070 }; 2071})(jQuery); 2072 2073/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh) 2074 * Licensed under the MIT License (LICENSE.txt). 2075 * 2076 * Version: 3.1.9 2077 * 2078 * Requires: jQuery 1.2.2+ 2079 */ 2080 2081(function(factory) { 2082 if (typeof define === 'function' && define.amd) { 2083 // AMD. Register as an anonymous module. 2084 define(['jquery'], factory); 2085 } else if (typeof exports === 'object') { 2086 // Node/CommonJS style for Browserify 2087 module.exports = factory; 2088 } else { 2089 // Browser globals 2090 factory(jQuery); 2091 } 2092}(function($) { 2093 2094 var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'], 2095 toBind = ('onwheel' in document || document.documentMode >= 9) ? ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'], 2096 slice = Array.prototype.slice, 2097 nullLowestDeltaTimeout, lowestDelta; 2098 2099 if ($.event.fixHooks) { 2100 for (var i = toFix.length; i;) { 2101 $.event.fixHooks[toFix[--i]] = $.event.mouseHooks; 2102 } 2103 } 2104 2105 var special = $.event.special.mousewheel = { 2106 version: '3.1.9', 2107 2108 setup: function() { 2109 if (this.addEventListener) { 2110 for (var i = toBind.length; i;) { 2111 this.addEventListener(toBind[--i], handler, false); 2112 } 2113 } else { 2114 this.onmousewheel = handler; 2115 } 2116 // Store the line height and page height for this particular element 2117 $.data(this, 'mousewheel-line-height', special.getLineHeight(this)); 2118 $.data(this, 'mousewheel-page-height', special.getPageHeight(this)); 2119 }, 2120 2121 teardown: function() { 2122 if (this.removeEventListener) { 2123 for (var i = toBind.length; i;) { 2124 this.removeEventListener(toBind[--i], handler, false); 2125 } 2126 } else { 2127 this.onmousewheel = null; 2128 } 2129 }, 2130 2131 getLineHeight: function(elem) { 2132 return parseInt($(elem)['offsetParent' in $.fn ? 'offsetParent' : 'parent']().css('fontSize'), 10); 2133 }, 2134 2135 getPageHeight: function(elem) { 2136 return $(elem).height(); 2137 }, 2138 2139 settings: { 2140 adjustOldDeltas: true 2141 } 2142 }; 2143 2144 $.fn.extend({ 2145 mousewheel: function(fn) { 2146 return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel'); 2147 }, 2148 2149 unmousewheel: function(fn) { 2150 return this.unbind('mousewheel', fn); 2151 } 2152 }); 2153 2154 2155 function handler(event) { 2156 var orgEvent = event || window.event, 2157 args = slice.call(arguments, 1), 2158 delta = 0, 2159 deltaX = 0, 2160 deltaY = 0, 2161 absDelta = 0; 2162 event = $.event.fix(orgEvent); 2163 event.type = 'mousewheel'; 2164 2165 // Old school scrollwheel delta 2166 if ('detail' in orgEvent) { 2167 deltaY = orgEvent.detail * -1; 2168 } 2169 if ('wheelDelta' in orgEvent) { 2170 deltaY = orgEvent.wheelDelta; 2171 } 2172 if ('wheelDeltaY' in orgEvent) { 2173 deltaY = orgEvent.wheelDeltaY; 2174 } 2175 if ('wheelDeltaX' in orgEvent) { 2176 deltaX = orgEvent.wheelDeltaX * -1; 2177 } 2178 2179 // Firefox < 17 horizontal scrolling related to DOMMouseScroll event 2180 if ('axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS) { 2181 deltaX = deltaY * -1; 2182 deltaY = 0; 2183 } 2184 2185 // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy 2186 delta = deltaY === 0 ? deltaX : deltaY; 2187 2188 // New school wheel delta (wheel event) 2189 if ('deltaY' in orgEvent) { 2190 deltaY = orgEvent.deltaY * -1; 2191 delta = deltaY; 2192 } 2193 if ('deltaX' in orgEvent) { 2194 deltaX = orgEvent.deltaX; 2195 if (deltaY === 0) { 2196 delta = deltaX * -1; 2197 } 2198 } 2199 2200 // No change actually happened, no reason to go any further 2201 if (deltaY === 0 && deltaX === 0) { 2202 return; 2203 } 2204 2205 // Need to convert lines and pages to pixels if we aren't already in pixels 2206 // There are three delta modes: 2207 // * deltaMode 0 is by pixels, nothing to do 2208 // * deltaMode 1 is by lines 2209 // * deltaMode 2 is by pages 2210 if (orgEvent.deltaMode === 1) { 2211 var lineHeight = $.data(this, 'mousewheel-line-height'); 2212 delta *= lineHeight; 2213 deltaY *= lineHeight; 2214 deltaX *= lineHeight; 2215 } else if (orgEvent.deltaMode === 2) { 2216 var pageHeight = $.data(this, 'mousewheel-page-height'); 2217 delta *= pageHeight; 2218 deltaY *= pageHeight; 2219 deltaX *= pageHeight; 2220 } 2221 2222 // Store lowest absolute delta to normalize the delta values 2223 absDelta = Math.max(Math.abs(deltaY), Math.abs(deltaX)); 2224 2225 if (!lowestDelta || absDelta < lowestDelta) { 2226 lowestDelta = absDelta; 2227 2228 // Adjust older deltas if necessary 2229 if (shouldAdjustOldDeltas(orgEvent, absDelta)) { 2230 lowestDelta /= 40; 2231 } 2232 } 2233 2234 // Adjust older deltas if necessary 2235 if (shouldAdjustOldDeltas(orgEvent, absDelta)) { 2236 // Divide all the things by 40! 2237 delta /= 40; 2238 deltaX /= 40; 2239 deltaY /= 40; 2240 } 2241 2242 // Get a whole, normalized value for the deltas 2243 delta = Math[delta >= 1 ? 'floor' : 'ceil'](delta / lowestDelta); 2244 deltaX = Math[deltaX >= 1 ? 'floor' : 'ceil'](deltaX / lowestDelta); 2245 deltaY = Math[deltaY >= 1 ? 'floor' : 'ceil'](deltaY / lowestDelta); 2246 2247 // Add information to the event object 2248 event.deltaX = deltaX; 2249 event.deltaY = deltaY; 2250 event.deltaFactor = lowestDelta; 2251 // Go ahead and set deltaMode to 0 since we converted to pixels 2252 // Although this is a little odd since we overwrite the deltaX/Y 2253 // properties with normalized deltas. 2254 event.deltaMode = 0; 2255 2256 // Add event and delta to the front of the arguments 2257 args.unshift(event, delta, deltaX, deltaY); 2258 2259 // Clearout lowestDelta after sometime to better 2260 // handle multiple device types that give different 2261 // a different lowestDelta 2262 // Ex: trackpad = 3 and mouse wheel = 120
2263 if (nullLowestDeltaTimeout) { 2264 clearTimeout(nullLowestDeltaTimeout); 2265 } 2266 nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200); 2267 2268 return ($.event.dispatch || $.event.handle).apply(this, args); 2269 } 2270 2271 function nullLowestDelta() { 2272 lowestDelta = null; 2273 } 2274 2275 function shouldAdjustOldDeltas(orgEvent, absDelta) { 2276 // If this is an older event and the delta is divisable by 120, 2277 // then we are assuming that the browser is treating this as an 2278 // older mouse wheel event and that we should divide the deltas 2279 // by 40 to try and get a more usable deltaFactor. 2280 // Side note, this actually impacts the reported scroll distance 2281 // in older browsers and can cause scrolling to be slower than native. 2282 // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false. 2283 return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0; 2284 } 2285 2286})); 2287 2288/*! 2289 * jScrollPane - v2.0.19 - 2013-11-16 2290 * http://jscrollpane.kelvinluck.com/ 2291 * 2292 * Copyright (c) 2013 Kelvin Luck 2293 * Dual licensed under the MIT or GPL licenses. 2294 */ 2295! function(a, b, c) { 2296 a.fn.jScrollPane = function(d) { 2297 function e(d, e) { 2298 function f(b) { 2299 var e, h, j, l, m, n, q = !1, 2300 r = !1; 2301 if (P = b, Q === c) m = d.scrollTop(), n = d.scrollLeft(), d.css({ 2302 overflow: "hidden", 2303 padding: 0 2304 }), R = d.innerWidth() + tb, S = d.innerHeight(), d.width(R), Q = a('<div class="jspPane" />').css("padding", sb).append(d.children()), T = a('<div class="jspContainer" />').css({ 2305 width: R + "px", 2306 height: S + "px" 2307 }).append(Q).appendTo(d); 2308 else { 2309 if (d.css("width", ""), q = P.stickToBottom && C(), r = P.stickToRight && D(), l = d.innerWidth() + tb != R || d.outerHeight() != S, l && (R = d.innerWidth() + tb, S = d.innerHeight(), T.css({ 2310 width: R + "px", 2311 height: S + "px" 2312 })), !l && ub == U && Q.outerHeight() == V) return d.width(R), void 0; 2313 ub = U, Q.css("width", ""), d.width(R), T.find(">.jspVerticalBar,>.jspHorizontalBar").remove().end() 2314 } 2315 Q.css("overflow", "auto"), U = b.contentWidth ? b.contentWidth : Q[0].scrollWidth, V = Q[0].scrollHeight, Q.css("overflow", ""), W = U / R, X = V / S, Y = X > 1, Z = W > 1, Z || Y ? (d.addClass("jspScrollable"), e = P.maintainPosition && (ab || db), e && (h = A(), j = B()), g(), i(), k(), e && (y(r ? U - R : h, !1), x(q ? V - S : j, !1)), H(), E(), N(), P.enableKeyboardNavigation && J(), P.clickOnTrack && o(), L(), P.hijackInternalLinks && M()) : (d.removeClass("jspScrollable"), Q.css({ 2316 top: 0, 2317 left: 0, 2318 width: T.width() - tb 2319 }), F(), I(), K(), p()), P.autoReinitialise && !rb ? rb = setInterval(function() { 2320 f(P) 2321 }, P.autoReinitialiseDelay) : !P.autoReinitialise && rb && clearInterval(rb), m && d.scrollTop(0) && x(m, !1), n && d.scrollLeft(0) && y(n, !1), d.trigger("jsp-initialised", [Z || Y]) 2322 } 2323 2324 function g() { 2325 Y && (T.append(a('<div class="jspVerticalBar" />').append(a('<div class="jspCap jspCapTop" />'), a('<div class="jspTrack" />').append(a('<div class="jspDrag" />').append(a('<div class="jspDragTop" />'), a('<div class="jspDragBottom" />'))), a('<div class="jspCap jspCapBottom" />'))), eb = T.find(">.jspVerticalBar"), fb = eb.find(">.jspTrack"), $ = fb.find(">.jspDrag"), P.showArrows && (jb = a('<a class="jspArrow jspArrowUp" />').bind("mousedown.jsp", m(0, -1)).bind("click.jsp", G), kb = a('<a class="jspArrow jspArrowDown" />').bind("mousedown.jsp", m(0, 1)).bind("click.jsp", G), P.arrowScrollOnHover && (jb.bind("mouseover.jsp", m(0, -1, jb)), kb.bind("mouseover.jsp", m(0, 1, kb))), l(fb, P.verticalArrowPositions, jb, kb)), hb = S, T.find(">.jspVerticalBar>.jspCap:visible,>.jspVerticalBar>.jspArrow").each(function() { 2326 hb -= a(this).outerHeight() 2327 }), $.hover(function() { 2328 $.addClass("jspHover") 2329 }, function() { 2330 $.removeClass("jspHover") 2331 }).bind("mousedown.jsp", function(b) { 2332 a("html").bind("dragstart.jsp selectstart.jsp", G), $.addClass("jspActive"); 2333 var c = b.pageY - $.position().top; 2334 return a("html").bind("mousemove.jsp", function(a) { 2335 r(a.pageY - c, !1) 2336 }).bind("mouseup.jsp mouseleave.jsp", q), !1 2337 }), h()) 2338 } 2339 2340 function h() {
2341 fb.height(hb + "px"), ab = 0, gb = P.verticalGutter + fb.outerWidth(), Q.width(R - gb - tb); 2342 try { 2343 0 === eb.position().left && Q.css("margin-left", gb + "px") 2344 } catch (a) {} 2345 } 2346 2347 function i() { 2348 Z && (T.append(a('<div class="jspHorizontalBar" />').append(a('<div class="jspCap jspCapLeft" />'), a('<div class="jspTrack" />').append(a('<div class="jspDrag" />').append(a('<div class="jspDragLeft" />'), a('<div class="jspDragRight" />'))), a('<div class="jspCap jspCapRight" />'))), lb = T.find(">.jspHorizontalBar"), mb = lb.find(">.jspTrack"), bb = mb.find(">.jspDrag"), P.showArrows && (pb = a('<a class="jspArrow jspArrowLeft" />').bind("mousedown.jsp", m(-1, 0)).bind("click.jsp", G), qb = a('<a class="jspArrow jspArrowRight" />').bind("mousedown.jsp", m(1, 0)).bind("click.jsp", G), P.arrowScrollOnHover && (pb.bind("mouseover.jsp", m(-1, 0, pb)), qb.bind("mouseover.jsp", m(1, 0, qb))), l(mb, P.horizontalArrowPositions, pb, qb)), bb.hover(function() { 2349 bb.addClass("jspHover") 2350 }, function() { 2351 bb.removeClass("jspHover") 2352 }).bind("mousedown.jsp", function(b) { 2353 a("html").bind("dragstart.jsp selectstart.jsp", G), bb.addClass("jspActive"); 2354 var c = b.pageX - bb.position().left; 2355 return a("html").bind("mousemove.jsp", function(a) { 2356 t(a.pageX - c, !1) 2357 }).bind("mouseup.jsp mouseleave.jsp", q), !1 2358 }), nb = T.innerWidth(), j()) 2359 } 2360 2361 function j() { 2362 T.find(">.jspHorizontalBar>.jspCap:visible,>.jspHorizontalBar>.jspArrow").each(function() { 2363 nb -= a(this).outerWidth() 2364 }), mb.width(nb + "px"), db = 0 2365 } 2366 2367 function k() { 2368 if (Z && Y) { 2369 var b = mb.outerHeight(), 2370 c = fb.outerWidth(); 2371 hb -= b, a(lb).find(">.jspCap:visible,>.jspArrow").each(function() { 2372 nb += a(this).outerWidth() 2373 }), nb -= c, S -= c, R -= b, mb.parent().append(a('<div class="jspCorner" />').css("width", b + "px")), h(), j() 2374 } 2375 Z && Q.width(T.outerWidth() - tb + "px"), V = Q.outerHeight(), X = V / S, Z && (ob = Math.ceil(1 / W * nb), ob > P.horizontalDragMaxWidth ? ob = P.horizontalDragMaxWidth : ob < P.horizontalDragMinWidth && (ob = P.horizontalDragMinWidth), bb.width(ob + "px"), cb = nb - ob, u(db)), Y && (ib = Math.ceil(1 / X * hb), ib > P.verticalDragMaxHeight ? ib = P.verticalDragMaxHeight : ib < P.verticalDragMinHeight && (ib = P.verticalDragMinHeight), $.height(ib + "px"), _ = hb - ib, s(ab)) 2376 } 2377 2378 function l(a, b, c, d) { 2379 var e, f = "before", 2380 g = "after"; 2381 "os" == b && (b = /Mac/.test(navigator.platform) ? "after" : "split"), b == f ? g = b : b == g && (f = b, e = c, c = d, d = e), a[f](c)[g](d) 2382 } 2383 2384 function m(a, b, c) { 2385 return function() { 2386 return n(a, b, this, c), this.blur(), !1 2387 } 2388 } 2389 2390 function n(b, c, d, e) { 2391 d = a(d).addClass("jspActive"); 2392 var f, g, h = !0, 2393 i = function() { 2394 0 !== b && vb.scrollByX(b * P.arrowButtonSpeed), 0 !== c && vb.scrollByY(c * P.arrowButtonSpeed), g = setTimeout(i, h ? P.initialDelay : P.arrowRepeatFreq), h = !1 2395 }; 2396 i(), f = e ? "mouseout.jsp" : "mouseup.jsp", e = e || a("html"), e.bind(f, function() { 2397 d.removeClass("jspActive"), g && clearTimeout(g), g = null, e.unbind(f) 2398 }) 2399 } 2400 2401 function o() { 2402 p(), Y && fb.bind("mousedown.jsp", function(b) { 2403 if (b.originalTarget === c || b.originalTarget == b.currentTarget) { 2404 var d, e = a(this), 2405 f = e.offset(), 2406 g = b.pageY - f.top - ab, 2407 h = !0, 2408 i = function() { 2409 var a = e.offset(), 2410 c = b.pageY - a.top - ib / 2, 2411 f = S * P.scrollPagePercent, 2412 k = _ * f / (V - S); 2413 if (0 > g) ab - k > c ? vb.scrollByY(-f) : r(c); 2414 else { 2415 if (!(g > 0)) return j(), void 0; 2416 c > ab + k ? vb.scrollByY(f) : r(c) 2417 } 2418 d = setTimeout(i, h ? P.initialDelay : P.trackClickRepeatFreq), h = !1 2419 }, 2420 j = function() { 2421 d && clearTimeout(d), d = null, a(document).unbind("mouseup.jsp", j) 2422 }; 2423 return i(), a(document).bind("mouseup.jsp", j), !1 2424 } 2425 }), Z && mb.bind("mousedown.jsp", function(b) { 2426 if (b.originalTarget === c || b.originalTarget == b.currentTarget) { 2427 var d, e = a(this), 2428 f = e.offset(), 2429 g = b.pageX - f.left - db, 2430 h = !0, 2431 i = function() { 2432 var a = e.offset(), 2433 c = b.pageX - a.left - ob / 2, 2434 f = R * P.scrollPagePercent, 2435 k = cb * f / (U - R); 2436 if (0 > g) db - k > c ? vb.scrollByX(-f) : t(c); 2437 else { 2438 if (!(g > 0)) return j(), void 0; 2439 c > db + k ? vb.scrollByX(f) : t(c) 2440 } 2441 d = setTimeout(i, h ? P.initialDelay : P.trackClickRepeatFreq), h = !1 2442 }, 2443 j = function() { 2444 d && clearTimeout(d), d = null, a(document).unbind("mouseup.jsp", j) 2445 }; 2446 return i(), a(document).bind("mouseup.jsp", j), !1 2447 } 2448 }) 2449 } 2450 2451 function p() { 2452 mb && mb.unbind("mousedown.jsp"), fb && fb.unbind("mousedown.jsp") 2453 } 2454 2455 function q() { 2456 a("html").unbind("dragstart.jsp selectstart.jsp mousemove.jsp mouseup.jsp mouseleave.jsp"), $ && $.removeClass("jspActive"), bb && bb.removeClass("jspActive") 2457 } 2458 2459 function r(a, b) { 2460 Y && (0 > a ? a = 0 : a > _ && (a = _), b === c && (b = P.animateScroll), b ? vb.animate($, "top", a, s) : ($.css("top", a), s(a))) 2461 } 2462 2463 function s(a) { 2464 a === c && (a = $.position().top), T.scrollTop(0), ab = a; 2465 var b = 0 === ab, 2466 e = ab == _, 2467 f = a / _, 2468 g = -f * (V - S); 2469 (wb != b || yb != e) && (wb = b, yb = e, d.trigger("jsp-arrow-change", [wb, yb, xb, zb])), v(b, e), Q.css("top", g), d.trigger("jsp-scroll-y", [-g, b, e]).trigger("scroll") 2470 } 2471 2472 function t(a, b) { 2473 Z && (0 > a ? a = 0 : a > cb && (a = cb), b === c && (b = P.animateScroll), b ? vb.animate(bb, "left", a, u) : (bb.css("left", a), u(a))) 2474 } 2475 2476 function u(a) { 2477 a === c && (a = bb.position().left), T.scrollTop(0), db = a; 2478 var b = 0 === db, 2479 e = db == cb, 2480 f = a / cb, 2481 g = -f * (U - R); 2482 (xb != b || zb != e) && (xb = b, zb = e, d.trigger("jsp-arrow-change", [wb, yb, xb, zb])), w(b, e), Q.css("left", g), d.trigger("jsp-scroll-x", [-g, b, e]).trigger("scroll") 2483 } 2484 2485 function v(a, b) { 2486 P.showArrows && (jb[a ? "addClass" : "removeClass"]("jspDisabled"), kb[b ? "addClass" : "removeClass"]("jspDisabled")) 2487 } 2488 2489 function w(a, b) { 2490 P.showArrows && (pb[a ? "addClass" : "removeClass"]("jspDisabled"), qb[b ? "addClass" : "removeClass"]("jspDisabled")) 2491 } 2492 2493 function x(a, b) { 2494 var c = a / (V - S); 2495 r(c * _, b) 2496 } 2497 2498 function y(a, b) { 2499 var c = a / (U - R); 2500 t(c * cb, b) 2501 } 2502 2503 function z(b, c, d) { 2504 var e, f, g, h, i, j, k, l, m, n = 0, 2505 o = 0; 2506 try { 2507 e = a(b) 2508 } catch (p) { 2509 return 2510 } 2511 for (f = e.outerHeight(), g = e.outerWidth(), T.scrollTop(0), T.scrollLeft(0); !e.is(".jspPane");) 2512 if (n += e.position().top, o += e.position().left, e = e.offsetParent(), /^body|html$/i.test(e[0].nodeName)) return; 2513 h = B(), j = h + S, h > n || c ? l = n - P.horizontalGutter : n + f > j && (l = n - S + f + P.horizontalGutter), isNaN(l) || x(l, d), i = A(), k = i + R, i > o || c ? m = o - P.horizontalGutter : o + g > k && (m = o - R + g + P.horizontalGutter), isNaN(m) || y(m, d) 2514 } 2515 2516 function A() { 2517 return -Q.position().left 2518 } 2519 2520 function B() { 2521 return -Q.position().top 2522 } 2523 2524 function C() { 2525 var a = V - S; 2526 return a > 20 && a - B() < 10 2527 } 2528 2529 function D() { 2530 var a = U - R; 2531 return a > 20 && a - A() < 10 2532 } 2533 2534 function E() { 2535 T.unbind(Bb).bind(Bb, function(a, b, c, d) { 2536 var e = db, 2537 f = ab, 2538 g = a.deltaFactor || P.mouseWheelSpeed; 2539 return vb.scrollBy(c * g, -d * g, !1), e == db && f == ab 2540 }) 2541 } 2542 2543 function F() { 2544 T.unbind(Bb) 2545 } 2546 2547 function G() { 2548 return !1 2549 } 2550 2551 function H() { 2552 Q.find(":input,a").unbind("focus.jsp").bind("focus.jsp", function(a) { 2553 z(a.target, !1) 2554 }) 2555 } 2556 2557 function I() { 2558 Q.find(":input,a").unbind("focus.jsp") 2559 } 2560 2561 function J() { 2562 function b() { 2563 var a = db, 2564 b = ab; 2565 switch (c) { 2566 case 40: 2567 vb.scrollByY(P.keyboardSpeed, !1); 2568 break; 2569 case 38: 2570 vb.scrollByY(-P.keyboardSpeed, !1); 2571 break; 2572 case 34: 2573 case 32: 2574 vb.scrollByY(S * P.scrollPagePercent, !1); 2575 break; 2576 case 33: 2577 vb.scrollByY(-S * P.scrollPagePercent, !1); 2578 break; 2579 case 39: 2580 vb.scrollByX(P.keyboardSpeed, !1); 2581 break; 2582 case 37: 2583 vb.scrollByX(-P.keyboardSpeed, !1) 2584 } 2585 return e = a != db || b != ab 2586 } 2587 var c, e, f = []; 2588 Z && f.push(lb[0]), Y && f.push(eb[0]), Q.focus(function() { 2589 d.focus() 2590 }
2590), d.attr("tabindex", 0).unbind("keydown.jsp keypress.jsp").bind("keydown.jsp", function(d) { 2591 if (d.target === this || f.length && a(d.target).closest(f).length) { 2592 var g = db, 2593 h = ab; 2594 switch (d.keyCode) { 2595 case 40: 2596 case 38: 2597 case 34: 2598 case 32: 2599 case 33: 2600 case 39: 2601 case 37: 2602 c = d.keyCode, b(); 2603 break; 2604 case 35: 2605 x(V - S), c = null; 2606 break; 2607 case 36: 2608 x(0), c = null 2609 } 2610 return e = d.keyCode == c && g != db || h != ab, !e 2611 } 2612 }).bind("keypress.jsp", function(a) { 2613 return a.keyCode == c && b(), !e 2614 }), P.hideFocus ? (d.css("outline", "none"), "hideFocus" in T[0] && d.attr("hideFocus", !0)) : (d.css("outline", ""), "hideFocus" in T[0] && d.attr("hideFocus", !1)) 2615 } 2616 2617 function K() { 2618 d.attr("tabindex", "-1").removeAttr("tabindex").unbind("keydown.jsp keypress.jsp") 2619 } 2620 2621 function L() { 2622 if (location.hash && location.hash.length > 1) { 2623 var b, c, d = escape(location.hash.substr(1)); 2624 try { 2625 b = a("#" + d + ', a[name="' + d + '"]') 2626 } catch (e) { 2627 return 2628 } 2629 b.length && Q.find(d) && (0 === T.scrollTop() ? c = setInterval(function() { 2630 T.scrollTop() > 0 && (z(b, !0), a(document).scrollTop(T.position().top), clearInterval(c)) 2631 }, 50) : (z(b, !0), a(document).scrollTop(T.position().top))) 2632 } 2633 } 2634 2635 function M() { 2636 a(document.body).data("jspHijack") || (a(document.body).data("jspHijack", !0), a(document.body).delegate("a[href*=#]", "click", function(c) { 2637 var d, e, f, g, h, i, j = this.href.substr(0, this.href.indexOf("#")), 2638 k = location.href; 2639 if (-1 !== location.href.indexOf("#") && (k = location.href.substr(0, location.href.indexOf("#"))), j === k) { 2640 d = escape(this.href.substr(this.href.indexOf("#") + 1)); 2641 try { 2642 e = a("#" + d + ', a[name="' + d + '"]') 2643 } catch (l) { 2644 return 2645 } 2646 e.length && (f = e.closest(".jspScrollable"), g = f.data("jsp"), g.scrollToElement(e, !0), f[0].scrollIntoView && (h = a(b).scrollTop(), i = e.offset().top, (h > i || i > h + a(b).height()) && f[0].scrollIntoView()), c.preventDefault()) 2647 } 2648 })) 2649 } 2650 2651 function N() { 2652 var a, b, c, d, e, f = !1; 2653 T.unbind("touchstart.jsp touchmove.jsp touchend.jsp click.jsp-touchclick").bind("touchstart.jsp", function(g) { 2654 var h = g.originalEvent.touches[0]; 2655 a = A(), b = B(), c = h.pageX, d = h.pageY, e = !1, f = !0 2656 }).bind("touchmove.jsp", function(g) { 2657 if (f) { 2658 var h = g.originalEvent.touches[0], 2659 i = db, 2660 j = ab; 2661 return vb.scrollTo(a + c - h.pageX, b + d - h.pageY), e = e || Math.abs(c - h.pageX) > 5 || Math.abs(d - h.pageY) > 5, i == db && j == ab 2662 } 2663 }).bind("touchend.jsp", function() { 2664 f = !1 2665 }).bind("click.jsp-touchclick", function() { 2666 return e ? (e = !1, !1) : void 0 2667 }) 2668 } 2669 2670 function O() { 2671 var a = B(), 2672 b = A(); 2673 d.removeClass("jspScrollable").unbind(".jsp"), d.replaceWith(Ab.append(Q.children())), Ab.scrollTop(a), Ab.scrollLeft(b), rb && clearInterval(rb) 2674 } 2675 var P, Q, R, S, T, U, V, W, X, Y, Z, $, _, ab, bb, cb, db, eb, fb, gb, hb, ib, jb, kb, lb, mb, nb, ob, pb, qb, rb, sb, tb, ub, vb = this, 2676 wb = !0, 2677 xb = !0, 2678 yb = !1, 2679 zb = !1, 2680 Ab = d.clone(!1, !1).empty(), 2681 Bb = a.fn.mwheelIntent ? "mwheelIntent.jsp" : "mousewheel.jsp"; 2682 "border-box" === d.css("box-sizing") ? (sb = 0, tb = 0) : (sb = d.css("paddingTop") + " " + d.
2682css("paddingRight") + " " + d.css("paddingBottom") + " " + d.css("paddingLeft"), tb = (parseInt(d.css("paddingLeft"), 10) || 0) + (parseInt(d.css("paddingRight"), 10) || 0)), a.extend(vb, { 2683 reinitialise: function(b) { 2684 b = a.extend({}, P, b), f(b) 2685 }, 2686 scrollToElement: function(a, b, c) { 2687 z(a, b, c) 2688 }, 2689 scrollTo: function(a, b, c) { 2690 y(a, c), x(b, c) 2691 }, 2692 scrollToX: function(a, b) { 2693 y(a, b) 2694 }, 2695 scrollToY: function(a, b) { 2696 x(a, b) 2697 }, 2698 scrollToPercentX: function(a, b) { 2699 y(a * (U - R), b) 2700 }, 2701 scrollToPercentY: function(a, b) { 2702 x(a * (V - S), b) 2703 }, 2704 scrollBy: function(a, b, c) { 2705 vb.scrollByX(a, c), vb.scrollByY(b, c) 2706 }, 2707 scrollByX: function(a, b) { 2708 var c = A() + Math[0 > a ? "floor" : "ceil"](a), 2709 d = c / (U - R); 2710 t(d * cb, b) 2711 }, 2712 scrollByY: function(a, b) { 2713 var c = B() + Math[0 > a ? "floor" : "ceil"](a), 2714 d = c / (V - S); 2715 r(d * _, b) 2716 }, 2717 positionDragX: function(a, b) { 2718 t(a, b) 2719 }, 2720 positionDragY: function(a, b) { 2721 r(a, b) 2722 }, 2723 animate: function(a, b, c, d) { 2724 var e = {}; 2725 e[b] = c, a.animate(e, { 2726 duration: P.animateDuration, 2727 easing: P.animateEase, 2728 queue: !1, 2729 step: d 2730 }) 2731 }, 2732 getContentPositionX: function() { 2733 return A() 2734 }, 2735 getContentPositionY: function() { 2736 return B() 2737 }, 2738 getContentWidth: function() { 2739 return U 2740 }, 2741 getContentHeight: function() { 2742 return V 2743 }, 2744 getPercentScrolledX: function() { 2745 return A() / (U - R) 2746 }, 2747 getPercentScrolledY: function() { 2748 return B() / (V - S) 2749 }, 2750 getIsScrollableH: function() { 2751 return Z 2752 }, 2753 getIsScrollableV: function() { 2754 return Y 2755 }, 2756 getContentPane: function() { 2757 return Q 2758 }, 2759 scrollToBottom: function(a) { 2760 r(_, a) 2761 }, 2762 hijackInternalLinks: a.noop, 2763 destroy: function() { 2764 O() 2765 } 2766 }), f(e) 2767 } 2768 return d = a.extend({}, a.fn.jScrollPane.defaults, d), a.each(["arrowButtonSpeed", "trackClickSpeed", "keyboardSpeed"], function() { 2769 d[this] = d[this] || d.speed 2770 }), this.each(function() { 2771 var b = a(this), 2772 c = b.data("jsp"); 2773 c ? c.reinitialise(d) : (a("script", b).filter('[type="text/javascript"],:not([type])').remove(), c = new e(b, d), b.data("jsp", c)) 2774 }) 2775 }, a.fn.jScrollPane.defaults = { 2776 showArrows: !1, 2777 maintainPosition: !0, 2778 stickToBottom: !1, 2779 stickToRight: !1, 2780 clickOnTrack: !0, 2781 autoReinitialise: !1, 2782 autoReinitialiseDelay: 500, 2783 verticalDragMinHeight: 0, 2784 verticalDragMaxHeight: 99999, 2785 horizontalDragMinWidth: 0, 2786 horizontalDragMaxWidth: 99999, 2787 contentWidth: c, 2788 animateScroll: !1, 2789 animateDuration: 300, 2790 animateEase: "linear", 2791 hijackInternalLinks: !1, 2792 verticalGutter: 4, 2793 horizontalGutter: 4, 2794 mouseWheelSpeed: 3, 2795 arrowButtonSpeed: 0, 2796 arrowRepeatFreq: 50, 2797 arrowScrollOnHover: !1, 2798 trackClickSpeed: 0, 2799 trackClickRepeatFreq: 70, 2800 verticalArrowPositions: "split", 2801 horizontalArrowPositions: "split", 2802 enableKeyboardNavigation: !0, 2803 hideFocus: !1, 2804 keyboardSpeed: 0, 2805 initialDelay: 300, 2806 speed: 30, 2807 scrollPagePercent: .8 2808 } 2809}(jQuery, this); 2810 2811jQuery(document).ready(function($) { 2812 2813 $('.gm-style-iw').parent().parent().addClass('wpgmp_infowindow_css'); 2814 $("div.scroll-pane").jScrollPane(); 2815 2816 $(".accordion").accordion({ 2817 speed: "slow" 2818 }); 2819 2820 2821 $('span a').on('click', function(e) { 2822 var elem = $('div.wpgmp_categories'); 2823 if ($(this).hasClass('wpgmp_grid')) { 2824 elem.fadeOut(500, function() { 2825 elem.removeClass('wpgmp_listing_list').addClass('wpgmp_listing_grid'); 2826 $('.wpgmp_locations').equalHeightGrid(); 2827 elem.fadeIn(500); 2828 }); 2829 } else if ($(this).hasClass('wpgmp_list')) { 2830 elem.fadeOut(500, function() { 2831 elem.removeClass('wpgmp_listing_grid').addClass('wpgmp_listing_list'); 2832 $('.wpgmp_locations').equalHeightGrid(); 2833 elem.fadeIn(500); 2834 }); 2835 } 2836 }); 2837 2838}); 2839 2840/* jQuery.print, version 1.0.3 2841 * (c) Sathvik Ponangi, Doers' Guild 2842 * Licence: CC-By (http://creativecommons.org/licenses/by/3.0/) 2843 *--------------------------------------------------------------------------*/ 2844 2845(function($) { 2846 "use strict"; 2847 // A nice closure for our definitions 2848 2849 function getjQueryObject(string) { 2850 // Make string a vaild jQuery thing 2851 var jqObj = $(""); 2852 try { 2853 jqObj = $(string).clone(); 2854 } catch (e) { 2855 jqObj = $("<span />").html(string); 2856 } 2857 return jqObj; 2858 } 2859 2860 function isNode(o) { 2861 /* http://stackoverflow.com/a/384380/937891 */ 2862 return !!(typeof Node === "object" ? o instanceof Node : o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string"); 2863 } 2864 2865 2866 $.print = $.fn.print = function() { 2867 // Print a given set of elements 2868 2869 var options, $this, self = this; 2870 2871 // console.log("Printing", this, arguments); 2872 2873 if (self instanceof $) { 2874 // Get the node if it is a jQuery object 2875 self = self.get(0); 2876 } 2877 2878 if (isNode(self)) { 2879 // If `this` is a HTML element, i.e. for 2880 // $(selector).print() 2881 $this = $(self); 2882 if (arguments.length > 0) { 2883 options = arguments[0]; 2884 } 2885 } else { 2886 if (arguments.length > 0) { 2887 // $.print(selector,options) 2888 $this = $(arguments[0]); 2889 if (isNode($this[0])) { 2890 if (arguments.length > 1) { 2891 options = arguments[1]; 2892 } 2893 } else { 2894 // $.print(options) 2895 options = arguments[0]; 2896 $this = $("html"); 2897 } 2898 } else { 2899 // $.print() 2900 $this = $("html"); 2901 } 2902 } 2903 2904 // Default options 2905 var defaults = { 2906 globalStyles: true, 2907 mediaPrint: false, 2908 stylesheet: null, 2909 noPrintSelector: ".no-print", 2910 iframe: true, 2911 append: null, 2912 prepend: null 2913 }; 2914 // Merge with user-options 2915 options = $.extend({}, defaults, (options || {})); 2916 2917 var $styles = $(""); 2918 if (options.globalStyles) { 2919 // Apply the stlyes from the current sheet to the printed page 2920 $styles = $("style, link, meta, title"); 2921 } else if (options.mediaPrint) { 2922 // Apply the media-print stylesheet 2923 $styles = $("link[media=print]"); 2924 } 2925 if (options.stylesheet) { 2926 // Add a custom stylesheet if given 2927 $styles = $.merge($styles, $('<link rel="stylesheet" href="' + options.stylesheet + '">')); 2928 } 2929 2930 // Create a copy of the element to print 2931 var copy = $this.clone(); 2932 // Wrap it in a span to get the HTML markup string 2933 copy = $("<span/>").append(copy); 2934 // Remove unwanted elements 2935 copy.find(options.noPrintSelector).remove(); 2936 // Add in the styles 2937 copy.append($styles.clone()); 2938 // Appedned content 2939 copy.append(getjQueryObject(options.append)); 2940 // Prepended content 2941 copy.prepend(getjQueryObject(options.prepend)); 2942 // Get the HTML markup string 2943 var content = copy.html(); 2944 // Destroy the copy 2945 copy.remove(); 2946 2947 var w, wdoc; 2948 if (options.iframe) { 2949 // Use an iframe for printing 2950 try { 2951 var $iframe = $(options.iframe + "");
2952 var iframeCount = $iframe.length; 2953 if (iframeCount === 0) { 2954 // Create a new iFrame if none is given 2955 $iframe = $('<iframe height="0" width="0" border="0" wmode="Opaque"/>').prependTo('body').css({ 2956 "position": "absolute", 2957 "top": -999, 2958 "left": -999 2959 }); 2960 } 2961 w = $iframe.get(0); 2962 w = w.contentWindow || w.contentDocument || w; 2963 wdoc = w.document || w.contentDocument || w; 2964 wdoc.open(); 2965 wdoc.write(content); 2966 wdoc.close(); 2967 setTimeout(function() { 2968 // Fix for IE : Allow it to render the iframe 2969 w.focus(); 2970 w.print(); 2971 setTimeout(function() { 2972 // Fix for IE 2973 if (iframeCount === 0) { 2974 // Destroy the iframe if created here 2975 $iframe.remove(); 2976 } 2977 }, 100); 2978 }, 250); 2979 } catch (e) { 2980 // Use the pop-up method if iframe fails for some reason 2981 console.error("Failed to print from iframe", e.stack, e.message); 2982 w = window.open(); 2983 w.document.write(content); 2984 w.document.close(); 2985 w.focus(); 2986 w.print(); 2987 w.close(); 2988 } 2989 } else { 2990 // Use a new window for printing 2991 w = window.open(); 2992 w.document.write(content); 2993 w.document.close(); 2994 w.focus(); 2995 w.print(); 2996 w.close(); 2997 } 2998 return this; 2999 }; 3000 3001})(jQuery); 3002 3003(function($) { 3004 3005 /** 3006 * Set all elements within the collection to have the same height. 3007 */ 3008 $.fn.equalHeight = function() { 3009 var heights = []; 3010 $.each(this, function(i, element) { 3011 $element = $(element); 3012 var element_height; 3013 // Should we include the elements padding in it's height? 3014 var includePadding = ($element.css('box-sizing') == 'border-box') || ($element.css('-moz-box-sizing') == 'border-box'); 3015 if (includePadding) { 3016 element_height = $element.innerHeight(); 3017 } else { 3018 element_height = $element.height(); 3019 } 3020 heights.push(element_height); 3021 }); 3022 this.css('height', Math.max.apply(window, heights) + 'px'); 3023 return this; 3024 }; 3025 3026 /** 3027 * Create a grid of equal height elements. 3028 */ 3029 $.fn.equalHeightGrid = function(columns) { 3030 var $tiles = this; 3031 $tiles.css('height', 'auto'); 3032 for (var i = 0; i < $tiles.length; i++) { 3033 if (i % columns === 0) { 3034 var row = $($tiles[i]); 3035 for (var n = 1; n < columns; n++) { 3036 row = row.add($tiles[i + n]); 3037 } 3038 row.equalHeight(); 3039 } 3040 } 3041 return this; 3042 }; 3043 3044 /** 3045 * Detect how many columns there are in a given layout. 3046 */ 3047 $.fn.detectGridColumns = function() { 3048 var offset = 0, 3049 cols = 0; 3050 this.each(function(i, elem) { 3051 var elem_offset = $(elem).offset().top; 3052 if (offset === 0 || elem_offset == offset) { 3053 cols++; 3054 offset = elem_offset; 3055 } else { 3056 return false; 3057 } 3058 }); 3059 return cols; 3060 }; 3061 3062 /** 3063 * Ensure equal heights now, on ready, load and resize. 3064 */ 3065 $.fn.responsiveEqualHeightGrid = function() { 3066 var _this = this; 3067 3068 function syncHeights() { 3069 var cols = _this.detectGridColumns(); 3070 _this.equalHeightGrid(cols); 3071 } 3072 $(window).bind('resize load', syncHeights); 3073 syncHeights(); 3074 return this; 3075 }; 3076 3077})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.