mapbox-wax-77e78c7/0000775000175000017500000000000012021614121013102 5ustar daviddavidmapbox-wax-77e78c7/Makefile0000664000175000017500000000443211730207611014555 0ustar daviddavidUGLIFYJS = ./node_modules/.bin/uglifyjs BANNER = ./node_modules/.bin/banner dist: dist_setup dist/wax.ol.min.js \ dist/wax.g.min.js dist/wax.mm.min.js \ dist/wax.leaf.min.js dist/wax.p.min.js lint dist/wax.ol.min.js: cat build/header.js \ ext/reqwest.min.js \ ext/html-sanitizer-bundle.js \ ext/html-sanitizer-loosen.js \ ext/mustache.js \ connectors/ol/*.js \ control/lib/*.js \ control/ol/*.js > dist/wax.ol.js $(UGLIFYJS) dist/wax.ol.js > dist/wax.ol.min.js dist/wax.g.min.js: cat build/header.js \ ext/reqwest.min.js \ ext/html-sanitizer-bundle.js \ ext/html-sanitizer-loosen.js \ ext/mustache.js \ control/lib/*.js \ control/g/*.js \ connectors/g/*.js > dist/wax.g.js $(UGLIFYJS) dist/wax.g.js > dist/wax.g.min.js dist/wax.mm.min.js: cat build/header.js \ ext/reqwest.min.js \ ext/html-sanitizer-bundle.js \ ext/html-sanitizer-loosen.js \ ext/mustache.js \ control/lib/*.js \ control/mm/*.js \ connectors/mm/*.js > dist/wax.mm.js $(UGLIFYJS) dist/wax.mm.js > dist/wax.mm.min.js dist/wax.leaf.min.js: cat build/header.js \ ext/reqwest.min.js \ ext/html-sanitizer-bundle.js \ ext/html-sanitizer-loosen.js \ ext/mustache.js \ control/lib/*.js \ control/leaf/*.js \ connectors/leaf/*.js > dist/wax.leaf.js $(UGLIFYJS) dist/wax.leaf.js > dist/wax.leaf.min.js dist/wax.p.min.js: cat build/header.js \ ext/reqwest.min.js \ control/lib/*.js \ connectors/p/*.js > dist/wax.p.js $(UGLIFYJS) dist/wax.p.js > dist/wax.p.min.js dist_setup: rm -rf dist rm -rf build mkdir dist mkdir build $(BANNER) package.json > build/header.js ext: -test ! -d ext && mkdir ext wget --no-check-certificate http://openlayers.org/api/2.10/OpenLayers.js -O ext/OpenLayers.js wget --no-check-certificate https://raw.github.com/CloudMade/Leaflet/master/dist/leaflet.js -O ext/leaflet.js wget --no-check-certificate https://raw.github.com/CloudMade/Leaflet/master/dist/leaflet.css -O ext/leaflet.css wget --no-check-certificate https://raw.github.com/CloudMade/Leaflet/master/dist/leaflet.ie.css -O ext/leaflet.ie.css wget --no-check-certificate https://github.com/stamen/modestmaps-js/raw/v0.17.0/modestmaps.min.js -O ext/modestmaps.min.js lint: ./node_modules/.bin/jshint control/lib/*.js control/mm/*.js control/leaf/*.js --config=jshint.json .PHONY: clean ext mapbox-wax-77e78c7/ext/0000775000175000017500000000000012021614121013702 5ustar daviddavidmapbox-wax-77e78c7/ext/html-sanitizer-loosen.js0000664000175000017500000000020411730207611020513 0ustar daviddavid// Loosen restrictions of Caja's // html-sanitizer to allow for styling html4.ATTRIBS['*::style'] = 0; html4.ELEMENTS['style'] = 0; mapbox-wax-77e78c7/ext/html4-defs.js0000664000175000017500000002231711071120647016225 0ustar daviddavid// Copyright (C) 2008 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview * Whitelists of HTML elements and attributes. * * @author mikesamuel@gmail.com */ /** @namespace */ var html4 = {}; /** * HTML element flags. * @enum {number} */ html4.eflags = { OPTIONAL_ENDTAG: 1, BREAKS_FLOW: 2, EMPTY: 4, NAVIGATES: 8, CDATA: 0x10, RCDATA: 0x20, UNSAFE: 0x40, /** * Elements that can be removed from the DOM without changing behavior as long * as their children are folded into the element's parent. * The set of FOLDABLE elements should be kept in sync with * HtmlSanitizer.java#isElementFoldable. */ FOLDABLE: 0x80 }; /** * HTML attribute flags. * @enum {number} */ html4.atype = { SCRIPT: 1, STYLE: 2, IDREF: 3, NAME: 4, NMTOKENS: 5, URI: 6, FRAME: 7 }; /** * Maps HTML4 element names to flag bitsets. * Since this is a whitelist, be sure to do * {@code html4.ELEMENTS.hasOwnProperty} to determine whether or not an element * is allowed. */ html4.ELEMENTS = { 'a' : html4.eflags.NAVIGATES, 'abbr' : 0, 'acronym' : 0, 'address' : 0, 'applet' : html4.eflags.UNSAFE, 'area' : html4.eflags.EMPTY | html4.eflags.NAVIGATES, 'b' : 0, // Changes the meaning of URIs 'base' : html4.eflags.UNSAFE | html4.eflags.EMPTY, // Affects global styles. 'basefont' : html4.eflags.UNSAFE | html4.eflags.EMPTY, 'bdo' : 0, 'big' : 0, 'blockquote' : html4.eflags.BREAKS_FLOW, // Attributes merged into global body. 'body' : (html4.eflags.FOLDABLE | html4.eflags.OPTIONAL_ENDTAG | html4.eflags.UNSAFE), 'br' : html4.eflags.EMPTY | html4.eflags.BREAKS_FLOW, 'button' : 0, 'caption' : 0, 'center' : html4.eflags.BREAKS_FLOW, 'cite' : 0, 'code' : 0, 'col' : html4.eflags.EMPTY, 'colgroup' : html4.eflags.OPTIONAL_ENDTAG, 'dd' : html4.eflags.OPTIONAL_ENDTAG | html4.eflags.BREAKS_FLOW, 'del' : 0, 'dfn' : 0, 'dir' : html4.eflags.BREAKS_FLOW, 'div' : html4.eflags.BREAKS_FLOW, 'dl' : html4.eflags.BREAKS_FLOW, 'dt' : html4.eflags.OPTIONAL_ENDTAG | html4.eflags.BREAKS_FLOW, 'em' : 0, 'fieldset' : 0, 'font' : 0, 'form' : html4.eflags.BREAKS_FLOW | html4.eflags.NAVIGATES, 'frame' : html4.eflags.UNSAFE | html4.eflags.EMPTY, // Attributes merged into global frameset. 'frameset' : html4.eflags.UNSAFE, 'h1' : html4.eflags.BREAKS_FLOW, 'h2' : html4.eflags.BREAKS_FLOW, 'h3' : html4.eflags.BREAKS_FLOW, 'h4' : html4.eflags.BREAKS_FLOW, 'h5' : html4.eflags.BREAKS_FLOW, 'h6' : html4.eflags.BREAKS_FLOW, 'head' : (html4.eflags.FOLDABLE | html4.eflags.OPTIONAL_ENDTAG | html4.eflags.BREAKS_FLOW | html4.eflags.UNSAFE), 'hr' : html4.eflags.EMPTY | html4.eflags.BREAKS_FLOW, 'html' : (html4.eflags.FOLDABLE | html4.eflags.OPTIONAL_ENDTAG | html4.eflags.BREAKS_FLOW | html4.eflags.UNSAFE), 'i' : 0, 'iframe' : html4.eflags.UNSAFE, 'img' : html4.eflags.EMPTY, 'input' : html4.eflags.EMPTY, 'ins' : 0, 'isindex' : (html4.eflags.UNSAFE | html4.eflags.EMPTY | html4.eflags.BREAKS_FLOW | html4.eflags.NAVIGATES), 'kbd' : 0, 'label' : 0, 'legend' : 0, 'li' : html4.eflags.OPTIONAL_ENDTAG | html4.eflags.BREAKS_FLOW, // Can load global styles. 'link' : html4.eflags.UNSAFE | html4.eflags.EMPTY, 'map' : 0, 'menu' : html4.eflags.BREAKS_FLOW, // Can override document headers and encoding, or cause navigation. 'meta' : html4.eflags.UNSAFE | html4.eflags.EMPTY, // Ambiguous tokenization. Content is CDATA/PCDATA depending on browser. 'noframes' : html4.eflags.UNSAFE | html4.eflags.BREAKS_FLOW, // Ambiguous tokenization. Content is CDATA/PCDATA depending on browser. 'noscript' : html4.eflags.UNSAFE, 'object' : html4.eflags.UNSAFE, 'ol' : html4.eflags.BREAKS_FLOW, 'optgroup' : 0, 'option' : html4.eflags.OPTIONAL_ENDTAG, 'p' : html4.eflags.OPTIONAL_ENDTAG | html4.eflags.BREAKS_FLOW, 'param' : html4.eflags.UNSAFE | html4.eflags.EMPTY, 'plaintext' : (html4.eflags.OPTIONAL_ENDTAG | html4.eflags.UNSAFE | html4.eflags.CDATA), 'pre' : html4.eflags.BREAKS_FLOW, 'q' : 0, 's' : 0, 'samp' : 0, 'script' : html4.eflags.UNSAFE | html4.eflags.CDATA, 'select' : 0, 'small' : 0, 'span' : 0, 'strike' : 0, 'strong' : 0, 'style' : html4.eflags.UNSAFE | html4.eflags.CDATA, 'sub' : 0, 'sup' : 0, 'table' : html4.eflags.BREAKS_FLOW, 'tbody' : html4.eflags.OPTIONAL_ENDTAG, 'td' : html4.eflags.OPTIONAL_ENDTAG | html4.eflags.BREAKS_FLOW, 'textarea' : html4.eflags.RCDATA, 'tfoot' : html4.eflags.OPTIONAL_ENDTAG, 'th' : html4.eflags.OPTIONAL_ENDTAG | html4.eflags.BREAKS_FLOW, 'thead' : html4.eflags.OPTIONAL_ENDTAG, 'title' : (html4.eflags.UNSAFE | html4.eflags.BREAKS_FLOW | html4.eflags.RCDATA), 'tr' : html4.eflags.OPTIONAL_ENDTAG | html4.eflags.BREAKS_FLOW, 'tt' : 0, 'u' : 0, 'ul' : html4.eflags.BREAKS_FLOW, 'var' : 0, 'xmp' : html4.eflags.CDATA }; /** * Maps HTML4 attribute names to flag bitsets. */ html4.ATTRIBS = { 'abbr' : 0, 'accept' : 0, 'accept-charset': 0, 'action' : html4.atype.URI, 'align' : 0, 'alink' : 0, 'alt' : 0, 'archive' : html4.atype.URI, 'axis' : 0, 'background' : html4.atype.URI, 'bgcolor' : 0, 'border' : 0, 'cellpadding' : 0, 'cellspacing' : 0, 'char' : 0, 'charoff' : 0, 'charset' : 0, 'checked' : 0, 'cite' : html4.atype.URI, 'class' : html4.atype.NMTOKENS, 'classid' : html4.atype.URI, 'clear' : 0, 'code' : 0, 'codebase' : html4.atype.URI, 'codetype' : 0, 'color' : 0, 'cols' : 0, 'colspan' : 0, 'compact' : 0, 'content' : 0, 'coords' : 0, 'data' : html4.atype.URI, 'datetime' : 0, 'declare' : 0, 'defer' : 0, 'dir' : 0, 'disabled' : 0, 'enctype' : 0, 'face' : 0, 'for' : html4.atype.IDREF, 'frame' : 0, 'frameborder' : 0, 'headers' : 0, 'height' : 0, 'href' : html4.atype.URI, 'hreflang' : 0, 'hspace' : 0, //'http-equiv' : 0, // unsafe 'id' : html4.atype.IDREF, 'ismap' : 0, 'label' : 0, 'lang' : 0, 'language' : 0, 'link' : 0, 'longdesc' : html4.atype.URI, 'marginheight' : 0, 'marginwidth' : 0, 'maxlength' : 0, 'media' : 0, 'method' : 0, 'multiple' : 0, 'name' : html4.atype.NAME, 'nohref' : 0, 'noresize' : 0, 'noshade' : 0, 'nowrap' : 0, 'object' : 0, 'onblur' : html4.atype.SCRIPT, 'onchange' : html4.atype.SCRIPT, 'onclick' : html4.atype.SCRIPT, 'ondblclick' : html4.atype.SCRIPT, 'onfocus' : html4.atype.SCRIPT, 'onkeydown' : html4.atype.SCRIPT, 'onkeypress' : html4.atype.SCRIPT, 'onkeyup' : html4.atype.SCRIPT, 'onload' : html4.atype.SCRIPT, 'onmousedown' : html4.atype.SCRIPT, 'onmousemove' : html4.atype.SCRIPT, 'onmouseout' : html4.atype.SCRIPT, 'onmouseover' : html4.atype.SCRIPT, 'onmouseup' : html4.atype.SCRIPT, 'onreset' : html4.atype.SCRIPT, 'onselect' : html4.atype.SCRIPT, 'onsubmit' : html4.atype.SCRIPT, 'onunload' : html4.atype.SCRIPT, 'profile' : html4.atype.URI, 'prompt' : 0, 'readonly' : 0, 'rel' : 0, 'rev' : 0, 'rows' : 0, 'rowspan' : 0, 'rules' : 0, 'scheme' : 0, 'scope' : 0, 'scrolling' : 0, 'selected' : 0, 'shape' : 0, 'size' : 0, 'span' : 0, 'src' : html4.atype.URI, 'standby' : 0, 'start' : 0, 'style' : html4.atype.STYLE, 'summary' : 0, 'tabindex' : 0, 'target' : html4.atype.FRAME, 'text' : 0, 'title' : 0, 'type' : 0, 'usemap' : html4.atype.URI, 'valign' : 0, 'value' : 0, 'valuetype' : 0, 'version' : 0, 'vlink' : 0, 'vspace' : 0, 'width' : 0 }; mapbox-wax-77e78c7/ext/html-sanitizer.js0000664000175000017500000010435712002104352017223 0ustar daviddavid// Copyright (C) 2006 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview * An HTML sanitizer that can satisfy a variety of security policies. * *

* The HTML sanitizer is built around a SAX parser and HTML element and * attributes schemas. * * If the cssparser is loaded, inline styles are sanitized using the * css property and value schemas. Else they are remove during * sanitization. * * If it exists, uses parseCssDeclarations, sanitizeCssProperty, cssSchema * * @author mikesamuel@gmail.com * @author jasvir@gmail.com * \@requires html4 * \@overrides window * \@provides html, html_sanitize */ // The Turkish i seems to be a non-issue, but abort in case it is. if ('I'.toLowerCase() !== 'i') { throw 'I/i problem'; } /** * \@namespace */ var html = (function(html4) { // For closure compiler var parseCssDeclarations, sanitizeCssProperty, cssSchema; if ('undefined' !== typeof window) { parseCssDeclarations = window['parseCssDeclarations']; sanitizeCssProperty = window['sanitizeCssProperty']; cssSchema = window['cssSchema']; } // The keys of this object must be 'quoted' or JSCompiler will mangle them! var ENTITIES = { 'lt': '<', 'gt': '>', 'amp': '&', 'nbsp': '\240', 'quot': '"', 'apos': '\'' }; var decimalEscapeRe = /^#(\d+)$/; var hexEscapeRe = /^#x([0-9A-Fa-f]+)$/; /** * Decodes an HTML entity. * * {\@updoc * $ lookupEntity('lt') * # '<' * $ lookupEntity('GT') * # '>' * $ lookupEntity('amp') * # '&' * $ lookupEntity('nbsp') * # '\xA0' * $ lookupEntity('apos') * # "'" * $ lookupEntity('quot') * # '"' * $ lookupEntity('#xa') * # '\n' * $ lookupEntity('#10') * # '\n' * $ lookupEntity('#x0a') * # '\n' * $ lookupEntity('#010') * # '\n' * $ lookupEntity('#x00A') * # '\n' * $ lookupEntity('Pi') // Known failure * # '\u03A0' * $ lookupEntity('pi') // Known failure * # '\u03C0' * } * * @param {string} name the content between the '&' and the ';'. * @return {string} a single unicode code-point as a string. */ function lookupEntity(name) { name = name.toLowerCase(); // TODO: π is different from Π if (ENTITIES.hasOwnProperty(name)) { return ENTITIES[name]; } var m = name.match(decimalEscapeRe); if (m) { return String.fromCharCode(parseInt(m[1], 10)); } else if (!!(m = name.match(hexEscapeRe))) { return String.fromCharCode(parseInt(m[1], 16)); } return ''; } function decodeOneEntity(_, name) { return lookupEntity(name); } var nulRe = /\0/g; function stripNULs(s) { return s.replace(nulRe, ''); } var ENTITY_RE_1 = /&(#[0-9]+|#[xX][0-9A-Fa-f]+|\w+);/g; var ENTITY_RE_2 = /^(#[0-9]+|#[xX][0-9A-Fa-f]+|\w+);/; /** * The plain text of a chunk of HTML CDATA which possibly containing. * * {\@updoc * $ unescapeEntities('') * # '' * $ unescapeEntities('hello World!') * # 'hello World!' * $ unescapeEntities('1 < 2 && 4 > 3 ') * # '1 < 2 && 4 > 3\n' * $ unescapeEntities('<< <- unfinished entity>') * # '<< <- unfinished entity>' * $ unescapeEntities('/foo?bar=baz©=true') // & often unescaped in URLS * # '/foo?bar=baz©=true' * $ unescapeEntities('pi=ππ, Pi=Π\u03A0') // FIXME: known failure * # 'pi=\u03C0\u03c0, Pi=\u03A0\u03A0' * } * * @param {string} s a chunk of HTML CDATA. It must not start or end inside * an HTML entity. */ function unescapeEntities(s) { return s.replace(ENTITY_RE_1, decodeOneEntity); } var ampRe = /&/g; var looseAmpRe = /&([^a-z#]|#(?:[^0-9x]|x(?:[^0-9a-f]|$)|$)|$)/gi; var ltRe = /[<]/g; var gtRe = />/g; var quotRe = /\"/g; /** * Escapes HTML special characters in attribute values. * * {\@updoc * $ escapeAttrib('') * # '' * $ escapeAttrib('"<<&==&>>"') // Do not just escape the first occurrence. * # '"<<&==&>>"' * $ escapeAttrib('Hello !') * # 'Hello <World>!' * } */ function escapeAttrib(s) { return ('' + s).replace(ampRe, '&').replace(ltRe, '<') .replace(gtRe, '>').replace(quotRe, '"'); } /** * Escape entities in RCDATA that can be escaped without changing the meaning. * {\@updoc * $ normalizeRCData('1 < 2 && 3 > 4 && 5 < 7&8') * # '1 < 2 && 3 > 4 && 5 < 7&8' * } */ function normalizeRCData(rcdata) { return rcdata .replace(looseAmpRe, '&$1') .replace(ltRe, '<') .replace(gtRe, '>'); } // TODO(felix8a): validate sanitizer regexs against the HTML5 grammar at // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html // We initially split input so that potentially meaningful characters // like '<' and '>' are separate tokens, using a fast dumb process that // ignores quoting. Then we walk that token stream, and when we see a // '<' that's the start of a tag, we use ATTR_RE to extract tag // attributes from the next token. That token will never have a '>' // character. However, it might have an unbalanced quote character, and // when we see that, we combine additional tokens to balance the quote. var ATTR_RE = new RegExp( '^\\s*' + '([-.:\\w]+)' + // 1 = Attribute name '(?:' + ( '\\s*(=)\\s*' + // 2 = Is there a value? '(' + ( // 3 = Attribute value // TODO(felix8a): maybe use backref to match quotes '(\")[^\"]*(\"|$)' + // 4, 5 = Double-quoted string '|' + '(\')[^\']*(\'|$)' + // 6, 7 = Single-quoted string '|' + // Positive lookahead to prevent interpretation of // as // TODO(felix8a): might be able to drop this case '(?=[a-z][-\\w]*\\s*=)' + '|' + // Unquoted value that isn't an attribute name // (since we didn't match the positive lookahead above) '[^\"\'\\s]*' ) + ')' ) + ')?', 'i'); // false on IE<=8, true on most other browsers var splitWillCapture = ('a,b'.split(/(,)/).length === 3); // bitmask for tags with special parsing, like mapbox-wax-77e78c7/index.js0000664000175000017500000000000011730207611014545 0ustar daviddavidmapbox-wax-77e78c7/connectors/0000775000175000017500000000000011730207611015267 5ustar daviddavidmapbox-wax-77e78c7/connectors/leaf/0000775000175000017500000000000011730207611016176 5ustar daviddavidmapbox-wax-77e78c7/connectors/leaf/connector.js0000664000175000017500000000052711730207611020532 0ustar daviddavidwax = wax || {}; wax.leaf = wax.leaf || {}; wax.leaf.connector = L.TileLayer.extend({ initialize: function(options) { options = options || {}; options.minZoom = options.minzoom || 0; options.maxZoom = options.maxzoom || 22; L.TileLayer.prototype.initialize.call(this, options.tiles[0], options); } }); mapbox-wax-77e78c7/connectors/ol/0000775000175000017500000000000011730207611015701 5ustar daviddavidmapbox-wax-77e78c7/connectors/ol/connector.js0000664000175000017500000000111611730207611020230 0ustar daviddavidvar wax = wax || {}; wax.ol = wax.ol || {}; wax.ol.connector = function(tilejson) { for (var i = 0; i < tilejson.tiles.length; i++) { tilejson.tiles[i] = tilejson.tiles[i] .replace('{z}', '${z}') .replace('{x}', '${x}') .replace('{y}', '${y}'); } return new OpenLayers.Layer.XYZ( tilejson.name, tilejson.tiles, { sphericalMercator: true, zoomOffset: tilejson.minzoom, numZoomLevels: tilejson.maxzoom - tilejson.minzoom, attribution: tilejson.attribution }); }; mapbox-wax-77e78c7/connectors/mm/0000775000175000017500000000000011730207611015700 5ustar daviddavidmapbox-wax-77e78c7/connectors/mm/waxconnector.js0000664000175000017500000000347311730207611020757 0ustar daviddavidvar wax = wax || {}; wax.mm = wax.mm || {}; // A layer connector for Modest Maps conformant to TileJSON // https://github.com/mapbox/tilejson wax.mm._provider = function(options) { this.options = { tiles: options.tiles, scheme: options.scheme || 'xyz', minzoom: options.minzoom || 0, maxzoom: options.maxzoom || 22, bounds: options.bounds || [-180, -90, 180, 90] }; }; wax.mm._provider.prototype = { outerLimits: function() { return [ this.locationCoordinate( new MM.Location( this.options.bounds[0], this.options.bounds[1])).zoomTo(this.options.minzoom), this.locationCoordinate( new MM.Location( this.options.bounds[2], this.options.bounds[3])).zoomTo(this.options.maxzoom) ]; }, getTile: function(c) { if (!(coord = this.sourceCoordinate(c))) return null; if (coord.zoom < this.options.minzoom || coord.zoom > this.options.maxzoom) return null; coord.row = (this.options.scheme === 'tms') ? Math.pow(2, coord.zoom) - coord.row - 1 : coord.row; var u = this.options.tiles[parseInt(Math.pow(2, coord.zoom) * coord.row + coord.column, 10) % this.options.tiles.length] .replace('{z}', coord.zoom.toFixed(0)) .replace('{x}', coord.column.toFixed(0)) .replace('{y}', coord.row.toFixed(0)); if (wax._ && wax._.bw) { u = u.replace('.png', wax._.bw_png) .replace('.jpg', wax._.bw_jpg); } return u; } }; if (MM) { MM.extend(wax.mm._provider, MM.MapProvider); } wax.mm.connector = function(options) { var x = new wax.mm._provider(options); return new MM.Layer(x); }; mapbox-wax-77e78c7/connectors/p/0000775000175000017500000000000011730207611015526 5ustar daviddavidmapbox-wax-77e78c7/connectors/p/connector.js0000664000175000017500000000052311730207611020056 0ustar daviddavid;var wax = wax || {}; wax.p = wax.p || {}; wax.p.connector = function(tilejson) { var po = org.polymaps; if (tilejson.scheme === 'tms') { throw new Error('Wax only supports XYZ TileJSON urls.'); } return po.image().url(tilejson.tiles[0].replace(/\{\w\}/g, function(m) { return m.toUpperCase(); })); }; mapbox-wax-77e78c7/connectors/g/0000775000175000017500000000000011730207611015515 5ustar daviddavidmapbox-wax-77e78c7/connectors/g/connector.js0000664000175000017500000000452311730207611020051 0ustar daviddavid// Wax for Google Maps API v3 // -------------------------- // Wax header var wax = wax || {}; wax.g = wax.g || {}; // Wax Google Maps MapType: takes an object of options in the form // // { // name: '', // filetype: '.png', // layerName: 'world-light', // alt: '', // zoomRange: [0, 18], // baseUrl: 'a url', // } wax.g.connector = function(options) { options = options || {}; this.options = { tiles: options.tiles, scheme: options.scheme || 'xyz', blankImage: options.blankImage }; this.minZoom = options.minzoom || 0; this.maxZoom = options.maxzoom || 22; this.name = options.name || ''; this.description = options.description || ''; // non-configurable options this.interactive = true; this.tileSize = new google.maps.Size(256, 256); // DOM element cache this.cache = {}; }; // Get a tile element from a coordinate, zoom level, and an ownerDocument. wax.g.connector.prototype.getTile = function(coord, zoom, ownerDocument) { var key = zoom + '/' + coord.x + '/' + coord.y; if (!this.cache[key]) { var img = this.cache[key] = new Image(256, 256); this.cache[key].src = this.getTileUrl(coord, zoom); this.cache[key].setAttribute('gTileKey', key); this.cache[key].onerror = function() { img.style.display = 'none'; }; } return this.cache[key]; }; // Remove a tile that has fallen out of the map's viewport. // // TODO: expire cache data in the gridmanager. wax.g.connector.prototype.releaseTile = function(tile) { var key = tile.getAttribute('gTileKey'); this.cache[key] && delete this.cache[key]; tile.parentNode && tile.parentNode.removeChild(tile); }; // Get a tile url, based on x, y coordinates and a z value. wax.g.connector.prototype.getTileUrl = function(coord, z) { // Y coordinate is flipped in Mapbox, compared to Google var mod = Math.pow(2, z), y = (this.options.scheme === 'tms') ? (mod - 1) - coord.y : coord.y, x = (coord.x % mod); x = (x < 0) ? (coord.x % mod) + mod : x; if (y < 0) return this.options.blankImage; return this.options.tiles [parseInt(x + y, 10) % this.options.tiles.length] .replace('{z}', z) .replace('{x}', x) .replace('{y}', y); }; mapbox-wax-77e78c7/control/0000775000175000017500000000000011730207611014572 5ustar daviddavidmapbox-wax-77e78c7/control/leaf/0000775000175000017500000000000011730207611015501 5ustar daviddavidmapbox-wax-77e78c7/control/leaf/interaction.js0000664000175000017500000001760311730207611020365 0ustar daviddavidwax = wax || {}; wax.leaf = wax.leaf || {}; // If not given, the `wax.tooltip` library will be expected. // * `clickAction` (optional): **full** or **location**: default is // **full**. wax.leaf.interaction = function(map, tilejson, options) { tilejson = tilejson || {}; options = options || {}; var waxGM = wax.GridManager(tilejson), callbacks = options.callbacks || new wax.tooltip(options), clickAction = options.clickAction || ['full', 'location'], addListener = L.DomEvent.addListener, removeListener = L.DomEvent.removeListener, clickHandler = options.clickHandler || function(url) { window.location = url; }, interaction = {}, _downLock = false, _clickTimeout = false, _container = map._container, touchable = ('ontouchstart' in document.documentElement), // Active feature _af, // Down event _d, // Touch tolerance tol = 4, tileGrid; // Search through `.tiles` and determine the position, // from the top-left of the **document**, and cache that data // so that `mousemove` events don't always recalculate. function getTileGrid() { // TODO: don't build for tiles outside of viewport // Touch interaction leads to intermediate //var zoomLayer = map.createOrGetLayer(Math.round(map.getZoom())); //?what is this doing? // Calculate a tile grid and cache it, by using the `.tiles` // element on this map. return tileGrid || (tileGrid = (function(layers) { var o = []; for (var layerId in layers) { // This only supports tiled layers if (layers[layerId]._tiles) { for (var tile in layers[layerId]._tiles) { var offset = wax.util.offset(layers[layerId]._tiles[tile]); o.push([offset.top, offset.left, layers[layerId]._tiles[tile]]); } } } return o; })(map._layers)); } // When the map moves, the tile grid is no longer valid. function clearTileGrid(map, e) { tileGrid = null; } function getTile(e) { for (var i = 0, grid = getTileGrid(); i < grid.length; i++) { if ((grid[i][0] < e.y) && ((grid[i][0] + 256) > e.y) && (grid[i][1] < e.x) && ((grid[i][1] + 256) > e.x)) return grid[i][2]; } return false; } // Clear the double-click timeout to prevent double-clicks from // triggering popups. function killTimeout() { if (_clickTimeout) { window.clearTimeout(_clickTimeout); _clickTimeout = null; return true; } else { return false; } } function onMove(e) { // If the user is actually dragging the map, exit early // to avoid performance hits. if (_downLock) return; var pos = wax.util.eventoffset(e), tile = getTile(pos), feature; if (tile) waxGM.getGrid(tile.src, function(err, g) { if (err || !g) return; feature = g.tileFeature(pos.x, pos.y, tile, { format: 'teaser' }); if (feature) { if (feature && _af !== feature) { _af = feature; callbacks.out(_container); callbacks.over(feature, _container); } else if (!feature) { _af = null; callbacks.out(_container); } } else { _af = null; callbacks.out(_container); } }); } // A handler for 'down' events - which means `mousedown` and `touchstart` function onDown(e) { // Ignore double-clicks by ignoring clicks within 300ms of // each other. if (killTimeout()) { return; } // Prevent interaction offset calculations happening while // the user is dragging the map. // // Store this event so that we can compare it to the // up event _downLock = true; _d = wax.util.eventoffset(e); if (e.type === 'mousedown') { addListener(_container, 'mouseup', onUp, this); // Only track single-touches. Double-touches will not affect this // control } else if (e.type === 'touchstart' && e.touches.length === 1) { // turn this into touch-mode. Fallback to teaser and full. clickAction = ['full', 'teaser']; // Don't make the user click close if they hit another tooltip if (callbacks._currentTooltip) { callbacks.hideTooltip(callbacks._currentTooltip); } // Touch moves invalidate touches addListener(_container, 'touchend', onUp, this); addListener(_container, 'touchmove', touchCancel, this); } } function touchCancel() { removeListener(_container, 'touchend', onUp); removeListener(_container, 'touchmove', onUp); _downLock = false; } function onUp(e) { var pos = wax.util.eventoffset(e); _downLock = false; removeListener(_container, 'mouseup', onUp); if (_container.ontouchend) { removeListener(_container, 'touchend', onUp); removeListener(_container, 'touchmove', _touchCancel); } if (e.type === 'touchend') { // If this was a touch and it survived, there's no need to avoid a double-tap click(e, _d); } else if (Math.round(pos.y / tol) === Math.round(_d.y / tol) && Math.round(pos.x / tol) === Math.round(_d.x / tol)) { // Contain the event data in a closure. _clickTimeout = window.setTimeout( function() { _clickTimeout = null; click(e, pos); }, 300); } return onUp; } // Handle a click event. Takes a second function click(e, pos) { var tile = getTile(pos), feature; if (tile) waxGM.getGrid(tile.src, function(err, g) { for (var i = 0; g && (i < clickAction.length); i++) { feature = g.tileFeature(pos.x, pos.y, tile, { format: clickAction[i] }); if (feature) { switch (clickAction[i]) { case 'full': case 'teaser': // clickAction can be teaser in touch interaction return callbacks.click(feature, _container, 0, e); case 'location': return clickHandler(feature); } } } }); } // Attach listeners to the map interaction.add = function() { var l = ['moveend', 'layerswitched']; for (var i = 0; i < l.length; i++) { map.on(l[i], clearTileGrid); } addListener(_container, 'mousemove', onMove); addListener(_container, 'mousedown', onDown); if (touchable) { addListener(_container, 'touchstart', onDown); } return this; }; // Remove this control from the map. interaction.remove = function() { var l = ['moveend', 'layerswitched']; for (var i = 0; i < l.length; i++) { map.off(l[i], clearTileGrid); } removeListener(_container, 'mousemove', onMove); removeListener(_container, 'mousedown', onDown); if (touchable) { removeListener(_container, 'touchstart', onDown); } if (callbacks._currentTooltip) { callbacks.hideTooltip(callbacks._currentTooltip); } return this; }; // Ensure chainability return interaction.add(map); }; mapbox-wax-77e78c7/control/ol/0000775000175000017500000000000011730207611015204 5ustar daviddavidmapbox-wax-77e78c7/control/ol/interaction.js0000664000175000017500000001627411730207611020073 0ustar daviddavid// Wax header var wax = wax || {}; wax.ol = wax.ol || {}; var addEv = function(element, name, observer) { if (element.addEventListener) { element.addEventListener(name, observer, false); } else if (element.attachEvent) { element.attachEvent('on' + name, observer); } }; // An interaction toolkit for tiles that implement the // [MBTiles UTFGrid spec](https://github.com/mapbox/mbtiles-spec) wax.ol.Interaction = OpenLayers.Class(OpenLayers.Control, { feature: {}, handlerOptions: null, handlers: null, gm: new wax.GridManager(), initialize: function(tilejson, options) { this.options = options || {}; this.clickAction = this.options.clickAction || 'full'; this.gm = new wax.GridManager(tilejson); OpenLayers.Control.prototype.initialize.apply(this, [this.options || {}]); this.callbacks = this.options.callbacks || new wax.tooltip(); }, setMap: function(map) { addEv(map.viewPortDiv, 'mousemove', wax.util.bind(this.getInfoForHover, this)); addEv(map.viewPortDiv, 'mouseout', wax.util.bind(this.resetLayers, this)); this.clickHandler = new OpenLayers.Handler.Click( this, { click: this.getInfoForClick } ); this.clickHandler.setMap(map); this.clickHandler.activate(); map.events.on({ addlayer: this.resetLayers, changelayer: this.resetLayers, removelayer: this.resetLayers, changebaselayer: this.resetLayers, scope: this }); OpenLayers.Control.prototype.setMap.apply(this, arguments); }, // Get an Array of the stack of tiles under the mouse. // This operates with pixels only, since there's no way // to bubble through an element which is sitting on the map // (like an SVG overlay). // // If no tiles are under the mouse, returns an empty array. getTileStack: function(layers, pos) { // If we don't have both an event and some tiles, it's nothing. if (!layers || !pos) return []; var tiles = []; layerfound: for (var j = 0; j < layers.length; j++) { for (var x = 0; x < layers[j].grid.length; x++) { for (var y = 0; y < layers[j].grid[x].length; y++) { var divpos; if (layers[j].grid[x][y].imgDiv) { divpos = wax.util.offset(layers[j].grid[x][y].imgDiv); } else { divpos = wax.util.offset(layers[j].grid[x][y].frame); } if (divpos && ((divpos.top < pos.y) && ((divpos.top + 256) > pos.y) && (divpos.left < pos.x) && ((divpos.left + 256) > pos.x))) { tiles.push(layers[j].grid[x][y]); continue layerfound; } } } } return tiles; }, // Get all interactable layers viableLayers: function() { if (this._viableLayers) return this._viableLayers; this._viableLayers = []; for (var i in this.map.layers) { // TODO: make better indication of whether // this is an interactive layer if ((this.map.layers[i].visibility === true) && (this.map.layers[i].CLASS_NAME === 'OpenLayers.Layer.TMS')) { this._viableLayers.push(this.map.layers[i]); } } return this._viableLayers; }, resetLayers: function(evt) { this._viableLayers = null; // Fix a condition in which mouseout is called, but the user is really mousing // over to a different tile. var newTarget = evt.relatedTarget || evt.toElement; if (newTarget && newTarget.className !== 'olTileImage') { this.callbacks.out(this.map.viewPortDiv); } }, // React to a click mouse event // This is the `pause` handler attached to the map. getInfoForClick: function(evt) { // If there's no event, this handler should not continue. if (!evt) return; var layers = this.viableLayers(), pos = wax.util.eventoffset(evt), tiles = this.getTileStack(this.viableLayers(), pos), feature = null, g = null; var that = this; for (var t = 0; t < tiles.length; t++) { if (!tiles[t].url) continue; this.gm.getGrid(tiles[t].url, function(err, g) { if (!g) return; var feature = g.tileFeature(pos.x, pos.y, tiles[t].frame || tiles[t].imgDiv, { format: that.clickAction }); if (feature) { switch (that.clickAction) { case 'full': that.callbacks.click(feature, tiles[t].layer.map.viewPortDiv, t); break; case 'location': window.location = feature; break; } } }); } }, // React to a hover mouse event, by finding all tiles, // finding features, and calling `this.callbacks[]` // This is the `click` handler attached to the map. getInfoForHover: function(evt) { // If there's no event, this handler should not proceed. if (!evt) return; var options = { format: 'teaser' }, layers = this.viableLayers(), pos = wax.util.eventoffset(evt), tiles = this.getTileStack(this.viableLayers(), pos), feature = null, g = null; var that = this; for (var t = 0; t < tiles.length; t++) { if (!tiles[t].url) continue; // This features has already been loaded, or // is currently being requested. this.gm.getGrid(tiles[t].url, function(err, g) { if (g && tiles[t]) { var feature = g.tileFeature(pos.x, pos.y, tiles[t].frame || tiles[t].imgDiv, options); if (feature) { if (!tiles[t]) return; if (feature && that.feature[t] !== feature) { that.feature[t] = feature; that.callbacks.out(tiles[t].layer.map.div); that.callbacks.over(feature, tiles[t].layer.map.div, evt); } else if (!feature) { that.feature[t] = null; that.callbacks.out(tiles[t].layer.map.div); } } else { // Request this feature // TODO(tmcw) re-add layer // Only nix this tooltip if the current tooltip is // owned by this layer if (that.feature[t]) { that.callbacks.out(tiles[t].layer.map.div); } that.feature[t] = null; } } }); } }, CLASS_NAME: 'wax.ol.Interaction' }); mapbox-wax-77e78c7/control/ol/legend.js0000664000175000017500000000243011730207611016777 0ustar daviddavid// Wax: Legend Control // ------------------- // Wax header var wax = wax || {}; wax.ol = wax.ol || {}; wax.ol.Legend = OpenLayers.Class(OpenLayers.Control, { CLASS_NAME: 'wax.ol.Legend', legend: null, options: null, initialize: function(options) { this.options = options || {}; OpenLayers.Control.prototype.initialize.apply(this, [options || {}]); }, activate: function() { this.legend = new wax.Legend(this.map.viewPortDiv, this.options.container); return OpenLayers.Control.prototype.activate.apply(this, arguments); }, setMap: function(map) { OpenLayers.Control.prototype.setMap.apply(this, arguments); this.activate(); this.map.events.on({ 'addlayer': this.setLegend, 'changelayer': this.setLegend, 'removelayer': this.setLegend, 'changebaselayer': this.setLegend, scope: this }); }, setLegend: function() { var urls = []; for (var i = 0; i < this.map.layers.length; i++) { var layer = this.map.layers[i]; if (layer && layer.getURL && layer.visibility) { urls.push(layer.getURL(new OpenLayers.Bounds())); } } this.legend.render(urls); } }); mapbox-wax-77e78c7/control/mm/0000775000175000017500000000000011730207611015203 5ustar daviddavidmapbox-wax-77e78c7/control/mm/bwdetect.js0000664000175000017500000000117711730207611017350 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; wax._ = {}; // Bandwidth Detection // ------------------ wax.mm.bwdetect = function(map, options) { options = options || {}; var lowpng = options.png || '.png128', lowjpg = options.jpg || '.jpg70', bw = false; wax._.bw_png = lowpng; wax._.bw_jpg = lowjpg; return wax.bwdetect(options, function(x) { wax._.bw = !x; for (var i = 0; i < map.layers.length; i++) { if (map.getLayerAt(i).provider instanceof wax.mm.connector) { map.getLayerAt(i).setProvider(map.getLayerAt(i).provider); } } }); }; mapbox-wax-77e78c7/control/mm/fullscreen.js0000664000175000017500000000425211730207611017706 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; // Fullscreen // ---------- // A simple fullscreen control for Modest Maps // Add zoom links, which can be styled as buttons, to a `modestmaps.Map` // control. This function can be used chaining-style with other // chaining-style controls. wax.mm.fullscreen = function(map) { // true: fullscreen // false: minimized var fullscreened = false, fullscreen = {}, a, body = document.body, smallSize; function click(e) { if (e) MM.cancelEvent(e); if (fullscreened) { fullscreen.original(); } else { fullscreen.full(); } } function ss(w, h) { map.dimensions = new MM.Point(w, h); map.parent.style.width = Math.round(map.dimensions.x) + 'px'; map.parent.style.height = Math.round(map.dimensions.y) + 'px'; map.dispatchCallback('resized', map.dimensions); } // Modest Maps demands an absolute height & width, and doesn't auto-correct // for changes, so here we save the original size of the element and // restore to that size on exit from fullscreen. fullscreen.add = function(map) { a = document.createElement('a'); a.className = 'wax-fullscreen'; a.href = '#fullscreen'; a.innerHTML = 'fullscreen'; MM.addEvent(a, 'click', click); return this; }; fullscreen.full = function() { if (fullscreened) { return; } else { fullscreened = true; } smallSize = [map.parent.offsetWidth, map.parent.offsetHeight]; map.parent.className += ' wax-fullscreen-map'; body.className += ' wax-fullscreen-view'; ss(map.parent.offsetWidth, map.parent.offsetHeight); }; fullscreen.original = function() { if (!fullscreened) { return; } else { fullscreened = false; } map.parent.className = map.parent.className.replace(' wax-fullscreen-map', ''); body.className = body.className.replace(' wax-fullscreen-view', ''); ss(smallSize[0], smallSize[1]); }; fullscreen.appendTo = function(elem) { wax.util.$(elem).appendChild(a); return this; }; return fullscreen.add(map); }; mapbox-wax-77e78c7/control/mm/boxselector.js0000664000175000017500000001007111730207611020071 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; // Box Selector // ------------ wax.mm.boxselector = function(map, tilejson, opts) { var mouseDownPoint = null, callback = ((typeof opts === 'function') ? opts : opts.callback), boxDiv, addEvent = MM.addEvent, removeEvent = MM.removeEvent, box, boxselector = {}; function getMousePoint(e) { // start with just the mouse (x, y) var point = new MM.Point(e.clientX, e.clientY); // correct for scrolled document point.x += document.body.scrollLeft + document.documentElement.scrollLeft; point.y += document.body.scrollTop + document.documentElement.scrollTop; // correct for nested offsets in DOM for (var node = map.parent; node; node = node.offsetParent) { point.x -= node.offsetLeft; point.y -= node.offsetTop; } return point; } function mouseDown(e) { if (!e.shiftKey) return; mouseDownPoint = getMousePoint(e); boxDiv.style.left = mouseDownPoint.x + 'px'; boxDiv.style.top = mouseDownPoint.y + 'px'; addEvent(map.parent, 'mousemove', mouseMove); addEvent(map.parent, 'mouseup', mouseUp); map.parent.style.cursor = 'crosshair'; return MM.cancelEvent(e); } function mouseMove(e) { var point = getMousePoint(e), style = boxDiv.style; style.display = 'block'; if (point.x < mouseDownPoint.x) { style.left = point.x + 'px'; } else { style.left = mouseDownPoint.x + 'px'; } if (point.y < mouseDownPoint.y) { style.top = point.y + 'px'; } else { style.top = mouseDownPoint.y + 'px'; } style.width = Math.abs(point.x - mouseDownPoint.x) + 'px'; style.height = Math.abs(point.y - mouseDownPoint.y) + 'px'; return MM.cancelEvent(e); } function mouseUp(e) { var point = getMousePoint(e), l1 = map.pointLocation(point), l2 = map.pointLocation(mouseDownPoint); // Format coordinates like mm.map.getExtent(). boxselector.extent([ new MM.Location( Math.max(l1.lat, l2.lat), Math.min(l1.lon, l2.lon)), new MM.Location( Math.min(l1.lat, l2.lat), Math.max(l1.lon, l2.lon)) ]); removeEvent(map.parent, 'mousemove', mouseMove); removeEvent(map.parent, 'mouseup', mouseUp); map.parent.style.cursor = 'auto'; } function drawbox(map, e) { if (!boxDiv || !box) return; var br = map.locationPoint(box[1]), tl = map.locationPoint(box[0]), style = boxDiv.style; style.display = 'block'; style.height = 'auto'; style.width = 'auto'; style.left = Math.max(0, tl.x) + 'px'; style.top = Math.max(0, tl.y) + 'px'; style.right = Math.max(0, map.dimensions.x - br.x) + 'px'; style.bottom = Math.max(0, map.dimensions.y - br.y) + 'px'; } boxselector.extent = function(x, silent) { if (!x) return box; box = [ new MM.Location( Math.max(x[0].lat, x[1].lat), Math.min(x[0].lon, x[1].lon)), new MM.Location( Math.min(x[0].lat, x[1].lat), Math.max(x[0].lon, x[1].lon)) ]; drawbox(map); if (!silent) callback(box); }; boxselector.add = function(map) { boxDiv = boxDiv || document.createElement('div'); boxDiv.id = map.parent.id + '-boxselector-box'; boxDiv.className = 'boxselector-box'; map.parent.appendChild(boxDiv); addEvent(map.parent, 'mousedown', mouseDown); map.addCallback('drawn', drawbox); return this; }; boxselector.remove = function() { map.parent.removeChild(boxDiv); removeEvent(map.parent, 'mousedown', mouseDown); map.removeCallback('drawn', drawbox); }; return boxselector.add(map); }; mapbox-wax-77e78c7/control/mm/attribution.js0000664000175000017500000000125311730207611020106 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; // Attribution // ----------- // Attribution wrapper for Modest Maps. wax.mm.attribution = function(map, tilejson) { tilejson = tilejson || {}; var a, // internal attribution control attribution = {}; attribution.element = function() { return a.element(); }; attribution.appendTo = function(elem) { wax.util.$(elem).appendChild(a.element()); return this; }; attribution.init = function() { a = wax.attribution(); a.set(tilejson.attribution); a.element().className = 'wax-attribution wax-mm'; return this; }; return attribution.init(); }; mapbox-wax-77e78c7/control/mm/zoombox.js0000664000175000017500000000573611730207611017251 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; // ZoomBox // ------- // An OL-style ZoomBox control, from the Modest Maps example. wax.mm.zoombox = function(map) { // TODO: respond to resize var zoombox = {}, drawing = false, box, mouseDownPoint = null; function getMousePoint(e) { // start with just the mouse (x, y) var point = new MM.Point(e.clientX, e.clientY); // correct for scrolled document point.x += document.body.scrollLeft + document.documentElement.scrollLeft; point.y += document.body.scrollTop + document.documentElement.scrollTop; // correct for nested offsets in DOM for (var node = map.parent; node; node = node.offsetParent) { point.x -= node.offsetLeft; point.y -= node.offsetTop; } return point; } function mouseUp(e) { if (!drawing) return; drawing = false; var point = getMousePoint(e); var l1 = map.pointLocation(point), l2 = map.pointLocation(mouseDownPoint); map.setExtent([l1, l2]); box.style.display = 'none'; MM.removeEvent(map.parent, 'mousemove', mouseMove); MM.removeEvent(map.parent, 'mouseup', mouseUp); map.parent.style.cursor = 'auto'; } function mouseDown(e) { if (!(e.shiftKey && !this.drawing)) return; drawing = true; mouseDownPoint = getMousePoint(e); box.style.left = mouseDownPoint.x + 'px'; box.style.top = mouseDownPoint.y + 'px'; MM.addEvent(map.parent, 'mousemove', mouseMove); MM.addEvent(map.parent, 'mouseup', mouseUp); map.parent.style.cursor = 'crosshair'; return MM.cancelEvent(e); } function mouseMove(e) { if (!drawing) return; var point = getMousePoint(e); box.style.display = 'block'; if (point.x < mouseDownPoint.x) { box.style.left = point.x + 'px'; } else { box.style.left = mouseDownPoint.x + 'px'; } box.style.width = Math.abs(point.x - mouseDownPoint.x) + 'px'; if (point.y < mouseDownPoint.y) { box.style.top = point.y + 'px'; } else { box.style.top = mouseDownPoint.y + 'px'; } box.style.height = Math.abs(point.y - mouseDownPoint.y) + 'px'; return MM.cancelEvent(e); } zoombox.add = function(map) { // Use a flag to determine whether the zoombox is currently being // drawn. Necessary only for IE because `mousedown` is triggered // twice. box = box || document.createElement('div'); box.id = map.parent.id + '-zoombox-box'; box.className = 'zoombox-box'; map.parent.appendChild(box); MM.addEvent(map.parent, 'mousedown', mouseDown); return this; }; zoombox.remove = function() { map.parent.removeChild(box); MM.removeEvent(map.parent, 'mousedown', mouseDown); }; return zoombox.add(map); }; mapbox-wax-77e78c7/control/mm/mobile.js0000664000175000017500000001511111730207611017007 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; // Mobile // ------ // For making maps on normal websites nicely mobile-ized wax.mm.mobile = function(map, tilejson, opts) { opts = opts || {}; // Inspired by Leaflet var ua = navigator.userAgent.toLowerCase(), isWebkit = ua.indexOf('webkit') != -1, isMobile = ua.indexOf('mobile') != -1, mobileWebkit = isMobile && isWebkit; // FIXME: testing // mobileWebkit = true; var defaultOverlayDraw = function(div) { var canvas = document.createElement('canvas'); var width = parseInt(div.style.width, 10), height = parseInt(div.style.height, 10), w2 = width / 2, h2 = height / 2, // Make the size of the arrow nicely proportional to the map size = Math.min(width, height) / 4, ctx = canvas.getContext('2d'); canvas.setAttribute('width', width); canvas.setAttribute('height', height); ctx.globalAlpha = 0.7; // Draw a nice gradient to signal that the map is inaccessible var inactive = ctx.createLinearGradient(0, 0, 300, 225); inactive.addColorStop(0, 'black'); inactive.addColorStop(1, 'rgb(144, 144, 144)'); ctx.fillStyle = inactive; ctx.fillRect(0, 0, width, height); ctx.beginPath(); ctx.arc( w2 - size * 0.3, h2, size * 1.3, size * 1.3, Math.PI * 2, true); ctx.closePath(); ctx.fillStyle = 'rgb(100, 100, 100)'; ctx.fill(); ctx.fillStyle = 'rgb(255, 255, 255)'; ctx.lineWidth = 5; ctx.beginPath(); ctx.moveTo(w2 - size * 0.8, h2 - size); // give the (x,y) coordinates ctx.lineTo(w2 - size * 0.8, h2 + size); ctx.lineTo(w2 + size * 0.8, h2); ctx.fill(); // Done! Now fill the shape, and draw the stroke. // Note: your shape will not be visible until you call any of the two methods. div.appendChild(canvas); }; function getDeviceScale() { return ((Math.abs(window.orientation) == 90) ? Math.max(480, screen.height) : screen.width) / window.innerWidth; } var defaultBackDraw = function(div) { div.style.position = 'absolute'; div.style.height = '50px'; div.style.left = div.style.right = '0'; var canvas = document.createElement('canvas'); canvas.setAttribute('width', div.offsetWidth); canvas.setAttribute('height', div.offsetHeight); var ctx = canvas.getContext('2d'); ctx.globalAlpha = 1; ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'; ctx.fillRect(0, 0, div.offsetWidth, div.offsetHeight); ctx.fillStyle = 'rgb(0, 0, 0)'; ctx.font = 'bold 20px sans-serif'; ctx.fillText('back', 20, 30); div.appendChild(canvas); }; var maximizeElement = function(elem) { elem.style.position = 'absolute'; elem.style.width = elem.style.height = 'auto'; elem.style.top = (window.pageYOffset) + 'px'; elem.style.left = elem.style.right = '0px'; }; var minimizeElement = function(elem) { elem.style.position = 'relative'; elem.style.width = elem.style.height = elem.style.top = elem.style.left = elem.style.right = 'auto'; }; var overlayDiv, oldBody, standIn, meta, oldscale, overlayDraw = opts.overlayDraw || defaultOverlayDraw, backDraw = opts.backDraw || defaultBackDraw; bodyDraw = opts.bodyDraw || function() {}; var mobile = { add: function(map) { // Code in this block is only run on Mobile Safari; // therefore HTML5 Canvas is fine. if (mobileWebkit) { meta = document.createElement('meta'); meta.id = 'wax-touch'; meta.setAttribute('name', 'viewport'); overlayDiv = document.createElement('div'); overlayDiv.id = map.parent.id + '-mobileoverlay'; overlayDiv.className = 'wax-mobileoverlay'; overlayDiv.style.position = 'absolute'; overlayDiv.style.width = map.dimensions.x + 'px'; overlayDiv.style.height = map.dimensions.y + 'px'; map.parent.appendChild(overlayDiv); overlayDraw(overlayDiv); standIn = document.createElement('div'); backDiv = document.createElement('div'); // Store the old body - we'll need it. oldBody = document.body; newBody = document.createElement('body'); newBody.className = 'wax-mobile-body'; newBody.appendChild(backDiv); MM.addEvent(overlayDiv, 'touchstart', this.toTouch); MM.addEvent(backDiv, 'touchstart', this.toPage); } return this; }, // Enter 'touch mode' toTouch: function() { // Enter a new body map.parent.parentNode.replaceChild(standIn, map.parent); newBody.insertBefore(map.parent, backDiv); document.body = newBody; oldscale = getDeviceScale(); document.head.appendChild(meta); bodyDraw(newBody); backDraw(backDiv); meta.setAttribute( 'content', 'initial-scale=1.0,' + 'minimum-scale=0, maximum-scale=10'); map._smallSize = [map.parent.clientWidth, map.parent.clientHeight]; maximizeElement(map.parent); map.setSize( map.parent.offsetWidth, window.innerHeight); backDiv.style.display = 'block'; overlayDiv.style.display = 'none'; }, // Return from touch mode toPage: function() { // Currently this code doesn't, and can't, reset the // scale of the page. Anything to not use the meta-element // would be a bit of a hack. document.body = oldBody; meta.setAttribute( 'content', 'user-scalable=yes, width=device-width,' + 'initial-scale=' + oldscale); standIn.parentNode.replaceChild(map.parent, standIn); minimizeElement(map.parent); map.setSize(map._smallSize[0], map._smallSize[1]); backDiv.style.display = 'none'; overlayDiv.style.display = 'block'; } }; return mobile.add(map); }; mapbox-wax-77e78c7/control/mm/interaction.js0000664000175000017500000002131011730207611020055 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; // A chaining-style control that adds // interaction to a modestmaps.Map object. // // Takes an options object with the following keys: // // * `callbacks` (optional): an `out`, `over`, and `click` callback. // If not given, the `wax.tooltip` library will be expected. // * `clickAction` (optional): **full** or **location**: default is // **full**. // * `clickHandler` (optional): if not given, `clickAction: 'location'` will // assign a location to your window with `window.location = 'location'`. // To make location-getting work with other systems, like those based on // pushState or Backbone, you can provide a custom function of the form // // // `clickHandler: function(url) { ... go to url ... }` wax.mm.interaction = function(map, tilejson, options) { options = options || {}; tilejson = tilejson || {}; var waxGM = wax.GridManager(tilejson), callbacks = options.callbacks || new wax.tooltip(options), clickAction = options.clickAction || ['full', 'location'], clickHandler = options.clickHandler || function(url) { window.top.location = url; }, eventoffset = wax.util.eventoffset, addEvent = MM.addEvent, removeEvent = MM.removeEvent, interaction = {}, _downLock = false, _clickTimeout = false, touchable = ('ontouchstart' in document.documentElement), // Active feature _af, // Down event _d, // Touch tolerance tol = 4, tileGrid, clearingEvents = ['zoomed', 'panned', 'centered', 'extentset', 'resized', 'drawn']; // Search through `.tiles` and determine the position, // from the top-left of the **document**, and cache that data // so that `mousemove` events don't always recalculate. function getTileGrid() { // TODO: don't build for tiles outside of viewport // Touch interaction leads to intermediate var zoomLayer = map.getLayerAt(0).levels[Math.round(map.getZoom())]; // Calculate a tile grid and cache it, by using the `.tiles` // element on this map. return tileGrid || (tileGrid = (function(t) { var o = []; for (var key in t) { if (t[key].parentNode === zoomLayer) { var offset = wax.util.offset(t[key]); o.push([offset.top, offset.left, t[key]]); } } return o; })(map.getLayerAt(0).tiles)); } // When the map moves, the tile grid is no longer valid. function clearTileGrid(map, e) { tileGrid = null; } function getTile(e) { for (var i = 0, grid = getTileGrid(); i < grid.length; i++) { if ((grid[i][0] < e.y) && ((grid[i][0] + 256) > e.y) && (grid[i][1] < e.x) && ((grid[i][1] + 256) > e.x)) return grid[i][2]; } return false; } // Clear the double-click timeout to prevent double-clicks from // triggering popups. function killTimeout() { if (_clickTimeout) { window.clearTimeout(_clickTimeout); _clickTimeout = null; return true; } else { return false; } } function onMove(e) { // If the user is actually dragging the map, exit early // to avoid performance hits. if (_downLock) return; var t = e.target || e.srcElement; if (t.className !== 'map-tile-loaded') return; var pos = eventoffset(e), tile = getTile(pos), feature; if (tile) waxGM.getGrid(tile.src, function(err, g) { if (err || !g) return; feature = g.tileFeature(pos.x, pos.y, tile, { format: 'teaser' }); if (feature) { if (feature && _af !== feature) { _af = feature; callbacks.out(map.parent); callbacks.over(feature, map.parent, e); } else if (!feature) { _af = null; callbacks.out(map.parent); } } else { _af = null; callbacks.out(map.parent); } }); } // A handler for 'down' events - which means `mousedown` and `touchstart` function onDown(e) { if (e.target.className !== 'map-tile-loaded') return; // Ignore double-clicks by ignoring clicks within 300ms of // each other. if (killTimeout()) { return; } // Prevent interaction offset calculations happening while // the user is dragging the map. // // Store this event so that we can compare it to the // up event _downLock = true; _d = eventoffset(e); if (e.type === 'mousedown') { addEvent(document.body, 'mouseup', onUp); // Only track single-touches. Double-touches will not affect this // control } else if (e.type === 'touchstart' && e.touches.length === 1) { // turn this into touch-mode. Fallback to teaser and full. clickAction = ['full', 'teaser']; // Don't make the user click close if they hit another tooltip if (callbacks._currentTooltip) { callbacks.hideTooltip(callbacks._currentTooltip); } // Touch moves invalidate touches addEvent(map.parent, 'touchend', onUp); addEvent(map.parent, 'touchmove', touchCancel); addEvent(map.parent, 'touchcancel', touchCancel); } } function touchCancel() { removeEvent(map.parent, 'touchend', onUp); removeEvent(map.parent, 'touchmove', onUp); removeEvent(map.parent, 'touchcancel', touchCancel); _downLock = false; } function onUp(e) { var evt = {}, pos = eventoffset(e); _downLock = false; for (var key in e) { evt[key] = e[key]; } removeEvent(document.body, 'mouseup', onUp); if (touchable) { removeEvent(map.parent, 'touchend', onUp); removeEvent(map.parent, 'touchmove', touchCancel); removeEvent(map.parent, 'touchcancel', touchCancel); } if (e.type === 'touchend') { // If this was a touch and it survived, there's no need to avoid a double-tap click(e, _d); } else if (Math.round(pos.y / tol) === Math.round(_d.y / tol) && Math.round(pos.x / tol) === Math.round(_d.x / tol)) { // Contain the event data in a closure. _clickTimeout = window.setTimeout( function() { _clickTimeout = null; click(evt, pos); }, 300); } return onUp; } // Handle a click event. Takes a second function click(e, pos) { var tile = getTile(pos), feature; if (tile) waxGM.getGrid(tile.src, function(err, g) { for (var i = 0; g && (i < clickAction.length); i++) { feature = g.tileFeature(pos.x, pos.y, tile, { format: clickAction[i] }); if (feature) { switch (clickAction[i]) { case 'full': // clickAction can be teaser in touch interaction case 'teaser': return callbacks.click(feature, map.parent, e); case 'location': return clickHandler(feature); } } } }); } // Attach listeners to the map interaction.add = function() { for (var i = 0; i < clearingEvents.length; i++) { map.addCallback(clearingEvents[i], clearTileGrid); } addEvent(map.parent, 'mousemove', onMove); addEvent(map.parent, 'mousedown', onDown); if (touchable) { addEvent(map.parent, 'touchstart', onDown); } return this; }; // Remove this control from the map. interaction.remove = function() { for (var i = 0; i < clearingEvents.length; i++) { map.removeCallback(clearingEvents[i], clearTileGrid); } removeEvent(map.parent, 'mousemove', onMove); removeEvent(map.parent, 'mousedown', onDown); if (touchable) { removeEvent(map.parent, 'touchstart', onDown); } if (callbacks._currentTooltip) { callbacks.hideTooltip(callbacks._currentTooltip); } return this; }; // Ensure chainability return interaction.add(map); }; mapbox-wax-77e78c7/control/mm/pointselector.js0000664000175000017500000001215311730207611020435 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; // Point Selector // -------------- // // This takes an object of options: // // * `callback`: a function called with an array of `com.modestmaps.Location` // objects when the map is edited // // It also exposes a public API function: `addLocation`, which adds a point // to the map as if added by the user. wax.mm.pointselector = function(map, tilejson, opts) { var mouseDownPoint = null, mouseUpPoint = null, tolerance = 5, overlayDiv, pointselector = {}, locations = []; var callback = (typeof opts === 'function') ? opts : opts.callback; // Create a `com.modestmaps.Point` from a screen event, like a click. function makePoint(e) { var coords = wax.util.eventoffset(e); var point = new MM.Point(coords.x, coords.y); // correct for scrolled document // and for the document var body = { x: parseFloat(MM.getStyle(document.documentElement, 'margin-left')), y: parseFloat(MM.getStyle(document.documentElement, 'margin-top')) }; if (!isNaN(body.x)) point.x -= body.x; if (!isNaN(body.y)) point.y -= body.y; // TODO: use wax.util.offset // correct for nested offsets in DOM for (var node = map.parent; node; node = node.offsetParent) { point.x -= node.offsetLeft; point.y -= node.offsetTop; } return point; } // Currently locations in this control contain circular references to elements. // These can't be JSON encoded, so here's a utility to clean the data that's // spit back. function cleanLocations(locations) { var o = []; for (var i = 0; i < locations.length; i++) { o.push(new MM.Location(locations[i].lat, locations[i].lon)); } return o; } // Attach this control to a map by registering callbacks // and adding the overlay // Redraw the points when the map is moved, so that they stay in the // correct geographic locations. function drawPoints() { var offset = new MM.Point(0, 0); for (var i = 0; i < locations.length; i++) { var point = map.locationPoint(locations[i]); if (!locations[i].pointDiv) { locations[i].pointDiv = document.createElement('div'); locations[i].pointDiv.className = 'wax-point-div'; locations[i].pointDiv.style.position = 'absolute'; locations[i].pointDiv.style.display = 'block'; // TODO: avoid circular reference locations[i].pointDiv.location = locations[i]; // Create this closure once per point MM.addEvent(locations[i].pointDiv, 'mouseup', (function selectPointWrap(e) { var l = locations[i]; return function(e) { MM.removeEvent(map.parent, 'mouseup', mouseUp); pointselector.deleteLocation(l, e); }; })()); map.parent.appendChild(locations[i].pointDiv); } locations[i].pointDiv.style.left = point.x + 'px'; locations[i].pointDiv.style.top = point.y + 'px'; } } function mouseDown(e) { mouseDownPoint = makePoint(e); MM.addEvent(map.parent, 'mouseup', mouseUp); } // Remove the awful circular reference from locations. // TODO: This function should be made unnecessary by not having it. function mouseUp(e) { if (!mouseDownPoint) return; mouseUpPoint = makePoint(e); if (MM.Point.distance(mouseDownPoint, mouseUpPoint) < tolerance) { pointselector.addLocation(map.pointLocation(mouseDownPoint)); callback(cleanLocations(locations)); } mouseDownPoint = null; } // API for programmatically adding points to the map - this // calls the callback for ever point added, so it can be symmetrical. // Useful for initializing the map when it's a part of a form. pointselector.addLocation = function(location) { locations.push(location); drawPoints(); callback(cleanLocations(locations)); }; pointselector.locations = function(x) { return locations; }; pointselector.add = function(map) { MM.addEvent(map.parent, 'mousedown', mouseDown); map.addCallback('drawn', drawPoints); return this; }; pointselector.remove = function(map) { MM.removeEvent(map.parent, 'mousedown', mouseDown); map.removeCallback('drawn', drawPoints); for (var i = locations.length - 1; i > -1; i--) { pointselector.deleteLocation(locations[i]); } return this; }; pointselector.deleteLocation = function(location, e) { if (!e || confirm('Delete this point?')) { location.pointDiv.parentNode.removeChild(location.pointDiv); locations.splice(wax.util.indexOf(locations, location), 1); callback(cleanLocations(locations)); } }; return pointselector.add(map); }; mapbox-wax-77e78c7/control/mm/hash.js0000664000175000017500000000153411730207611016467 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; wax.mm.hash = function(map) { return wax.hash({ getCenterZoom: function() { var center = map.getCenter(), zoom = map.getZoom(), precision = Math.max( 0, Math.ceil(Math.log(zoom) / Math.LN2)); return [zoom.toFixed(2), center.lat.toFixed(precision), center.lon.toFixed(precision) ].join('/'); }, setCenterZoom: function setCenterZoom(args) { map.setCenterZoom( new MM.Location(args[1], args[2]), args[0]); }, bindChange: function(fn) { map.addCallback('drawn', fn); }, unbindChange: function(fn) { map.removeCallback('drawn', fn); } }); }; mapbox-wax-77e78c7/control/mm/zoomer.js0000664000175000017500000000363411730207611017062 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; // Zoomer // ------ // Add zoom links, which can be styled as buttons, to a `modestmaps.Map` // control. This function can be used chaining-style with other // chaining-style controls. wax.mm.zoomer = function(map) { var mm = com.modestmaps; var zoomin = document.createElement('a'); zoomin.innerHTML = '+'; zoomin.href = '#'; zoomin.className = 'zoomer zoomin'; mm.addEvent(zoomin, 'mousedown', function(e) { mm.cancelEvent(e); }); mm.addEvent(zoomin, 'dblclick', function(e) { mm.cancelEvent(e); }); mm.addEvent(zoomin, 'click', function(e) { mm.cancelEvent(e); map.zoomIn(); }, false); var zoomout = document.createElement('a'); zoomout.innerHTML = '-'; zoomout.href = '#'; zoomout.className = 'zoomer zoomout'; mm.addEvent(zoomout, 'mousedown', function(e) { mm.cancelEvent(e); }); mm.addEvent(zoomout, 'dblclick', function(e) { mm.cancelEvent(e); }); mm.addEvent(zoomout, 'click', function(e) { mm.cancelEvent(e); map.zoomOut(); }, false); var zoomer = { add: function(map) { map.addCallback('drawn', function(map, e) { if (map.coordinate.zoom === map.coordLimits[0].zoom) { zoomout.className = 'zoomer zoomout zoomdisabled'; } else if (map.coordinate.zoom === map.coordLimits[1].zoom) { zoomin.className = 'zoomer zoomin zoomdisabled'; } else { zoomin.className = 'zoomer zoomin'; zoomout.className = 'zoomer zoomout'; } }); return this; }, appendTo: function(elem) { wax.util.$(elem).appendChild(zoomin); wax.util.$(elem).appendChild(zoomout); return this; } }; return zoomer.add(map); }; mapbox-wax-77e78c7/control/mm/legend.js0000664000175000017500000000134711730207611017004 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; // Legend Control // -------------- // The Modest Maps version of this control is a very, very // light wrapper around the `/lib` code for legends. wax.mm.legend = function(map, tilejson) { tilejson = tilejson || {}; var l, // parent legend legend = {}; legend.add = function() { l = wax.legend() .content(tilejson.legend || ''); return this; }; legend.content = function(x) { if (x) l.content(x.legend || ''); }; legend.element = function() { return l.element(); }; legend.appendTo = function(elem) { wax.util.$(elem).appendChild(l.element()); return this; }; return legend.add(); }; mapbox-wax-77e78c7/control/mm/latlngtooltip.js0000664000175000017500000000371211730207611020440 0ustar daviddavidwax = wax || {}; wax.mm = wax.mm || {}; // LatLng // ------ // Show the current cursor position in // lat/long wax.mm.latlngtooltip = function(map) { var tt, // tooltip _down = false, latlng = {}; function getMousePoint(e) { // start with just the mouse (x, y) var point = new MM.Point(e.clientX, e.clientY); // correct for scrolled document point.x += document.body.scrollLeft + document.documentElement.scrollLeft; point.y += document.body.scrollTop + document.documentElement.scrollTop; // correct for nested offsets in DOM for (var node = map.parent; node; node = node.offsetParent) { point.x -= node.offsetLeft; point.y -= node.offsetTop; } return point; } function onDown(e) { console.log('here'); _down = true; } function onUp(e) { _down = false; } function onMove(e) { if (!e.shiftKey || _down) { if (tt.parentNode === map.parent) { map.parent.removeChild(tt); } return; } var pt = getMousePoint(e), ll = map.pointLocation(pt), fmt = ll.lat.toFixed(2) + ', ' + ll.lon.toFixed(2); tt.innerHTML = fmt; pt.scale = pt.width = pt.height = 1; pt.x += 10; MM.moveElement(tt, pt); map.parent.appendChild(tt); } latlng.add = function() { MM.addEvent(map.parent, 'mousemove', onMove); MM.addEvent(map.parent, 'mousedown', onDown); MM.addEvent(map.parent, 'mouseup', onUp); tt = document.createElement('div'); tt.className = 'wax-latlngtooltip'; return this; }; latlng.remove = function() { MM.removeEvent(map.parent, 'mousemove', onMove); MM.removeEvent(map.parent, 'mousedown', onDown); MM.removeEvent(map.parent, 'mouseup', onUp); return this; }; return latlng.add(); }; mapbox-wax-77e78c7/control/g/0000775000175000017500000000000011730207611015020 5ustar daviddavidmapbox-wax-77e78c7/control/g/bwdetect.js0000664000175000017500000000176311730207611017166 0ustar daviddavidwax = wax || {}; wax.g = wax.g || {}; // Bandwidth Detection // ------------------ wax.g.bwdetect = function(map, options) { options = options || {}; var lowpng = options.png || '.png128', lowjpg = options.jpg || '.jpg70'; // Create a low-bandwidth map type. if (!map.mapTypes['mb-low']) { var mb = map.mapTypes.mb; var tilejson = { tiles: [], scheme: mb.options.scheme, blankImage: mb.options.blankImage, minzoom: mb.minZoom, maxzoom: mb.maxZoom, name: mb.name, description: mb.description }; for (var i = 0; i < mb.options.tiles.length; i++) { tilejson.tiles.push(mb.options.tiles[i] .replace('.png', lowpng) .replace('.jpg', lowjpg)); } m.mapTypes.set('mb-low', new wax.g.connector(tilejson)); } return wax.bwdetect(options, function(bw) { map.setMapTypeId(bw ? 'mb' : 'mb-low'); }); }; mapbox-wax-77e78c7/control/g/attribution.js0000664000175000017500000000124711730207611017726 0ustar daviddavidwax = wax || {}; wax.g = wax.g || {}; // Attribution // ----------- // Attribution wrapper for Google Maps. wax.g.attribution = function(map, tilejson) { tilejson = tilejson || {}; var a, // internal attribution control attribution = {}; attribution.element = function() { return a.element(); }; attribution.appendTo = function(elem) { wax.util.$(elem).appendChild(a.element()); return this; }; attribution.init = function() { a = wax.attribution(); a.set(tilejson.attribution); a.element().className = 'wax-attribution wax-g'; return this; }; return attribution.init(); }; mapbox-wax-77e78c7/control/g/interaction.js0000664000175000017500000001533311730207611017702 0ustar daviddavidwax = wax || {}; wax.g = wax.g || {}; // A control that adds interaction to a google Map object. // // Takes an options object with the following keys: // // * `callbacks` (optional): an `out`, `over`, and `click` callback. // If not given, the `wax.tooltip` library will be expected. // * `clickAction` (optional): **full** or **location**: default is // **full**. wax.g.interaction = function(map, tilejson, options) { tilejson = tilejson || {}; options = options || {}; // Our GridManager (from `gridutil.js`). This will keep the // cache of grid information and provide friendly utility methods // that return `GridTile` objects instead of raw data. var interaction = { waxGM: new wax.GridManager(tilejson), // This requires wax.Tooltip or similar callbacks: options.callbacks || new wax.tooltip(), clickAction: options.clickAction || 'full', eventHandlers:{}, // Attach listeners to the map add: function() { this.eventHandlers.tileloaded = google.maps.event.addListener(map, 'tileloaded', wax.util.bind(this.clearTileGrid, this)); this.eventHandlers.idle = google.maps.event.addListener(map, 'idle', wax.util.bind(this.clearTileGrid, this)); this.eventHandlers.mousemove = google.maps.event.addListener(map, 'mousemove', this.onMove()); this.eventHandlers.click = google.maps.event.addListener(map, 'click', this.click()); return this; }, // Remove interaction events from the map. remove: function() { google.maps.event.removeListener(this.eventHandlers.tileloaded); google.maps.event.removeListener(this.eventHandlers.idle); google.maps.event.removeListener(this.eventHandlers.mousemove); google.maps.event.removeListener(this.eventHandlers.click); return this; }, // Search through `.tiles` and determine the position, // from the top-left of the **document**, and cache that data // so that `mousemove` events don't always recalculate. getTileGrid: function() { // Get all 'marked' tiles, added by the `wax.g.MapType` layer. // Return an array of objects which have the **relative** offset of // each tile, with a reference to the tile object in `tile`, since the API // returns evt coordinates as relative to the map object. if (!this._getTileGrid) { this._getTileGrid = []; var zoom = map.getZoom(); var mapOffset = wax.util.offset(map.getDiv()); var get = wax.util.bind(function(mapType) { if (!mapType.interactive) return; for (var key in mapType.cache) { if (key.split('/')[0] != zoom) continue; var tileOffset = wax.util.offset(mapType.cache[key]); this._getTileGrid.push([ tileOffset.top - mapOffset.top, tileOffset.left - mapOffset.left, mapType.cache[key] ]); } }, this); // Iterate over base mapTypes and overlayMapTypes. for (var i in map.mapTypes) get(map.mapTypes[i]); map.overlayMapTypes.forEach(get); } return this._getTileGrid; }, clearTileGrid: function(map, e) { this._getTileGrid = null; }, getTile: function(evt) { var tile; var grid = this.getTileGrid(); for (var i = 0; i < grid.length; i++) { if ((grid[i][0] < evt.pixel.y) && ((grid[i][0] + 256) > evt.pixel.y) && (grid[i][1] < evt.pixel.x) && ((grid[i][1] + 256) > evt.pixel.x)) { tile = grid[i][2]; break; } } return tile || false; }, onMove: function(evt) { if (!this._onMove) this._onMove = wax.util.bind(function(evt) { var tile = this.getTile(evt); if (tile) { this.waxGM.getGrid(tile.src, wax.util.bind(function(err, g) { if (err || !g) return; var feature = g.tileFeature( evt.pixel.x + wax.util.offset(map.getDiv()).left, evt.pixel.y + wax.util.offset(map.getDiv()).top, tile, { format: 'teaser' } ); // Support only a single layer. // Thus a layer index of **0** is given to the tooltip library if (feature && this.feature !== feature) { this.feature = feature; this.callbacks.out(map.getDiv()); this.callbacks.over(feature, map.getDiv(), 0, evt); } else if (!feature) { this.feature = null; this.callbacks.out(map.getDiv()); } }, this)); } }, this); return this._onMove; }, click: function(evt) { if (!this._onClick) this._onClick = wax.util.bind(function(evt) { var tile = this.getTile(evt); if (tile) { this.waxGM.getGrid(tile.src, wax.util.bind(function(err, g) { if (err || !g) return; var feature = g.tileFeature( evt.pixel.x + wax.util.offset(map.getDiv()).left, evt.pixel.y + wax.util.offset(map.getDiv()).top, tile, { format: this.clickAction } ); if (feature) { switch (this.clickAction) { case 'full': this.callbacks.click(feature, map.getDiv(), 0, evt); break; case 'location': window.location = feature; break; } } }, this)); } }, this); return this._onClick; } }; // Return the interaction control such that the caller may manipulate it // e.g. remove it. return interaction.add(map); }; mapbox-wax-77e78c7/control/g/hash.js0000664000175000017500000000157311730207611016307 0ustar daviddavidwax = wax || {}; wax.g = wax.g || {}; wax.g.hash = function(map) { return wax.hash({ getCenterZoom: function() { var center = map.getCenter(), zoom = map.getZoom(), precision = Math.max( 0, Math.ceil(Math.log(zoom) / Math.LN2)); return [zoom.toFixed(2), center.lat().toFixed(precision), center.lng().toFixed(precision) ].join('/'); }, setCenterZoom: function setCenterZoom(args) { map.setCenter(new google.maps.LatLng(args[1], args[2])); map.setZoom(args[0]); }, bindChange: function(fn) { google.maps.event.addListener(map, 'idle', fn); }, unbindChange: function(fn) { google.maps.event.removeListener(map, 'idle', fn); } }); }; mapbox-wax-77e78c7/control/g/legend.js0000664000175000017500000000110711730207611016613 0ustar daviddavidwax = wax || {}; wax.g = wax.g || {}; // Legend Control // -------------- // Adds legends to a google Map object. wax.g.legend = function(map, tilejson) { tilejson = tilejson || {}; var l, // parent legend legend = {}; legend.add = function() { l = wax.legend() .content(tilejson.legend || ''); return this; }; legend.element = function() { return l.element(); }; legend.appendTo = function(elem) { wax.util.$(elem).appendChild(l.element()); return this; }; return legend.add(); }; mapbox-wax-77e78c7/control/lib/0000775000175000017500000000000011730207611015340 5ustar daviddavidmapbox-wax-77e78c7/control/lib/gridinstance.js0000664000175000017500000000511711730207611020354 0ustar daviddavid// GridInstance // ------------ // GridInstances are queryable, fully-formed // objects for acquiring features from events. // // This code ignores format of 1.1-1.2 wax.GridInstance = function(grid_tile, formatter, options) { options = options || {}; // resolution is the grid-elements-per-pixel ratio of gridded data. // The size of a tile element. For now we expect tiles to be squares. var instance = {}, resolution = options.resolution || 4, tileSize = options.tileSize || 256; // Resolve the UTF-8 encoding stored in grids to simple // number values. // See the [utfgrid spec](https://github.com/mapbox/utfgrid-spec) // for details. function resolveCode(key) { if (key >= 93) key--; if (key >= 35) key--; key -= 32; return key; } instance.grid_tile = function() { return grid_tile; }; instance.getKey = function(x, y) { if (!(grid_tile && grid_tile.grid)) return; if ((y < 0) || (x < 0)) return; if ((Math.floor(y) >= tileSize) || (Math.floor(x) >= tileSize)) return; // Find the key in the grid. The above calls should ensure that // the grid's array is large enough to make this work. return resolveCode(grid_tile.grid[ Math.floor((y) / resolution) ].charCodeAt( Math.floor((x) / resolution) )); }; // Lower-level than tileFeature - has nothing to do // with the DOM. Takes a px offset from 0, 0 of a grid. instance.gridFeature = function(x, y) { // Find the key in the grid. The above calls should ensure that // the grid's array is large enough to make this work. var key = this.getKey(x, y), keys = grid_tile.keys; if (keys && keys[key] && grid_tile.data[keys[key]]) { return grid_tile.data[keys[key]]; } }; // Get a feature: // // * `x` and `y`: the screen coordinates of an event // * `tile_element`: a DOM element of a tile, from which we can get an offset. // * `options` options to give to the formatter: minimally having a `format` // member, being `full`, `teaser`, or something else. instance.tileFeature = function(x, y, tile_element, options) { if (!grid_tile) return; // IE problem here - though recoverable, for whatever reason var offset = wax.util.offset(tile_element); feature = this.gridFeature(x - offset.left, y - offset.top); if (feature) return formatter.format(options, feature); }; return instance; }; mapbox-wax-77e78c7/control/lib/template.js0000664000175000017500000000173611730207611017520 0ustar daviddavid// Templating // --------- wax.template = function(x) { var template = {}; function urlX(url) { // Data URIs are subject to a bug in Firefox // https://bugzilla.mozilla.org/show_bug.cgi?id=255107 // which let them be a vector. But WebKit does 'the right thing' // or at least 'something' about this situation, so we'll tolerate // them. if (/^(https?:\/\/|data:image)/.test(url)) { return url; } } function idX(id) { return id; } // Clone the data object such that the '__[format]__' key is only // set for this instance of templating. template.format = function(options, data) { var clone = {}; for (var key in data) { clone[key] = data[key]; } if (options.format) { clone['__' + options.format + '__'] = true; } return html_sanitize(Mustache.to_html(x, clone), urlX, idX); }; return template; }; mapbox-wax-77e78c7/control/lib/movetip.js0000664000175000017500000001131311730207611017360 0ustar daviddavidvar wax = wax || {}; wax.movetip = {}; wax.movetip = function(options) { options = options || {}; var t = {}, _currentTooltip = undefined, _context = undefined, _animationOut = options.animationOut, _animationIn = options.animationIn; // Helper function to determine whether a given element is a wax popup. function isPopup (el) { return el && el.className.indexOf('wax-popup') !== -1; } function getTooltip(feature, context) { var tooltip = document.createElement('div'); tooltip.className = 'wax-movetip'; tooltip.style.cssText = 'position:absolute;' tooltip.innerHTML = feature; context.appendChild(tooltip); _context = context; _tooltipOffset = wax.util.offset(tooltip); _contextOffset = wax.util.offset(_context); return tooltip; } function moveTooltip(e) { if (!_currentTooltip) return; var eo = wax.util.eventoffset(e); _currentTooltip.className = 'wax-movetip'; // faux-positioning if ((_tooltipOffset.height + eo.y) > (_contextOffset.top + _contextOffset.height) && (_contextOffset.height > _tooltipOffset.height)) { eo.y -= _tooltipOffset.height; _currentTooltip.className += ' flip-y'; } // faux-positioning if ((_tooltipOffset.width + eo.x) > (_contextOffset.left + _contextOffset.width)) { eo.x -= _tooltipOffset.width; _currentTooltip.className += ' flip-x'; } _currentTooltip.style.left = eo.x + 'px'; _currentTooltip.style.top = eo.y + 'px'; } // Hide a given tooltip. function hideTooltip(el) { if (!el) return; var event, remove = function() { if (this.parentNode) this.parentNode.removeChild(this); }; if (el.style['-webkit-transition'] !== undefined && _animationOut) { event = 'webkitTransitionEnd'; } else if (el.style.MozTransition !== undefined && _animationOut) { event = 'transitionend'; } if (event) { // This code assumes that transform-supporting browsers // also support proper events. IE9 does both. el.addEventListener(event, remove, false); el.addEventListener('transitionend', remove, false); el.className += ' ' + _animationOut; } else { if (el.parentNode) el.parentNode.removeChild(el); } } // Expand a tooltip to be a "popup". Suspends all other tooltips from being // shown until this popup is closed or another popup is opened. function click(feature, context) { // Hide any current tooltips. if (_currentTooltip) { hideTooltip(_currentTooltip); _currentTooltip = undefined; } var tooltip = getTooltip(feature, context); tooltip.className += ' wax-popup'; tooltip.innerHTML = feature; var close = document.createElement('a'); close.href = '#close'; close.className = 'close'; close.innerHTML = 'Close'; tooltip.appendChild(close); var closeClick = function(ev) { hideTooltip(tooltip); _currentTooltip = undefined; ev.returnValue = false; // Prevents hash change. if (ev.stopPropagation) ev.stopPropagation(); if (ev.preventDefault) ev.preventDefault(); return false; }; // IE compatibility. if (close.addEventListener) { close.addEventListener('click', closeClick, false); } else if (close.attachEvent) { close.attachEvent('onclick', closeClick); } _currentTooltip = tooltip; } t.over = function(feature, context, e) { if (!feature) return; context.style.cursor = 'pointer'; if (isPopup(_currentTooltip)) { return; } else { _currentTooltip = getTooltip(feature, context); moveTooltip(e); if (context.addEventListener) { context.addEventListener('mousemove', moveTooltip); } } }; // Hide all tooltips on this layer and show the first hidden tooltip on the // highest layer underneath if found. t.out = function(context) { context.style.cursor = 'default'; if (isPopup(_currentTooltip)) { return; } else if (_currentTooltip) { hideTooltip(_currentTooltip); if (context.removeEventListener) { context.removeEventListener('mousemove', moveTooltip); } _currentTooltip = undefined; } }; return t; }; mapbox-wax-77e78c7/control/lib/bwdetect.js0000664000175000017500000000345611730207611017507 0ustar daviddavidwax = wax || {}; // Attribution // ----------- wax.bwdetect = function(options, callback) { var detector = {}, threshold = options.threshold || 400, // test image: 30.29KB testImage = 'http://a.tiles.mapbox.com/mapbox/1.0.0/blue-marble-topo-bathy-jul/0/0/0.png?preventcache=' + (+new Date()), // High-bandwidth assumed // 1: high bandwidth (.png, .jpg) // 0: low bandwidth (.png128, .jpg70) bw = 1, // Alternative versions auto = options.auto === undefined ? true : options.auto; function bwTest() { wax.bw = -1; var im = new Image(); im.src = testImage; var first = true; var timeout = setTimeout(function() { if (first && wax.bw == -1) { detector.bw(0); first = false; } }, threshold); im.onload = function() { if (first && wax.bw == -1) { clearTimeout(timeout); detector.bw(1); first = false; } }; } detector.bw = function(x) { if (!arguments.length) return bw; var oldBw = bw; if (wax.bwlisteners && wax.bwlisteners.length) (function () { listeners = wax.bwlisteners; wax.bwlisteners = []; for (i = 0; i < listeners; i++) { listeners[i](x); } })(); wax.bw = x; if (bw != (bw = x)) callback(x); }; detector.add = function() { if (auto) bwTest(); return this; }; if (wax.bw == -1) { wax.bwlisteners = wax.bwlisteners || []; wax.bwlisteners.push(detector.bw); } else if (wax.bw !== undefined) { detector.bw(wax.bw); } else { detector.add(); } return detector; }; mapbox-wax-77e78c7/control/lib/attribution.js0000664000175000017500000000173111730207611020244 0ustar daviddavid;wax = wax || {}; // Attribution // ----------- wax.attribution = function() { var container, a = {}; function urlX(url) { // Data URIs are subject to a bug in Firefox // https://bugzilla.mozilla.org/show_bug.cgi?id=255107 // which let them be a vector. But WebKit does 'the right thing' // or at least 'something' about this situation, so we'll tolerate // them. if (/^(https?:\/\/|data:image)/.test(url)) { return url; } } function idX(id) { return id; } a.set = function(content) { if (typeof content === 'undefined') return; container.innerHTML = html_sanitize(content, urlX, idX); return this; }; a.element = function() { return container; }; a.init = function() { container = document.createElement('div'); container.className = 'wax-attribution'; return this; }; return a.init(); }; mapbox-wax-77e78c7/control/lib/formatter.js0000664000175000017500000000206611730207611017705 0ustar daviddavid// Formatter // --------- // // This code is no longer the recommended code path for Wax - // see `template.js`, a safe implementation of Mustache templates. wax.formatter = function(x) { var formatter = {}, f; // Prevent against just any input being used. if (x && typeof x === 'string') { try { // Ugly, dangerous use of eval. eval('f = ' + x); } catch (e) { if (console) console.log(e); } } else if (x && typeof x === 'function') { f = x; } else { f = function() {}; } function urlX(url) { if (/^(https?:\/\/|data:image)/.test(url)) { return url; } } function idX(id) { return id; } // Wrap the given formatter function in order to // catch exceptions that it may throw. formatter.format = function(options, data) { try { return html_sanitize(f(options, data), urlX, idX); } catch (e) { if (console) console.log(e); } }; return formatter; }; mapbox-wax-77e78c7/control/lib/gridmanager.js0000664000175000017500000000447611730207611020171 0ustar daviddavid// GridManager // ----------- // Generally one GridManager will be used per map. // // It takes one options object, which current accepts a single option: // `resolution` determines the number of pixels per grid element in the grid. // The default is 4. wax.GridManager = function(options) { options = options || {}; var resolution = options.resolution || 4, version = options.version || '1.1', grid_tiles = {}, manager = {}, formatter; var gridUrl = function(url) { return url.replace(/(\.png|\.jpg|\.jpeg)(\d*)/, '.grid.json'); }; function templatedGridUrl(template) { if (typeof template === 'string') template = [template]; return function templatedGridFinder(url) { if (!url) return; var xyz = /(\d+)\/(\d+)\/(\d+)\.[\w\._]+/g.exec(url); if (!xyz) return; return template[parseInt(xyz[2], 10) % template.length] .replace('{z}', xyz[1]) .replace('{x}', xyz[2]) .replace('{y}', xyz[3]); }; } manager.formatter = function(x) { if (!arguments.length) return formatter; formatter = wax.formatter(x); return manager; }; manager.template = function(x) { if (!arguments.length) return formatter; formatter = wax.template(x); return manager; }; manager.gridUrl = function(x) { if (!arguments.length) return gridUrl; gridUrl = typeof x === 'function' ? x : templatedGridUrl(x); return manager; }; manager.getGrid = function(url, callback) { var gurl = gridUrl(url); if (!formatter || !gurl) return callback(null, null); wax.request.get(gurl, function(err, t) { if (err) return callback(err, null); callback(null, wax.GridInstance(t, formatter, { resolution: resolution || 4 })); }); return manager; }; manager.add = function(options) { if (options.template) { manager.template(options.template); } else if (options.formatter) { manager.formatter(options.formatter); } if (options.grids) { manager.gridUrl(options.grids); } return this; }; return manager.add(options); }; mapbox-wax-77e78c7/control/lib/request.js0000664000175000017500000000304611730207611017371 0ustar daviddavid// Wax GridUtil // ------------ // Wax header var wax = wax || {}; // Request // ------- // Request data cache. `callback(data)` where `data` is the response data. wax.request = { cache: {}, locks: {}, promises: {}, get: function(url, callback) { // Cache hit. if (this.cache[url]) { return callback(this.cache[url][0], this.cache[url][1]); // Cache miss. } else { this.promises[url] = this.promises[url] || []; this.promises[url].push(callback); // Lock hit. if (this.locks[url]) return; // Request. var that = this; this.locks[url] = true; reqwest({ url: url + (~url.indexOf('?') ? '&' : '?') + 'callback=grid', type: 'jsonp', jsonpCallback: 'callback', success: function(data) { that.locks[url] = false; that.cache[url] = [null, data]; for (var i = 0; i < that.promises[url].length; i++) { that.promises[url][i](that.cache[url][0], that.cache[url][1]); } }, error: function(err) { that.locks[url] = false; that.cache[url] = [err, null]; for (var i = 0; i < that.promises[url].length; i++) { that.promises[url][i](that.cache[url][0], that.cache[url][1]); } } }); } } }; mapbox-wax-77e78c7/control/lib/tilejson.js0000664000175000017500000000052111730207611017523 0ustar daviddavidif (!wax) var wax = {}; // A wrapper for reqwest jsonp to easily load TileJSON from a URL. wax.tilejson = function(url, callback) { reqwest({ url: url + (~url.indexOf('?') ? '&' : '?') + 'callback=grid', type: 'jsonp', jsonpCallback: 'callback', success: callback, error: callback }); }; mapbox-wax-77e78c7/control/lib/tooltip.js0000664000175000017500000000713011730207611017371 0ustar daviddavidvar wax = wax || {}; wax.tooltip = {}; wax.tooltip = function(options) { this._currentTooltip = undefined; options = options || {}; if (options.animationOut) this.animationOut = options.animationOut; if (options.animationIn) this.animationIn = options.animationIn; }; // Helper function to determine whether a given element is a wax popup. wax.tooltip.prototype.isPopup = function(el) { return el && el.className.indexOf('wax-popup') !== -1; }; // Get the active tooltip for a layer or create a new one if no tooltip exists. // Hide any tooltips on layers underneath this one. wax.tooltip.prototype.getTooltip = function(feature, context) { var tooltip = document.createElement('div'); tooltip.className = 'wax-tooltip wax-tooltip-0'; tooltip.innerHTML = feature; context.appendChild(tooltip); return tooltip; }; // Hide a given tooltip. wax.tooltip.prototype.hideTooltip = function(el) { if (!el) return; var event, remove = function() { if (this.parentNode) this.parentNode.removeChild(this); }; if (el.style['-webkit-transition'] !== undefined && this.animationOut) { event = 'webkitTransitionEnd'; } else if (el.style.MozTransition !== undefined && this.animationOut) { event = 'transitionend'; } if (event) { // This code assumes that transform-supporting browsers // also support proper events. IE9 does both. el.addEventListener(event, remove, false); el.addEventListener('transitionend', remove, false); el.className += ' ' + this.animationOut; } else { if (el.parentNode) el.parentNode.removeChild(el); } }; // Expand a tooltip to be a "popup". Suspends all other tooltips from being // shown until this popup is closed or another popup is opened. wax.tooltip.prototype.click = function(feature, context) { // Hide any current tooltips. if (this._currentTooltip) { this.hideTooltip(this._currentTooltip); this._currentTooltip = undefined; } var tooltip = this.getTooltip(feature, context); tooltip.className += ' wax-popup'; tooltip.innerHTML = feature; var close = document.createElement('a'); close.href = '#close'; close.className = 'close'; close.innerHTML = 'Close'; tooltip.appendChild(close); var closeClick = wax.util.bind(function(ev) { this.hideTooltip(tooltip); this._currentTooltip = undefined; ev.returnValue = false; // Prevents hash change. if (ev.stopPropagation) ev.stopPropagation(); if (ev.preventDefault) ev.preventDefault(); return false; }, this); // IE compatibility. if (close.addEventListener) { close.addEventListener('click', closeClick, false); close.addEventListener('touchend', closeClick, false); } else if (close.attachEvent) { close.attachEvent('onclick', closeClick); } this._currentTooltip = tooltip; }; // Show a tooltip. wax.tooltip.prototype.over = function(feature, context) { if (!feature) return; context.style.cursor = 'pointer'; if (this.isPopup(this._currentTooltip)) { return; } else { this._currentTooltip = this.getTooltip(feature, context); } }; // Hide all tooltips on this layer and show the first hidden tooltip on the // highest layer underneath if found. wax.tooltip.prototype.out = function(context) { context.style.cursor = 'default'; if (this.isPopup(this._currentTooltip)) { return; } else if (this._currentTooltip) { this.hideTooltip(this._currentTooltip); this._currentTooltip = undefined; } }; mapbox-wax-77e78c7/control/lib/util.js0000664000175000017500000001464711730207611016667 0ustar daviddavidvar wax = wax || {}; wax.util = wax.util || {}; // Utils are extracted from other libraries or // written from scratch to plug holes in browser compatibility. wax.util = { // From Bonzo offset: function(el) { // TODO: window margins // // Okay, so fall back to styles if offsetWidth and height are botched // by Firefox. var width = el.offsetWidth || parseInt(el.style.width, 10), height = el.offsetHeight || parseInt(el.style.height, 10), doc_body = document.body, top = 0, left = 0; var calculateOffset = function(el) { if (el === doc_body || el === document.documentElement) return; top += el.offsetTop; left += el.offsetLeft; var style = el.style.transform || el.style.WebkitTransform || el.style.OTransform || el.style.MozTransform || el.style.msTransform; if (style) { if (match = style.match(/translate\((.+)px, (.+)px\)/)) { top += parseInt(match[2], 10); left += parseInt(match[1], 10); } else if (match = style.match(/translate3d\((.+)px, (.+)px, (.+)px\)/)) { top += parseInt(match[2], 10); left += parseInt(match[1], 10); } else if (match = style.match(/matrix3d\(([\-\d,\s]+)\)/)) { var pts = match[1].split(','); top += parseInt(pts[13], 10); left += parseInt(pts[12], 10); } else if (match = style.match(/matrix\(.+, .+, .+, .+, (.+), (.+)\)/)) { top += parseInt(match[2], 10); left += parseInt(match[1], 10); } } }; calculateOffset(el); try { while (el = el.offsetParent) calculateOffset(el); } catch(e) { // Hello, internet explorer. } // Offsets from the body top += doc_body.offsetTop; left += doc_body.offsetLeft; // Offsets from the HTML element top += doc_body.parentNode.offsetTop; left += doc_body.parentNode.offsetLeft; // Firefox and other weirdos. Similar technique to jQuery's // `doesNotIncludeMarginInBodyOffset`. var htmlComputed = document.defaultView ? window.getComputedStyle(doc_body.parentNode, null) : doc_body.parentNode.currentStyle; if (doc_body.parentNode.offsetTop !== parseInt(htmlComputed.marginTop, 10) && !isNaN(parseInt(htmlComputed.marginTop, 10))) { top += parseInt(htmlComputed.marginTop, 10); left += parseInt(htmlComputed.marginLeft, 10); } return { top: top, left: left, height: height, width: width }; }, '$': function(x) { return (typeof x === 'string') ? document.getElementById(x) : x; }, // From underscore, minus funcbind for now. // Returns a version of a function that always has the second parameter, // `obj`, as `this`. bind: function(func, obj) { var args = Array.prototype.slice.call(arguments, 2); return function() { return func.apply(obj, args.concat(Array.prototype.slice.call(arguments))); }; }, // From underscore isString: function(obj) { return !!(obj === '' || (obj && obj.charCodeAt && obj.substr)); }, // IE doesn't have indexOf indexOf: function(array, item) { var nativeIndexOf = Array.prototype.indexOf; if (array === null) return -1; var i, l; if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item); for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i; return -1; }, // is this object an array? isArray: Array.isArray || function(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }, // From underscore: reimplement the ECMA5 `Object.keys()` method keys: Object.keys || function(obj) { var ho = Object.prototype.hasOwnProperty; if (obj !== Object(obj)) throw new TypeError('Invalid object'); var keys = []; for (var key in obj) if (ho.call(obj, key)) keys[keys.length] = key; return keys; }, // From quirksmode: normalize the offset of an event from the top-left // of the page. eventoffset: function(e) { var posx = 0; var posy = 0; if (!e) var e = window.event; if (e.pageX || e.pageY) { // Good browsers return { x: e.pageX, y: e.pageY }; } else if (e.clientX || e.clientY) { // Internet Explorer var doc = document.documentElement, body = document.body; var htmlComputed = document.body.parentNode.currentStyle; var topMargin = parseInt(htmlComputed.marginTop, 10) || 0; var leftMargin = parseInt(htmlComputed.marginLeft, 10) || 0; return { x: e.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0) + leftMargin, y: e.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0) + topMargin }; } else if (e.touches && e.touches.length === 1) { // Touch browsers return { x: e.touches[0].pageX, y: e.touches[0].pageY }; } }, // Ripped from underscore.js // Internal function used to implement `_.throttle` and `_.debounce`. limit: function(func, wait, debounce) { var timeout; return function() { var context = this, args = arguments; var throttler = function() { timeout = null; func.apply(context, args); }; if (debounce) clearTimeout(timeout); if (debounce || !timeout) timeout = setTimeout(throttler, wait); }; }, // Returns a function, that, when invoked, will only be triggered at most once // during a given window of time. throttle: function(func, wait) { return this.limit(func, wait, false); } }; mapbox-wax-77e78c7/control/lib/hash.js0000664000175000017500000000264411730207611016627 0ustar daviddavidwax = wax || {}; // Hash // ---- wax.hash = function(options) { options = options || {}; function getState() { return location.hash.substring(1); } function pushState(state) { location.hash = '#' + state; } var s0, // old hash hash = {}, lat = 90 - 1e-8; // allowable latitude range function parseHash(s) { var args = s.split('/'); for (var i = 0; i < args.length; i++) { args[i] = Number(args[i]); if (isNaN(args[i])) return true; } if (args.length < 3) { // replace bogus hash return true; } else if (args.length == 3) { options.setCenterZoom(args); } } function move() { var s1 = options.getCenterZoom(); if (s0 !== s1) { s0 = s1; // don't recenter the map! pushState(s0); } } function stateChange(state) { // ignore spurious hashchange events if (state === s0) return; if (parseHash(s0 = state)) { // replace bogus hash move(); } } var _move = wax.util.throttle(move, 500); hash.add = function() { stateChange(getState()); options.bindChange(_move); return this; }; hash.remove = function() { options.unbindChange(_move); return this; }; return hash.add(); }; mapbox-wax-77e78c7/control/lib/melt.js0000664000175000017500000000054711730207611016645 0ustar daviddavid// Like underscore's bind, except it runs a function // with no arguments off of an object. // // var map = ...; // w(map).melt(myFunction); // // is equivalent to // // var map = ...; // myFunction(map); // var w = function(self) { self.melt = function(func, obj) { return func.apply(obj, [self, obj]); }; return self; }; mapbox-wax-77e78c7/control/lib/legend.js0000664000175000017500000000255711730207611017145 0ustar daviddavid// Wax Legend // ---------- // Wax header var wax = wax || {}; wax.legend = function() { var element, legend = {}, container; function urlX(url) { // Data URIs are subject to a bug in Firefox // https://bugzilla.mozilla.org/show_bug.cgi?id=255107 // which let them be a vector. But WebKit does 'the right thing' // or at least 'something' about this situation, so we'll tolerate // them. if (/^(https?:\/\/|data:image)/.test(url)) { return url; } } function idX(id) { return id; } legend.element = function() { return container; }; legend.content = function(content) { if (!arguments.length) return element.innerHTML; if (content) { element.innerHTML = html_sanitize(content, urlX, idX); element.style.display = 'block'; } else { element.innerHTML = ''; element.style.display = 'none'; } return this; }; legend.add = function() { container = document.createElement('div'); container.className = 'wax-legends'; element = document.createElement('div'); element.className = 'wax-legend'; element.style.display = 'none'; container.appendChild(element); return this; }; return legend.add(); }; mapbox-wax-77e78c7/CHANGELOG.md0000664000175000017500000001614211730207611014727 0ustar daviddavid## Changelog ## 5.0.1 * Real support for Modest Maps 1.0.0-alpha2 * No longer requires layers to be wrapped with `MM.Layer()` in MM 1.x ## 5.0.0-alpha* * Initial support for Modest Maps 1.0.0-alpha ## 4.1.5 * Fixes a bug in touch interaction controls discovered and fixed by @lxbarth ## 4.1.4 * Calls callbacks with `evt` argument from `wax.ol.interaction` * Strict interaction: doesn't tolerate actions that are on elements that are not actually map tiles. This changes behavior significantly * Adds latlngtooltip - a hover-based tooltip. * Adds a Polymaps connector to the build. ## 4.1.3 * Adds a class, `wax-fullscreen-view`, when the fullscreen control is toggled. ## 4.1.2 * Sanitize legend, attribution output. * Fix bug where template would return multiple formats. ## 4.1.1 * Fix for URL regex in HTML sanitizer integration. ## 4.1.0 * Adds `wax.ol.connector`, an OpenLayers connector ## 4.0.0 * Removes `wax.Record`, which has been deprecated in favor of templating, TileJSON, and possibly albums * Removes interaction autoconfiguration - you should use the `wax.tilejson()` instead to provide these details up-front. * Removes OpenLayers embedder and switcher controls. Embedder was deprecated by changes in embedding strategy and map-exchanging should be used instead of layer switching. ## 3.1.0 * Adds new formatter api: TileJSON with a `version=1.2` key and `template` data will now be templated with Mustache and sent through `html-sanitizer` ### 3.0.9 * Simplified hash control - `hashchange` and `pushState` APIs discontinued * Added `wax.g.hash()` control * Moved hash fundamentals to `control/lib/hash.js` and utilities to `utils.js` * Simplified URL construction * Added micro-optimizations to reduce code size * Removed `wax.Record` * Fixes interaction control in Leaflet ### 3.0.8 * Fixed `wax.mm.interaction()` click callback ### 3.0.7 * Fixes `pointselector.remove()` * Adds `pointselector.locations()` * Fixes Google Maps connector - thanks @tokumine * Fixes and adds silent option to `boxselector.extent()` * Fixes `wax.tooltip` var error in IE8 ### 3.0.6 * Update [reqwest](https://github.com/ded/reqwest) to 0.2.7 for IE9 compatibility. * Parse `matrix()` CSS transforms for IE9 compatibility. * Fix to `wax.mm.interaction()` on dragging outside of the map. ### 3.0.5 * `bwdetect` refactored to be compatible with Google Maps. * Tests for `bwdetect`, `gridFeature`. ### 3.0.4 * Added `bwdetect` control for Modest Maps. * Add tests using `jasmine` library. * Bugfixes. ### 3.0.3 * Add support for bleeding-edge MM. * Bugfixes. ### 3.0.2 * Fixes `xyzFinder` in `wax.GridManager` to handle URLs with query strings. ### 3.0.1 * Expose `#full` and `#original` methods on `wax.mm.fullscreen` allowing API access to fullscreen toggling actions. * Fixes to boxselector and zoombox controls. ### 3.0.0 * TileJSON support in new map connectors: `wax.mm.connector`, `wax.leaf.connector` and `wax.g.connector`. The old `wax.g.maptype` and `wax.mm.provider` have been removed. * All `g`, `leaf` and `mm` controls now use the signature `function (map, tilejson, options) {}` where relevant TileJSON keys in `tilejson` are used if present and `options` contains settings specific to the control. * Fullscreen, zoomer, attribution and legend controls no longer automatically append themselves to the map div. Use the `.appendTo` method to add the DOM element to the map or to another element on the page. * `w.melt(func)` now has the same return value as `func`. _2.x.x_ ```javascript var provider = new wax.mm.provider({ baseUrl: 'http://a.tiles.mapbox.com/mapbox/', layerName: 'natural-earth-2'}); var map = ...; wax.mm.legend(map); ``` _3.x.x_ ```javascript var connector = new wax.mm.connector({ tiles: 'http://a.tiles.mapbox.com/mapbox/1.0.0/natural-earth-2/{z}/{x}/{y}.png', scheme: 'tms' }); var map = ...; wax.mm.legend(map, { legend: 'Content' }).appendTo(map.parent); ``` ### 2.1.6 * Fix for window margin offset calculation. * Fix zoombox control in IE. ### 2.1.5 * Fixed Hash in FF 4.x ### 2.1.4 * Fixed Interaction in FF 3.6.x * Optimized Modest Maps scrolling behavior on interactive maps * Fixed OpenLayers compatibility bug between 2.9 and 2.10 ### 2.1.3 * Fixing a touch javascript error. ### 2.1.1 * Overeager touch-events handling fixed. ### 2.1.0 * Leaflet compatibility - interaction control and documentation. * New pushState-based hash manager, used by default. * Interaction support on mobile devices, with fallback to teaser if full formatter isnt available. ### 2.0.0 * Stripped down the README, now references the manual. * Renamed `build` to `dist` to avoid `npm` cleanup problems. * Fix for interaction in Firefox with body margins. * Added OpenLayers and Google to manual. * Removed Google and Modest Maps embedder controls. Will return some day... * All Modest Maps controls are flipped - instead of being extensions of the `com.modestmaps.Map` object, they are off of the `wax` object and are called with the map as the first argument and an options object as the second * Modest Maps controls and provider moved from `wax.*` to `wax.mm.*`. * Google control API changed from `wax.g.Controls` object to `wax.g.*` mirroring Modest Maps controls API. * jQuery, jQuery-jsonp, and Underscore dependency removed * `gridutil` now uses [reqwest](https://github.com/ded/reqwest) as its XMLHTTPRequest library. #### 1.4.2 * Beta pointselector control. * Make zoombox removable. #### 1.4.1 * Tweaks to `boxselect` including removability. #### 1.4.0 * Added `.boxselect(function())` #### 1.3.0 * Added `.zoombox()` and `hash()` controls for Modest Maps. #### 1.2.1 * Bug fixes for OpenLayers #### 1.2.0 * Functions on the Google Maps `Controls` object are now lowercase. * Changed `WaxProvider`'s signature: now takes an object of settings and supports multiple domains, filetypes and zoom restrictions. * Changed `wax.g.MapType`'s signature: now accepts an object of settings in the same form as `WaxProvider` * Modest Maps `.interaction()` now supports clicks, with the same `clickAction` setting as the OpenLayers version. * Added large manual for usage. * Fixed Modest Maps `.fullscreen()` sizing. * Removed `/examples` directory: examples will be in manuals. * Performance optimization of interaction code: no calculations are performed during drag events. #### 1.1.0 * connector/mm: Added [Modest Maps](https://github.com/stamen/modestmaps-js) connector. * control/mm: Added `.legend()`, `.interaction()`, `.fullscreen()`, and `.zoomer()` controls for Modest Maps. * control/lib: Added `addedTooltip` event to `tooltip.js` to allow for external styling code. #### 1.0.4 * connector/g: Hide error tiles and wrap on dateline. * connector/g: Performance improvements. * control/legend: Fix rerender bug. * control/tooltip: `addedtooltip` event for binding/extending tooltip behavior. Subject to change. #### 1.0.3 * Embedder functionality for Google Maps and OpenLayers. #### 1.0.2 * Bug fixes for Firefox 3. #### 1.0.1 * `make ext` added for downloading and installing external libraries needed to use examples. * Bug fixes for legend, IE compatibility. #### 1.0.0 * Initial release. mapbox-wax-77e78c7/package.json0000664000175000017500000000130011730207611015372 0ustar daviddavid{ "name": "wax", "version": "5.0.0-alpha2", "description": "Tools for improving web maps.", "repository": { "type": "git", "url": "git://github.com/mapbox/wax.git" }, "author": { "name": "MapBox", "url": "http://mapbox.com/", "email": "info@mapbox.com" }, "homepage": "http://mapbox.github.com/wax/", "contributors": [ "Tom MacWright ", "Young Hahn ", "Will White " ], "licenses": [{ "type": "BSD" }], "devDependencies": { "jshint": "0.2.3", "uglify-js": "1.0.x", "banner": "0.0.x" } } mapbox-wax-77e78c7/theme/0000775000175000017500000000000011730207611014214 5ustar daviddavidmapbox-wax-77e78c7/theme/controls.css0000664000175000017500000000445211730207611016576 0ustar daviddavida.wax-fullscreen { /* TODO: sprite-based fullscreen button */ position: absolute; top: 5px; left: 5px; z-index: 99999; } a.zoomer { text-decoration:none; position:absolute; background-color:#444; color:#fff; line-height:25px; font-size:20px; z-index:99999; text-align:center; width:25px; height:25px; } a.zoomin { left:5px; } a.zoomout { left:35px; } a.zoomdisabled { background-color:#333; } .wax-fullscreen-map { position:fixed !important; width:auto !important; height:auto !important; top:0; left:0; right:0; bottom:0; z-index:999999999999; } .wax-legends { position:absolute; left:10px; bottom:10px; z-index:999999; } .wax-legends .wax-legend { padding:10px; background:#333; color:#fff; } .wax-tooltip { z-index:999999; position:absolute; background:#333; color:#fff; padding:10px; left:10px; top:10px; max-width:300px; opacity:1; -webkit-transition:opacity .5s; -moz-transition:opacity .5s; } .wax-movetip { position:absolute; z-index:999999; background:#333; color:#fff; padding:10px; max-width:300px; } .wax-tooltip.hide { opacity:0; } .wax-tooltip .close { display:block; position:absolute; top:0px; right:0px; } .wax-mobile-body .wax-tooltip { position:absolute; top:50px; } .zoombox-box, .boxselector-box { margin:0; padding:0; border:1px dashed #888; background: rgba(255,255,255,0.25); position: absolute; top: 0; left: 0; width: 0; height: 0; display: none; } .zoombox-box-container, .boxselector-box-container, .pointselector-box-container { margin:0; padding:0; position:absolute; background: url(blank.gif); top:0; left:0; } .wax-point-div { width:10px; height:10px; margin-left:-5px; margin-top:-5px; background:#fff; border:1px solid #333; -webkit-border-radius:5px; } div.wax-attribution { position:absolute; background-color:rgba(255, 255, 255, 0.7); color:#333; font-size:11px; line-height:20px; z-index:99999; text-align:center; padding:0 5px; bottom:0; left:0; } .wax-attribution.wax-g { left:65px; bottom:4px; background:transparent; } .wax-latlngtooltip { position:absolute; background:#caedf4; padding:3px; border:1px solid #75c1d0; border-radius:3px; } mapbox-wax-77e78c7/theme/blank.gif0000664000175000017500000000005211730207611015767 0ustar daviddavidGIF89a€ÿÿÿ!ù,@D;mapbox-wax-77e78c7/LICENSE0000664000175000017500000000271411730207611014123 0ustar daviddavidCopyright (c), Development Seed All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name "Development Seed" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mapbox-wax-77e78c7/README.md0000664000175000017500000000244511730207611014376 0ustar daviddavid# Wax Tools for improving web maps. The centerpiece of the code is a client implementation of the [MBTiles interaction specification](https://github.com/mapbox/mbtiles-spec). For full documentation of supported mapping APIs and how to use Wax see http://mapbox.github.com/wax. ## Versions There are three current development branches of Wax: * `master`, this branch, supports **Modest Maps 1.0.0-alpha2 and later** * 4.x supports **Modest Maps 0.x**, older versions of Modest Maps * 6.x is **unstable** and will introduce new **event-based interaction APIs** To find more detail of what has changed in each version, consult `CHANGELOG.md`. ## Compatibility * Google Maps API v3 * Leaflet 0.x.x * Modest Maps 1.x.x * OpenLayers 2.11 ## Building Wax For end users, a minified library is already provided in `dist/`. But for developers you can rebuild a minified library by running: npm install --dev make ## Includes Wax currently includes three externals: * [reqwest](https://github.com/ded/reqwest) (MIT) * [mustache.js](https://github.com/janl/mustache.js) (MIT) * [html-sanitizer from Google Caja](http://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/plugin/html-sanitizer.js) (Apache) ## Authors - Tom MacWright (tmcw) - Young Hahn (yhahn) - Will White (willwhite)