├── .gitignore ├── _assets ├── Flaticon_24543.svg ├── Flaticon_33546.svg ├── Flaticon_35660.svg ├── Flaticon_3711.svg ├── Flaticon_3913.svg ├── Flaticon_48479.svg ├── dice.svg ├── north-big.png └── search.svg ├── companion ├── companion.js └── index.html ├── coverage-worker.js ├── debug.html ├── debug.js ├── fragment.glsl ├── geodesy.js ├── img ├── add.png ├── alert.png ├── crosshair.png ├── dice.png ├── github.png ├── help.png ├── layers.png ├── map.png ├── north.png ├── placemark.png ├── rotate.png ├── routes_example.jpg ├── search.png ├── settings.png └── swap.png ├── index.html ├── layer-test.js ├── layers-test.html ├── lib ├── filesaver.js ├── hammer.min.js ├── jquery-ui-1.10.4.custom.min.js └── stats.min.js ├── merc-extreme.js └── vertex.glsl /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | coords-db.js 3 | -------------------------------------------------------------------------------- /_assets/Flaticon_24543.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /_assets/Flaticon_33546.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /_assets/Flaticon_35660.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /_assets/Flaticon_3711.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /_assets/Flaticon_3913.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /_assets/Flaticon_48479.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /_assets/dice.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /_assets/north-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/_assets/north-big.png -------------------------------------------------------------------------------- /_assets/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /companion/companion.js: -------------------------------------------------------------------------------- 1 | 2 | TileLayer = L.TileLayer.extend({ 3 | getTileUrl: function (tilePoint) { 4 | return this._toUrl(this._getZoomForUrl(), tilePoint.x, tilePoint.y); 5 | } 6 | }); 7 | 8 | 9 | function init_companion() { 10 | LAYERS = load_tile_specs(); 11 | 12 | var map = new L.Map('map', { 13 | attributionControl: false, 14 | zoomControl: false, 15 | 16 | dragging: false, 17 | touchZoom: false, 18 | scrollWheelZoom: false, 19 | doubleClickZoom: false, 20 | boxZoom: false, 21 | keyboard: false, 22 | 23 | //fadeAnimation: false, 24 | //zoomAnimation: false, 25 | }); 26 | map.addControl(new L.Control.Scale({ 27 | maxWidth: 125, 28 | position: 'bottomright', 29 | })); 30 | 31 | window.addEventListener("message", function(e) { 32 | update(map, e.data); 33 | }, false); 34 | } 35 | 36 | // nicely split up geometry that crosses the IDL 37 | function process_geo(points) { 38 | var segments = []; 39 | var segment = []; 40 | for (var i = 0; i < points.length - 1; i++) { 41 | var a = points[i]; 42 | var b = points[i + 1]; 43 | segment.push(a); 44 | if (Math.abs(a[0] - b[0]) > 180.) { 45 | segment.push([unwraparound(a[0], b[0], 360), b[1]]); 46 | segments.push(segment); 47 | segment = []; 48 | segment.push([unwraparound(b[0], a[0], 360), a[1]]); 49 | } 50 | } 51 | segment.push(b); 52 | segments.push(segment); 53 | return segments; 54 | } 55 | 56 | function mk_geojson(data, scale_px) { 57 | var geojson = { 58 | type: 'FeatureCollection', 59 | features: [] 60 | }; 61 | var addMulti = function(props, points) { 62 | // need to split into separate linestring features because 63 | // leaflet geojson has issues with multi* 64 | _.each(process_geo(points), function(e) { 65 | geojson.features.push({ 66 | type: 'Feature', 67 | properties: props, 68 | geometry: { 69 | type: 'LineString', //'MultiLineString', 70 | coordinates: e, 71 | } 72 | }); 73 | }); 74 | } 75 | addMulti({name: 'arc'}, circplot(data.pole, data.dist, scale_px)); 76 | addMulti({name: 'line'}, lineplot(data.pole, data.bearing, data.dist, scale_px)); 77 | return geojson; 78 | } 79 | 80 | ZOOM_TIMEOUT = .5; 81 | LAST_ZOOM_CHANGE = null; 82 | function update(map, data) { 83 | // map layer 84 | if (map.cur_layer == null || map.cur_layer.tag != data.layer.id) { 85 | var new_layer = mk_layer(data.layer); 86 | map.addLayer(new_layer, true); 87 | if (map.cur_layer) { 88 | map.removeLayer(map.cur_layer); 89 | } 90 | map.cur_layer = new_layer; 91 | $('#map').css('background', data.layer.bg || '#ddd'); 92 | } 93 | 94 | if (data.dist == null) { 95 | map.setView(data.pole, map.getZoom(), {pan: {animate: false}}); 96 | map.fitWorld({pan: {animate: false}}); 97 | if (map.geodata) { 98 | map.removeLayer(map.geodata); 99 | } 100 | return; 101 | } 102 | 103 | // dimensions and viewport to determine zoom level 104 | if (data.dist / EARTH_MEAN_RAD < .5 * Math.PI) { 105 | var center = data.pole; 106 | var dist = data.dist; 107 | } else { 108 | var center = antipode(data.pole); 109 | var dist = Math.PI * EARTH_MEAN_RAD - data.dist; 110 | } 111 | var lon_extent = max_lon_extent(center[0], dist); 112 | var lat_extent = dist / EARTH_MEAN_RAD * DEG_RAD; 113 | var bounds = new L.latLngBounds([Math.max(center[0] - lat_extent, -85.05), center[1] - lon_extent], 114 | [Math.min(center[0] + lat_extent, 85.05), center[1] + lon_extent]) 115 | 116 | // update zoom 117 | var current_zoom = map.getZoom(); 118 | if (clock() - (LAST_ZOOM_CHANGE || -999) > ZOOM_TIMEOUT) { 119 | map.fitBounds(bounds, { 120 | padding: [60, 60], 121 | pan: {animate: false}, 122 | }); 123 | } 124 | if (map.getZoom() != current_zoom) { 125 | LAST_ZOOM_CHANGE = clock(); 126 | } 127 | 128 | // update center 129 | map.setView(center, map.getZoom(), {pan: {animate: false}}); 130 | 131 | // draw 132 | if (map.geodata) { 133 | map.removeLayer(map.geodata); 134 | } 135 | geodata = new L.geoJson(mk_geojson(data, 256 * Math.pow(2, map.getZoom())), { 136 | style: function(feature) { 137 | return { 138 | color: feature.properties.name == 'arc' ? '#0af' : 'red', 139 | opacity: .8, 140 | weight: 2, 141 | } 142 | }, 143 | onEachFeature: function (feature, layer) { 144 | layer.options.smoothFactor = 0; 145 | } 146 | }); 147 | map.addLayer(geodata); 148 | map.geodata = geodata; 149 | } 150 | 151 | function mk_layer(layer) { 152 | var opts = { 153 | maxZoom: layer.max_depth || 19, 154 | minZoom: layer.no_z0 ? 1 : 0, 155 | }; 156 | var maplayer = new TileLayer(null, opts); 157 | if (layer.url != null) { 158 | maplayer._toUrl = compile_tile_spec(layer.url); 159 | } else { 160 | var spec = _.find(LAYERS, function(e) { return e.key == layer.key; }); 161 | maplayer._toUrl = spec.urlgen(); 162 | } 163 | maplayer.tag = layer.id; 164 | return maplayer; 165 | } 166 | -------------------------------------------------------------------------------- /companion/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | merc extreme: companion map 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | 24 | 37 | 38 |
39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /coverage-worker.js: -------------------------------------------------------------------------------- 1 | importScripts('merc-extreme.js'); 2 | setComputedConstants(); 3 | // explicitly list which constants we use 4 | MAX_ZOOM = MAX_ZOOM; 5 | TILE_FRINGE_WIDTH = TILE_FRINGE_WIDTH; 6 | //// 7 | 8 | 9 | 10 | TILE_OFFSET = 32; 11 | 12 | self.addEventListener('message', function(e) { 13 | if (e.data instanceof Uint8Array) { 14 | self.postMessage(process_buffer(e.data)); 15 | } else { 16 | self.update_pole(e.data); 17 | } 18 | }, false); 19 | 20 | function update_pole(poles) { 21 | self.pole_tiles = {}; 22 | for (var i = 0; i < 2; i++) { 23 | var anti = (i > 0); 24 | var ref = (anti ? poles.antiref : poles.ref); 25 | for (var z = 0; z <= MAX_ZOOM; z++) { 26 | self.pole_tiles[zenc(anti, z)] = {x: Math.floor(Math.pow(2., z) * ref.x), y: Math.floor(Math.pow(2., z) * ref.y)}; 27 | } 28 | } 29 | } 30 | 31 | // mod that doesn't suck for negative numbers 32 | function mod(a, b) { 33 | return ((a % b) + b) % b; 34 | } 35 | 36 | function zenc(anti, z) { 37 | return +anti + ':' + z; 38 | } 39 | 40 | function condense_data(buff) { 41 | var uniques = {}; 42 | for (var i = 0; i < buff.length; i += 4) { 43 | var a = buff[i]; 44 | var b = buff[i + 1]; 45 | var c = buff[i + 2]; 46 | var val = (a << 16) | (b << 8) | c; 47 | uniques[val] = true; 48 | } 49 | return uniques; 50 | } 51 | 52 | function parse_fringe(_fr, child_left) { 53 | fringe = {}; 54 | if (_fr == 3) { 55 | if (TILE_FRINGE_WIDTH < 1/3.) { 56 | fringe.lo = false; 57 | fringe.hi = false; 58 | fringe.parent_lo = child_left; 59 | fringe.parent_hi = !child_left; 60 | } else { 61 | fringe.lo = !child_left; 62 | fringe.hi = child_left; 63 | fringe.parent_lo = false; 64 | fringe.parent_hi = false; 65 | } 66 | } else { 67 | fringe.lo = _fr & 0x1; 68 | fringe.hi = (_fr >> 1) & 0x1; 69 | fringe.parent_lo = fringe.lo && child_left; 70 | fringe.parent_hi = fringe.hi && !child_left; 71 | } 72 | return fringe; 73 | } 74 | 75 | function xadd(z, x0, offset) { 76 | return mod(x0 + offset, Math.pow(2, z)); 77 | } 78 | 79 | function process_buffer(buff) { 80 | data = condense_data(buff); 81 | 82 | tiles = {}; 83 | setTile = function(anti, z, x, y) { 84 | if (z < 0 || x < 0 || y < 0 || x >= Math.pow(2, z) || y >= Math.pow(2, z)) { 85 | return; 86 | } 87 | 88 | tiles[anti + ':' + z + ':' + x + ':' + y] = true; 89 | } 90 | setTiles = function(anti, z, x, y, left, right, top, bottom) { 91 | setTile(anti, z, x, y); 92 | if (left) { 93 | setTile(anti, z, xadd(z, x, -1), y); 94 | } else if (right) { 95 | setTile(anti, z, xadd(z, x, 1), y); 96 | } 97 | if (top) { 98 | setTile(anti, z, x, y - 1); 99 | } else if (bottom) { 100 | setTile(anti, z, x, y + 1); 101 | } 102 | } 103 | 104 | for (var e in data) { 105 | var anti = (e >> 22) & 0x1; 106 | var bleed = (e >> 21) & 0x1; 107 | var z = (e >> 16) & 0x1f; 108 | var _xfringe = (e >> 14) & 0x3; 109 | var _x = (e >> 8) & 0x3f; 110 | var _yfringe = (e >> 6) & 0x3; 111 | var _y = e & 0x3f; 112 | 113 | if (z > MAX_ZOOM) { 114 | // out of bounds 115 | continue; 116 | } 117 | 118 | var xdiff = _x - TILE_OFFSET; 119 | var ydiff = _y - TILE_OFFSET; 120 | var pole_tile = self.pole_tiles[zenc(anti, z)]; 121 | var x = xadd(z, pole_tile.x, xdiff); 122 | var y = ydiff + pole_tile.y; 123 | 124 | var xfringe = parse_fringe(_xfringe, x % 2 == 0); 125 | var yfringe = parse_fringe(_yfringe, y % 2 == 0); 126 | 127 | setTiles(anti, z, x, y, xfringe.lo, xfringe.hi, yfringe.lo, yfringe.hi); 128 | if (bleed) { 129 | setTiles(anti, z - 1, Math.floor(x / 2), Math.floor(y / 2), 130 | xfringe.parent_lo, xfringe.parent_hi, yfringe.parent_lo, yfringe.parent_hi); 131 | } 132 | } 133 | return tiles; 134 | } -------------------------------------------------------------------------------- /debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | merc extreme: debug dashboard 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /debug.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | function init_debug() { 4 | 5 | setComputedConstants(); 6 | 7 | var context = new DebugContext(); 8 | 9 | window.addEventListener("message", function(e) { 10 | context.update(e.data); 11 | }, false); 12 | } 13 | 14 | function DebugContext() { 15 | 16 | this.tovCanvas = $('#tileovl')[0]; 17 | $(this.tovCanvas).attr('width', (TILE_OFFSET_RESOLUTION * (1 + MAX_ZOOM)) + 'px'); 18 | $(this.tovCanvas).attr('height', (TILE_OFFSET_RESOLUTION * 2) + 'px'); 19 | this.tovCtx = this.tovCanvas.getContext('2d'); 20 | 21 | this.stats = new Stats(); 22 | //this.stats.domElement.style.position = 'absolute'; 23 | //this.stats.domElement.style.top = '0px'; 24 | $('body').prepend(this.stats.domElement); 25 | 26 | this.$text = $('#text'); 27 | 28 | this.update = function(data) { 29 | this[{ 30 | frame: 'onframe', 31 | tiles: 'tile_ix_overview', 32 | text: 'textvals', 33 | }[data.type]](data.data); 34 | } 35 | 36 | this.onframe = function() { 37 | this.stats.update(); 38 | } 39 | 40 | this.textvals = function(data) { 41 | var fields = []; 42 | _.each(data, function(v, k) { 43 | fields.push(k + ': ' + v); 44 | }); 45 | this.$text.html(fields.join('
')); 46 | } 47 | 48 | this.tile_ix_overview = function(data) { 49 | var canvas = this.tovCanvas; 50 | var ctx = this.tovCtx; 51 | 52 | //console.log('worker result', data, _.size(data)); 53 | ctx.fillStyle = '#444'; 54 | ctx.fillRect(0, 0, canvas.width, canvas.height); 55 | 56 | var TO = TILE_OFFSET_RESOLUTION; 57 | for (var i = 0; i < 1 + MAX_ZOOM; i++) { 58 | for (var j = 0; j < 2; j++) { 59 | ctx.fillStyle = ((i + j) % 2 == 0 ? '#200' : '#002'); 60 | ctx.fillRect(TO * i, TO * j, Math.min(Math.pow(2, i), TO), Math.min(Math.pow(2, i), TO)); 61 | 62 | ctx.textAlign = 'center'; 63 | ctx.textBaseline = 'middle'; 64 | ctx.fillStyle = '#555'; 65 | ctx.font = '12pt sans-serif'; 66 | ctx.fillText(i, TO * (i + .5), TO * (j + .5)); 67 | } 68 | } 69 | 70 | $.each(data, function(k, v) { 71 | var pcs = k.split(':'); 72 | var anti = +pcs[0]; 73 | var z = +pcs[1]; 74 | var dx = pcs[2] % TO; 75 | var dy = pcs[3] % TO; 76 | 77 | ctx.fillStyle = 'white'; 78 | ctx.fillRect(TO * z + dx, TO * ((anti ? 1 : 0)) + dy, 1, 1); 79 | }); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /fragment.glsl: -------------------------------------------------------------------------------- 1 | // -*- mode: c -*- 2 | 3 | #define PI 3.1415926535 4 | #define PI2 6.2831853071 5 | #define PI_2 1.5707963267 6 | #define PI_4 0.7853981633 7 | 8 | <% _.each(constants, function(v, k) { %> 9 | #define <%= k %> float(<%= v %>) 10 | <% }); %> 11 | 12 | uniform vec2 pole; // lat/lon degrees 13 | uniform vec2 pole_t; // maptile coordinates 14 | uniform vec2 ref_t; 15 | uniform vec2 anti_ref_t; 16 | uniform float scale; // pixels per earth circumference (undistorted) 17 | uniform float bias; // overzoom is capped at 2^bias 18 | uniform float zoom_blend; 19 | uniform float blinder_start; 20 | uniform float blinder_opacity; 21 | 22 | uniform vec2 hp_pole_tile; 23 | uniform vec2 hp_pole_offset; 24 | 25 | uniform vec2 hp_ref_tile; 26 | uniform vec2 hp_antiref_tile; 27 | 28 | varying vec2 merc; // projected mercator unit coordinates: lon:[-180,180] => x:[0,1], lat:0 => y:0 29 | varying vec2 altUV; 30 | 31 | uniform sampler2D tx_ix; 32 | uniform sampler2D tx_atlas[<%= num_atlas_pages %>]; 33 | uniform sampler2D tx_z0; 34 | 35 | void llr_to_xyz(in vec2 llr, out vec3 xyz) { 36 | xyz = vec3(cos(llr.s) * cos(llr.t), sin(llr.s) * cos(llr.t), sin(llr.t)); 37 | } 38 | 39 | void xyz_to_llr(in vec3 xyz, out vec2 llr) { 40 | // don't use asin(z) because of precision issues 41 | llr = vec2(atan(xyz.y, xyz.x), atan(xyz.z, length(vec2(xyz.x, xyz.y)))); 42 | } 43 | 44 | // reverse polar shift, i.e., shift lat/lon 90,0 to coordinates of fake_pole 45 | void translate_pole(in vec2 llr, in vec2 fake_pole, out vec2 llr_trans) { 46 | vec2 rpole = radians(fake_pole); 47 | vec3 xyz; 48 | llr_to_xyz(llr, xyz); 49 | 50 | float latrot = rpole.t - PI_2; 51 | vec3 xyz_trans = mat3( // shift lat +90 to pole lat 52 | cos(latrot), 0, sin(latrot), 53 | 0, 1, 0, 54 | -sin(latrot), 0, cos(latrot) 55 | ) * xyz; 56 | xyz_to_llr(xyz_trans, llr_trans); 57 | llr_trans.s += rpole.s; // shift lon 0 to pole lon 58 | } 59 | 60 | // given tile coordinates, find the location of the relevant tile in the texture atlas 61 | void tex_lookup_atlas(in float z, in bool anti_pole, in vec2 tile, 62 | out int tex_id, out vec2 atlas_t, out bool atlas_oob) { 63 | vec4 x_offset_enc = texture2D(tx_ix, (vec2(z, (.5 * TEX_IX_CELLS * float(anti_pole) + 1.) * TEX_Z_IX_SIZE - 1.) + .5) / TEX_IX_SIZE); 64 | vec4 y_offset_enc = texture2D(tx_ix, (vec2(z, (.5 * TEX_IX_CELLS * float(anti_pole) + 1.) * TEX_Z_IX_SIZE - 2.) + .5) / TEX_IX_SIZE); 65 | vec2 offset = TILE_OFFSET_RESOLUTION * vec2(65536. * floor(255. * x_offset_enc.r + .5) + 256. * floor(255. * x_offset_enc.g + .5) + floor(255. * x_offset_enc.b + .5), 66 | 65536. * floor(255. * y_offset_enc.r + .5) + 256. * floor(255. * y_offset_enc.g + .5) + floor(255. * y_offset_enc.b + .5)); 67 | 68 | vec2 z_cell = vec2(mod(z, TEX_IX_CELLS), floor(z / TEX_IX_CELLS) + TEX_IX_CELLS * .5 * float(anti_pole)); 69 | vec2 ix_offset = tile - offset; 70 | ix_offset.s = mod(ix_offset.s, TEX_Z_IX_SIZE); 71 | vec2 ix_cell = TEX_Z_IX_SIZE * z_cell + ix_offset; 72 | 73 | vec4 slot_enc = texture2D(tx_ix, (ix_cell + .5) / TEX_IX_SIZE); 74 | tex_id = int(floor(255. * slot_enc.r + .5)) - 1; 75 | int slot_x = int(floor(255. * slot_enc.g + .5)); 76 | int slot_y = int(floor(255. * slot_enc.b + .5)); 77 | atlas_t = vec2(slot_x, slot_y); 78 | 79 | atlas_oob = (ix_offset.s >= TEX_Z_IX_SIZE || ix_offset.t < 0. || ix_offset.t >= TEX_Z_IX_SIZE); 80 | } 81 | 82 | // given tile coordinates, get texture atlas coordinates suitable for actual texture access 83 | void tex_lookup_coord(in float z, in bool anti_pole, in vec2 tile, in vec2 tile_p, 84 | out int tex_id, out vec2 atlas_p, out bool atlas_oob) { 85 | vec2 atlas_t; 86 | tex_lookup_atlas(z, anti_pole, tile, tex_id, atlas_t, atlas_oob); 87 | atlas_p = (ATLAS_TILE_SIZE * atlas_t + TILE_SIZE * tile_p + TILE_SKIRT) / ATLAS_TEX_SIZE; 88 | } 89 | 90 | // given "world" coordinates (like tile coordinates but for z0), get texture atlas coordinates 91 | void tex_lookup_abs(in float z, in bool anti_pole, in vec2 abs_map, 92 | out int tex_id, out vec2 atlas_p, out bool atlas_oob) { 93 | vec2 abs_map_z = abs_map * exp2(z); 94 | vec2 tile = floor(abs_map_z); 95 | vec2 tile_p = mod(abs_map_z, 1.); 96 | tex_lookup_coord(z, anti_pole, tile, tile_p, tex_id, atlas_p, atlas_oob); 97 | } 98 | 99 | // look up the texture value for a position, handling various exceptional cases 100 | void tex_lookup_val(in float z, in vec2 abs_map, in bool atlas_oob, in int tex_id, in vec2 atlas_p, out vec4 val) { 101 | if (z == 0. || abs_map.t < 0. || abs_map.t >= 1.) { 102 | // note: just out-of-bounds pixels will only ever blend with the z0 texture, regardless 103 | // of the appropriate zoom level 104 | val = texture2D(tx_z0, vec2(abs_map.s, .5 * (abs_map.t + .5))); 105 | //} else if (atlas_oob) { 106 | //val = vec4(1, 0, 0, 1); 107 | } else if (tex_id >= 0 && !atlas_oob) { 108 | <% for (var i = 0; i < num_atlas_pages; i++) { %> 109 | if (tex_id == <%= i %>) { 110 | val = texture2D(tx_atlas[<%= i %>], atlas_p); 111 | } 112 | <% } %> 113 | } else { 114 | val = vec4(.65, .7, .75, 1); 115 | } 116 | } 117 | 118 | void fringe(in float tile, in float tile_p, out float mode) { 119 | bool fringelo = tile_p < TILE_FRINGE_WIDTH; 120 | bool fringehi = tile_p > 1. - TILE_FRINGE_WIDTH; 121 | bool fringeparent = mod(tile, 2.) == 0. ? tile_p < 2. * TILE_FRINGE_WIDTH : tile_p > 1. - 2. * TILE_FRINGE_WIDTH; 122 | 123 | if (TILE_FRINGE_WIDTH < 1./3. ? fringeparent && !(fringelo || fringehi) : !fringeparent) { 124 | mode = 3.; 125 | } else { 126 | mode = 2. * float(fringehi) + float(fringelo); 127 | } 128 | } 129 | 130 | // perform addition on a 'high precision' number 131 | void hp_reco(in vec2 told, in vec2 oold, out vec2 tnew, out vec2 onew) { 132 | oold += fract(told); 133 | tnew = floor(told) + floor(oold); 134 | onew = fract(oold); 135 | } 136 | 137 | void abs_antipode(in vec2 abs, out vec2 anti) { 138 | anti = vec2(mod(abs.s + .5, 1.), 1. - abs.t); 139 | } 140 | 141 | void main() { 142 | vec2 merc_rad = (merc + vec2(-.5, 0)) * PI2; // projected mercator radians: lon:[-180:180] => x:[-pi, pi] 143 | bool anti_pole = (merc.t < 0.); 144 | 145 | float proj_scale_factor; 146 | float abs_geo_lat; 147 | vec2 abs_map; 148 | vec2 tile; 149 | bool out_of_bounds; 150 | 151 | <% if (geo_mode == 'flat' || geo_mode == 'linear' || output_mode == 'tile') { %> 152 | vec2 tile_p; 153 | vec2 base_tile; 154 | vec2 base_offset; 155 | <% } %> 156 | 157 | <% if (geo_mode == 'flat') { %> 158 | 159 | base_tile = hp_pole_tile; 160 | base_offset = hp_pole_offset; 161 | float theta = merc_rad.s; 162 | if (anti_pole) { 163 | vec2 anti_tile; 164 | abs_antipode(base_tile, anti_tile); 165 | base_tile = anti_tile; 166 | base_offset.t = -base_offset.t; 167 | theta = -theta; 168 | } 169 | 170 | float dist_rad = 2. * exp(-abs(merc_rad.t)); // distance to pole (radians) 171 | float dist = dist_rad / PI2 / cos(radians(pole.t)); // dist in unit merc 172 | vec2 ray = dist * vec2(sin(theta), cos(theta)); 173 | base_offset += ray; 174 | 175 | proj_scale_factor = dist_rad; 176 | abs_geo_lat = radians(pole.t) + dist_rad * cos(theta); 177 | 178 | <% } else if (geo_mode == 'linear') { %> 179 | 180 | base_tile = (anti_pole ? hp_antiref_tile : hp_ref_tile); 181 | base_offset = altUV; 182 | 183 | <% } %> 184 | 185 | <% if (geo_mode == 'sphere' || geo_mode == 'linear') { %> 186 | vec2 geo_rad = vec2(merc_rad.s, 2. * atan(exp(merc_rad.t)) - PI_2); // geographic coordinates, radians 187 | proj_scale_factor = cos(geo_rad.t); 188 | 189 | vec2 abs_geo_rad; 190 | translate_pole(geo_rad, pole, abs_geo_rad); 191 | abs_geo_lat = abs_geo_rad.t; 192 | <% } %> 193 | 194 | <% if (geo_mode == 'sphere') { %> 195 | vec2 abs_merc_rad = vec2(abs_geo_rad.s, log(tan(.5 * abs_geo_rad.t + PI_4))); // projected mercator coordinates, radians 196 | mat3 merc_map_tx = mat3( 197 | 1./PI2, 0, 0, 198 | 0, -1./PI2, 0, 199 | .5, .5, 1 200 | ); 201 | 202 | abs_map = vec2(merc_map_tx * vec3(abs_merc_rad.s, abs_merc_rad.t, 1.)); // map tile coordiantes: lon:[-180,180] => x:[0,1], lat:[-90,90] => y:[+inf,-inf] 203 | abs_map.s = mod(abs_map.s, 1.); 204 | out_of_bounds = (abs_map.t < 0. || abs_map.t >= 1.); 205 | <% } %> 206 | 207 | float res = PI2 / scale * proj_scale_factor; // radians per pixel 208 | // compensate for the fact the map imagery is mercator projected, and has higher resolution towards the poles 209 | float base_res = PI2 / TILE_SIZE * cos(abs_geo_lat); // radians per pixel offered by the lowest zoom layer 210 | float fzoom = min(log2(base_res / res) - bias + .5 * zoom_blend, MAX_ZOOM); // necessary zoom level (on a continuous scale) 211 | float z = max(ceil(fzoom), 0.); // without clamping, -z would be the lod for mipmap of z0 212 | float zblend = (fract(fzoom) == 0. ? 1. : min(fract(fzoom) / zoom_blend, 1.)); // fract(fzoom) == 0 special case needed due to z = ceil(fzoom) 213 | 214 | <% if (geo_mode == 'flat' || geo_mode == 'linear') { %> 215 | hp_reco(base_tile * exp2(z), base_offset * exp2(z), tile, tile_p); 216 | tile.s = mod(tile.s, exp2(z)); 217 | 218 | out_of_bounds = (tile.s < 0. || tile.t < 0. || tile.s >= exp2(z) || tile.t >= exp2(z)); 219 | abs_map = (tile + tile_p) * exp2(-z); // don't think this var would ever be needed in these modes 220 | <% } %> 221 | 222 | 223 | <% if (output_mode == 'tile') { %> 224 | 225 | // zoom level - 5 bits 226 | // anti-pole - 1 bit 227 | // zoom bleed - 1 bit 228 | // tile fringe - 2 bits each for x, y 229 | // offset from ref - 6 bits each for x, y 230 | // 1 bit left over... primary vs. secondary overlay? 231 | 232 | float z_enc; 233 | bool zoom_bleed; 234 | vec2 tile_enc; 235 | float xfringe_enc; 236 | float yfringe_enc; 237 | 238 | if (out_of_bounds) { 239 | z_enc = 31.; // 2^(# zoom bits) - 1 240 | } else { 241 | <% if (geo_mode == 'sphere') { %> 242 | tile = floor(abs_map * exp2(z)); 243 | tile_p = fract(abs_map * exp2(z)); 244 | <% } %> 245 | 246 | z_enc = z; 247 | zoom_bleed = (zblend < 1.); 248 | 249 | vec2 ref = anti_pole ? anti_ref_t : ref_t; 250 | vec2 ref_tile = floor(ref * exp2(z)); 251 | vec2 diff = tile - ref_tile; 252 | diff.s = mod(diff.s + exp2(z - 1.), exp2(z)) - exp2(z - 1.); // handle wraparound 253 | tile_enc = diff + 32.; // 2^(# offset bits - 1) 254 | 255 | fringe(tile.s, tile_p.s, xfringe_enc); 256 | fringe(tile.t, tile_p.t, yfringe_enc); 257 | } 258 | 259 | gl_FragColor = vec4( 260 | (float(anti_pole) * 64. + float(zoom_bleed) * 32. + z_enc) / 255., 261 | (xfringe_enc * 64. + tile_enc.s) / 255., 262 | (yfringe_enc * 64. + tile_enc.t) / 255., 263 | 1); 264 | 265 | <% } %> 266 | 267 | <% if (output_mode == 'tex') { %> 268 | 269 | int tex_id; 270 | bool z_oob; 271 | vec2 atlas_p; 272 | 273 | <% if (geo_mode == 'sphere') { %> 274 | tex_lookup_abs(z, anti_pole, abs_map, tex_id, atlas_p, z_oob); 275 | <% } else if (geo_mode == 'flat' || geo_mode == 'linear') { %> 276 | tex_lookup_coord(z, anti_pole, tile, tile_p, tex_id, atlas_p, z_oob); 277 | <% } %> 278 | 279 | float z_orig = z; 280 | bool zoob_orig = z_oob; 281 | <% for (var i = 0; i < constants.MAX_ZOOM; i++) { %> 282 | if (tex_id < 0 && z > 0.) { 283 | z -= 1.; 284 | <% if (geo_mode == 'sphere') { %> 285 | tex_lookup_abs(z, anti_pole, abs_map, tex_id, atlas_p, z_oob); 286 | <% } else if (geo_mode == 'flat' || geo_mode == 'linear') { %> 287 | vec2 _tile = .5 * tile; 288 | vec2 _offset = .5 * tile_p; 289 | hp_reco(_tile, _offset, tile, tile_p); 290 | tex_lookup_coord(z, anti_pole, tile, tile_p, tex_id, atlas_p, z_oob); 291 | <% } %> 292 | } 293 | <% } %> 294 | 295 | vec4 result; 296 | tex_lookup_val(z, abs_map, z_oob, tex_id, atlas_p, result); 297 | 298 | if (zoom_blend > 0.) { 299 | vec4 parent = result; 300 | if (!(z < z_orig || z == 0. || zblend == 1.)) { 301 | 302 | // only one iteration; we don't care if parent tile is not loaded 303 | z -= 1.; 304 | <% if (geo_mode == 'sphere') { %> 305 | tex_lookup_abs(z, anti_pole, abs_map, tex_id, atlas_p, z_oob); 306 | <% } else if (geo_mode == 'flat' || geo_mode == 'linear') { %> 307 | vec2 _tile = .5 * tile; 308 | vec2 _offset = .5 * tile_p; 309 | hp_reco(_tile, _offset, tile, tile_p); 310 | tex_lookup_coord(z, anti_pole, tile, tile_p, tex_id, atlas_p, z_oob); 311 | <% } %> 312 | 313 | if (tex_id >= 0) { 314 | tex_lookup_val(z, abs_map, z_oob, tex_id, atlas_p, parent); 315 | } 316 | } 317 | result = mix(parent, result, zblend); 318 | } 319 | 320 | // tint according to geo_mode for debugging 321 | //if (true) { 322 | if (false) { 323 | vec4 tint = result; 324 | <% if (geo_mode == 'linear') { %> 325 | tint = vec4(0, 1, 0, 1); 326 | <% } else if (geo_mode == 'flat') { %> 327 | tint = vec4(1, 0, 1, 1); 328 | <% } %> 329 | result = mix(result, tint, .1); 330 | } 331 | 332 | if (floor(merc.x - blinder_start) != 0.) { 333 | result = mix(result, vec4(0, 0, 0, 1), blinder_opacity); 334 | } 335 | 336 | gl_FragColor = result; 337 | <% } %> 338 | 339 | } 340 | 341 | -------------------------------------------------------------------------------- /geodesy.js: -------------------------------------------------------------------------------- 1 | EARTH_MEAN_RAD = 6371009.0 2 | DEG_RAD = 180. / Math.PI; 3 | 4 | function is_zero(x) { 5 | var EPSILON = 1.0e-9 6 | return Math.abs(x) < EPSILON; 7 | } 8 | 9 | // dot product 10 | function dotp(a, b) { 11 | var result = 0; 12 | for (var i = 0; i < a.length; i++) { 13 | result += a[i] * b[i]; 14 | } 15 | return result; 16 | } 17 | 18 | // cross product 19 | // 3-vectors only 20 | function crossp(a, b) { 21 | var result = Array(3); 22 | for (var i = 0; i < 3; i++) { 23 | var t = (i + 1) % 3; 24 | var u = (i + 2) % 3; 25 | result[i] = a[t] * b[u] - a[u] * b[t]; 26 | } 27 | return result; 28 | } 29 | 30 | // length of vector 31 | function vlen(v) { 32 | var sum = 0; 33 | for (var i = 0; i < v.length; i++) { 34 | sum += v[i] * v[i]; 35 | } 36 | return Math.sqrt(sum); 37 | } 38 | 39 | // scale vector by scalar 'k' 40 | function vscale(v, k) { 41 | var result = Array(v.length); 42 | for (var i = 0; i < v.length; i++) { 43 | result[i] = k * v[i]; 44 | } 45 | return result; 46 | } 47 | 48 | // add two vectors 49 | function vadd(a, b) { 50 | var result = Array(a.length); 51 | for (var i = 0; i < a.length; i++) { 52 | result[i] = a[i] + b[i]; 53 | } 54 | return result; 55 | } 56 | 57 | // a - b 58 | function vdiff(a, b) { 59 | return vadd(a, vscale(b, -1)); 60 | } 61 | 62 | // normalize vector; return null if length 0 63 | function vnorm(v) { 64 | var norm = vlen(v); 65 | if (is_zero(norm)) { 66 | return null; 67 | } 68 | return vscale(v, 1. / norm); 69 | } 70 | 71 | // return portion of 'b' orthogonal to 'a', and cosine between 'a' and 'b' 72 | // 'a' and 'b' are unit vectors 73 | function vortho(a, b) { 74 | var cos = dotp(a, b); 75 | return { 76 | v: vadd(b, vscale(a, -cos)), 77 | cos: cos, 78 | }; 79 | } 80 | 81 | // create angle vector given orthogonal basis vectors 'u' and 'v', and angle 'theta' in radians 82 | function vangle(u, v, theta) { 83 | return vadd(vscale(u, Math.cos(theta)), vscale(v, Math.sin(theta))); 84 | } 85 | 86 | // return a function(theta) that rotates 'v' around 'axis' by angle 'theta' 87 | // clockwise when looking from 'axis' toward origin 88 | // 'v' and 'axis' are unit 3-vectors; need not be orthogonal 89 | function vrotator(v, axis) { 90 | var orth = vortho(axis, v); 91 | var axial = vscale(axis, orth.cos); 92 | var vd = crossp(orth.v, axis); 93 | return function(theta) { 94 | return vadd(axial, vangle(orth.v, vd, theta)); 95 | }; 96 | } 97 | 98 | // lat/lon to unit 3-vector 99 | function ll_to_xyz(ll) { 100 | var rlat = ll[0] / DEG_RAD; 101 | var rlon = ll[1] / DEG_RAD; 102 | var latcos = Math.cos(rlat); 103 | return [Math.cos(rlon) * latcos, Math.sin(rlon) * latcos, Math.sin(rlat)]; 104 | } 105 | 106 | // unit 3-vector to lat/lon 107 | function xyz_to_ll(v) { 108 | var x = v[0]; 109 | var y = v[1]; 110 | var z = v[2]; 111 | var rlon = (is_zero(x) && is_zero(y) ? 0 : Math.atan2(y, x)); 112 | var rlat = Math.atan2(z, vlen([x, y])); 113 | return [rlat * DEG_RAD, rlon * DEG_RAD]; 114 | } 115 | 116 | // return 'north' and 'east' vectors for a given position vector 117 | function orientate(vp) { 118 | var east = vnorm(crossp([0, 0, 1], vp)); 119 | if (east == null) { 120 | // pole 121 | east = [0, -vp[2], 0]; 122 | } 123 | north = crossp(vp, east); 124 | return {n: north, e: east}; 125 | } 126 | 127 | // return heading vector for position and heading 128 | function vhead(vp, heading) { 129 | var rhead = heading / DEG_RAD; 130 | var basis = orientate(vp); 131 | return vangle(basis.n, basis.e, rhead); 132 | } 133 | 134 | // compute heading from from position and direction vector 135 | function headv(vp, vdir) { 136 | var basis = orientate(vp); 137 | return Math.atan2(dotp(vdir, basis.e), dotp(vdir, basis.n)) * DEG_RAD; 138 | } 139 | 140 | // distance between p0 and p1 in meters (in radians if 'in_rad' is true) 141 | function distance(p0, p1, in_rad) { 142 | var v0 = ll_to_xyz(p0); 143 | var v1 = ll_to_xyz(p1); 144 | var orth = vortho(v0, v1); 145 | return (in_rad ? 1 : EARTH_MEAN_RAD) * Math.atan2(vlen(orth.v), orth.cos); 146 | } 147 | 148 | // return compass bearing from src to dst; null if src/dst are antipodal 149 | // if src is polar, treat direction of 0 longitude as north 150 | function bearing(src, dst) { 151 | var v0 = ll_to_xyz(src); 152 | var v1 = ll_to_xyz(dst); 153 | var orth = vortho(v0, v1); 154 | if (is_zero(vlen(orth.v))) { 155 | return null; 156 | } 157 | return headv(v0, orth.v) 158 | } 159 | 160 | function plotv(vp, vdir, theta, incl_new_heading) { 161 | var vp2 = vangle(vp, vdir, theta); 162 | var p = xyz_to_ll(vp2); 163 | if (incl_new_heading) { 164 | var vdir2 = vangle(vdir, vscale(vp, -1.), theta); 165 | var heading = headv(vp2, vdir2); 166 | return {p: p, heading: heading}; 167 | } 168 | return p; 169 | } 170 | 171 | // return a function(dist) 172 | function line_plotter(p, heading) { 173 | var vp = ll_to_xyz(p); 174 | var vdir = vhead(vp, heading); 175 | return function(dist, incl_new_heading) { 176 | return plotv(vp, vdir, dist / EARTH_MEAN_RAD, incl_new_heading); 177 | }; 178 | } 179 | 180 | // return a function(theta) 181 | function arc_plotter(p, dist) { 182 | var vp = ll_to_xyz(p); 183 | var ref = ll_to_xyz(line_plotter(p, 0)(dist)); 184 | var rot = vrotator(ref, vp); 185 | return function(theta) { 186 | return xyz_to_ll(rot(theta / DEG_RAD)); 187 | } 188 | } 189 | 190 | function max_lon_extent(lat, radius) { 191 | var k = Math.sin(radius / EARTH_MEAN_RAD) / Math.cos(lat / DEG_RAD); 192 | return (Math.abs(k) > 1. ? Math.PI : Math.asin(k)) * DEG_RAD; 193 | } 194 | 195 | function interpolate_curve(func, anchors, error_thresh, min_dt) { 196 | var points = []; 197 | var _interp = function(lo, hi, flo, fhi) { 198 | if (Math.abs(hi - lo) <= min_dt) { 199 | return; 200 | } 201 | 202 | var mid = .5 * (lo + hi); 203 | var fmid = func(mid); 204 | if (error_thresh(fmid, flo, fhi)) { 205 | _interp(lo, mid, flo, fmid); 206 | points.push(fmid); 207 | _interp(mid, hi, fmid, fhi); 208 | } 209 | }; 210 | 211 | for (var i = 0; i < anchors.length - 1; i++) { 212 | var lo = anchors[i]; 213 | var hi = anchors[i + 1]; 214 | var flo = func(lo); 215 | var fhi = func(hi); 216 | points.push(flo); 217 | _interp(lo, hi, flo, fhi); 218 | } 219 | points.push(fhi); 220 | return points; 221 | } 222 | 223 | 224 | 225 | 226 | 227 | function base_point(p0, dist) { 228 | if (dist / EARTH_MEAN_RAD > .5 * Math.PI) { 229 | // antipode 230 | return [-p0[0], lon_norm(p0[1] + 180.)]; 231 | } else { 232 | return p0; 233 | } 234 | } 235 | 236 | function lineplot(p0, heading, maxdist, scale_px) { 237 | return mapplot( 238 | line_plotter(p0, heading), 239 | // don't start at zero to avoid clipping error when using antipole as basis 240 | [1e-6 * maxdist, .5 * maxdist, maxdist], 241 | base_point(p0, maxdist), scale_px, 100 242 | ); 243 | } 244 | 245 | function circplot(p0, radius, scale_px) { 246 | return mapplot( 247 | arc_plotter(p0, radius), 248 | [0, 60, 120, 180, 240, 300, 360], 249 | base_point(p0, radius), scale_px, 1 250 | ); 251 | } 252 | 253 | function mapplot(plotter, anchors, pbase, scale_px, min_dt) { 254 | var base_merc = ll_to_xy(pbase[0], pbase[1]); 255 | base_merc = [base_merc.x, base_merc.y]; 256 | 257 | return _.map(interpolate_curve( 258 | function(t) { 259 | var ll = plotter(t); 260 | var xy = ll_to_xy(ll[0], ll[1]); 261 | return {ll: ll, xy: [xy.x, xy.y]}; 262 | }, 263 | anchors, 264 | function(fmid, flo, fhi) { 265 | var approx = vadd(vscale(flo.xy, .5), vscale(fhi.xy, .5)); 266 | var diff = vlen(vdiff(fmid.xy, approx)); 267 | 268 | var remoteness = vlen(vdiff(approx, base_merc)); 269 | var MAX_VIEWPORT_RADIUS = 1024; 270 | var threshold_scale = Math.max(remoteness * scale_px / MAX_VIEWPORT_RADIUS, 1.); 271 | 272 | var threshold = .25 / scale_px * threshold_scale; 273 | return diff > threshold; 274 | }, 275 | min_dt 276 | ), function(e) { return [unwraparound(pbase[1], e.ll[1], 360), e.ll[0]]; }); 277 | } 278 | -------------------------------------------------------------------------------- /img/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/add.png -------------------------------------------------------------------------------- /img/alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/alert.png -------------------------------------------------------------------------------- /img/crosshair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/crosshair.png -------------------------------------------------------------------------------- /img/dice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/dice.png -------------------------------------------------------------------------------- /img/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/github.png -------------------------------------------------------------------------------- /img/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/help.png -------------------------------------------------------------------------------- /img/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/layers.png -------------------------------------------------------------------------------- /img/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/map.png -------------------------------------------------------------------------------- /img/north.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/north.png -------------------------------------------------------------------------------- /img/placemark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/placemark.png -------------------------------------------------------------------------------- /img/rotate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/rotate.png -------------------------------------------------------------------------------- /img/routes_example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/routes_example.jpg -------------------------------------------------------------------------------- /img/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/search.png -------------------------------------------------------------------------------- /img/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/settings.png -------------------------------------------------------------------------------- /img/swap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrgriscom/merc-extreme/b2d60a82bca24f8f9b1594de2d0552f414147615/img/swap.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Mercator: Extreme 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 40 | 41 | 48 | 49 | 50 | 51 | 52 | 319 | 320 |
321 | 322 |
323 | MERCATOR: EXTREME 324 | by Drew Roos 325 |
326 | 327 |
328 |
329 |
loading…
330 | 331 |
332 |
333 |
334 | 335 |
336 | 337 |
338 | 339 | 340 | MERCATOR: EXTREME 341 | by Drew Roos 342 | 343 | 344 | 351 | 352 |
353 | 539 |
540 | 541 | 542 | 543 |
544 | 582 |
583 | 584 |
585 |
586 | 587 |
588 | 600 | 620 | 629 | 630 | 649 |
650 | 651 | 655 | 656 |
657 |
658 | 659 |
660 | 661 |
662 |

663 |
×
664 | Controls 665 |

666 |
667 |
668 | 669 | 763 | 764 |
765 |
766 | POLE
767 |
768 |
769 | ANTI-POLE
770 | 771 | 772 | 773 |
774 |
775 | POINTER
776 |
777 | FROM POLE
778 |
779 | 780 |
781 |
782 |
783 |
784 |
785 | 786 |
787 | 788 |
789 | 790 | 791 | 792 | -------------------------------------------------------------------------------- /layer-test.js: -------------------------------------------------------------------------------- 1 | 2 | function tile_coord() { 3 | var params = new URLSearchParams(window.location.search); 4 | var defaults = {z: 2, x: 0, y: 1}; 5 | var z = params.get('z') || defaults.z; 6 | defaults.x = Math.min(defaults.x, Math.pow(2, z) - 1); 7 | defaults.y = Math.min(defaults.y, Math.pow(2, z) - 1); 8 | var x = params.get('x') || defaults.x; 9 | var y = params.get('y') || defaults.y; 10 | return {z: +z, x: +x, y: +y}; 11 | } 12 | 13 | function zoom_in(coord, dx, dy) { 14 | coord.z += 1; 15 | coord.x = 2*coord.x + dx; 16 | coord.y = 2*coord.y + dy; 17 | set_coord(coord); 18 | } 19 | 20 | function zoom_out(coord) { 21 | if (coord.z > 0) { 22 | coord.z -= 1; 23 | coord.x = Math.floor(coord.x / 2); 24 | coord.y = Math.floor(coord.y / 2); 25 | set_coord(coord); 26 | } 27 | } 28 | 29 | function set_coord(coord, no_reload) { 30 | var query = new URLSearchParams(coord).toString(); 31 | if (no_reload) { 32 | history.replaceState(null, null, window.location.pathname + '?' + query); 33 | } else { 34 | window.location.search = query; 35 | } 36 | } 37 | 38 | function test_tile(lyr, coord, specialcase) { 39 | var img = new Image(); 40 | img.width = TILE_SIZE; 41 | img.height = TILE_SIZE; 42 | img.crossOrigin = 'anonymous'; 43 | 44 | img.src = lyr.tilefunc()(coord.z, coord.x, coord.y); 45 | 46 | if (specialcase != null) { 47 | var $container = $('#specialcase'); 48 | var zstr = specialcase; // + ' ' + coord.z + '-' + coord.x + '-' + coord.y; 49 | } else { 50 | var $container = $('#general'); 51 | var zstr = (lyr.max_depth() != null ? 'z:' + lyr.min_depth() + '–' + lyr.max_depth() : 52 | lyr.min_depth() > 0 ? 'z:' + lyr.min_depth() + '+' : ''); 53 | 54 | $(img).css('cursor', 'pointer'); 55 | $(img).click(function(e) { 56 | zoom_in(coord, 57 | Math.floor((e.pageX - this.offsetLeft) / (TILE_SIZE / 2)), 58 | Math.floor((e.pageY - this.offsetTop) / (TILE_SIZE / 2)) 59 | ); 60 | }); 61 | $(img).bind('contextmenu', function(e) { 62 | zoom_out(coord); 63 | return false; 64 | }); 65 | } 66 | var name = (lyr.custom() ? 'custom:' + (lyr.name() || '') : lyr.key()); 67 | 68 | $test = $('
'); 69 | $title = $('
'); 70 | $link = $(''); 71 | $link.attr('href', img.src); 72 | $link.html(name + ' ' + zstr); 73 | $title.append($link); 74 | $(img).css('background', lyr.bg); 75 | $test.append($title); 76 | $test.append(img); 77 | $container.append($test); 78 | } 79 | 80 | function layer_test() { 81 | var coord = tile_coord(); 82 | // show full tile coord in url bar 83 | set_coord(coord, true); 84 | 85 | var layers = {}; 86 | _.each(load_layers(), function(spec) { 87 | var lyr = new LayerModel(spec); 88 | if (lyr.key()) { 89 | layers[lyr.key()] = lyr; 90 | } 91 | test_tile(lyr, coord); 92 | }); 93 | 94 | test_tile(layers['google:sat'], {z: 23, x:4965358, y: 4264294}, 'deepzoom'); 95 | } 96 | -------------------------------------------------------------------------------- /layers-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | merc extreme: layers test 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 29 | 30 |
31 |
32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /lib/filesaver.js: -------------------------------------------------------------------------------- 1 | /* FileSaver.js 2 | * A saveAs() FileSaver implementation. 3 | * 2014-08-29 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * License: X11/MIT 7 | * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md 8 | */ 9 | 10 | /*global self */ 11 | /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 12 | 13 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 14 | 15 | var saveAs = saveAs 16 | // IE 10+ (native saveAs) 17 | || (typeof navigator !== "undefined" && 18 | navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator)) 19 | // Everyone else 20 | || (function(view) { 21 | "use strict"; 22 | // IE <10 is explicitly unsupported 23 | if (typeof navigator !== "undefined" && 24 | /MSIE [1-9]\./.test(navigator.userAgent)) { 25 | return; 26 | } 27 | var 28 | doc = view.document 29 | // only get URL when necessary in case Blob.js hasn't overridden it yet 30 | , get_URL = function() { 31 | return view.URL || view.webkitURL || view; 32 | } 33 | , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 34 | , can_use_save_link = "download" in save_link 35 | , click = function(node) { 36 | var event = doc.createEvent("MouseEvents"); 37 | event.initMouseEvent( 38 | "click", true, false, view, 0, 0, 0, 0, 0 39 | , false, false, false, false, 0, null 40 | ); 41 | node.dispatchEvent(event); 42 | } 43 | , webkit_req_fs = view.webkitRequestFileSystem 44 | , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem 45 | , throw_outside = function(ex) { 46 | (view.setImmediate || view.setTimeout)(function() { 47 | throw ex; 48 | }, 0); 49 | } 50 | , force_saveable_type = "application/octet-stream" 51 | , fs_min_size = 0 52 | // See https://code.google.com/p/chromium/issues/detail?id=375297#c7 for 53 | // the reasoning behind the timeout and revocation flow 54 | , arbitrary_revoke_timeout = 10 55 | , revoke = function(file) { 56 | var revoker = function() { 57 | if (typeof file === "string") { // file is an object URL 58 | get_URL().revokeObjectURL(file); 59 | } else { // file is a File 60 | file.remove(); 61 | } 62 | }; 63 | if (view.chrome) { 64 | revoker(); 65 | } else { 66 | setTimeout(revoker, arbitrary_revoke_timeout); 67 | } 68 | } 69 | , dispatch = function(filesaver, event_types, event) { 70 | event_types = [].concat(event_types); 71 | var i = event_types.length; 72 | while (i--) { 73 | var listener = filesaver["on" + event_types[i]]; 74 | if (typeof listener === "function") { 75 | try { 76 | listener.call(filesaver, event || filesaver); 77 | } catch (ex) { 78 | throw_outside(ex); 79 | } 80 | } 81 | } 82 | } 83 | , FileSaver = function(blob, name) { 84 | // First try a.download, then web filesystem, then object URLs 85 | var 86 | filesaver = this 87 | , type = blob.type 88 | , blob_changed = false 89 | , object_url 90 | , target_view 91 | , dispatch_all = function() { 92 | dispatch(filesaver, "writestart progress write writeend".split(" ")); 93 | } 94 | // on any filesys errors revert to saving with object URLs 95 | , fs_error = function() { 96 | // don't create more object URLs than needed 97 | if (blob_changed || !object_url) { 98 | object_url = get_URL().createObjectURL(blob); 99 | } 100 | if (target_view) { 101 | target_view.location.href = object_url; 102 | } else { 103 | var new_tab = view.open(object_url, "_blank"); 104 | if (new_tab == undefined && typeof safari !== "undefined") { 105 | //Apple do not allow window.open, see http://bit.ly/1kZffRI 106 | view.location.href = object_url 107 | } 108 | } 109 | filesaver.readyState = filesaver.DONE; 110 | dispatch_all(); 111 | revoke(object_url); 112 | } 113 | , abortable = function(func) { 114 | return function() { 115 | if (filesaver.readyState !== filesaver.DONE) { 116 | return func.apply(this, arguments); 117 | } 118 | }; 119 | } 120 | , create_if_not_found = {create: true, exclusive: false} 121 | , slice 122 | ; 123 | filesaver.readyState = filesaver.INIT; 124 | if (!name) { 125 | name = "download"; 126 | } 127 | if (can_use_save_link) { 128 | object_url = get_URL().createObjectURL(blob); 129 | save_link.href = object_url; 130 | save_link.download = name; 131 | click(save_link); 132 | filesaver.readyState = filesaver.DONE; 133 | dispatch_all(); 134 | revoke(object_url); 135 | return; 136 | } 137 | // Object and web filesystem URLs have a problem saving in Google Chrome when 138 | // viewed in a tab, so I force save with application/octet-stream 139 | // http://code.google.com/p/chromium/issues/detail?id=91158 140 | // Update: Google errantly closed 91158, I submitted it again: 141 | // https://code.google.com/p/chromium/issues/detail?id=389642 142 | if (view.chrome && type && type !== force_saveable_type) { 143 | slice = blob.slice || blob.webkitSlice; 144 | blob = slice.call(blob, 0, blob.size, force_saveable_type); 145 | blob_changed = true; 146 | } 147 | // Since I can't be sure that the guessed media type will trigger a download 148 | // in WebKit, I append .download to the filename. 149 | // https://bugs.webkit.org/show_bug.cgi?id=65440 150 | if (webkit_req_fs && name !== "download") { 151 | name += ".download"; 152 | } 153 | if (type === force_saveable_type || webkit_req_fs) { 154 | target_view = view; 155 | } 156 | if (!req_fs) { 157 | fs_error(); 158 | return; 159 | } 160 | fs_min_size += blob.size; 161 | req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) { 162 | fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { 163 | var save = function() { 164 | dir.getFile(name, create_if_not_found, abortable(function(file) { 165 | file.createWriter(abortable(function(writer) { 166 | writer.onwriteend = function(event) { 167 | target_view.location.href = file.toURL(); 168 | filesaver.readyState = filesaver.DONE; 169 | dispatch(filesaver, "writeend", event); 170 | revoke(file); 171 | }; 172 | writer.onerror = function() { 173 | var error = writer.error; 174 | if (error.code !== error.ABORT_ERR) { 175 | fs_error(); 176 | } 177 | }; 178 | "writestart progress write abort".split(" ").forEach(function(event) { 179 | writer["on" + event] = filesaver["on" + event]; 180 | }); 181 | writer.write(blob); 182 | filesaver.abort = function() { 183 | writer.abort(); 184 | filesaver.readyState = filesaver.DONE; 185 | }; 186 | filesaver.readyState = filesaver.WRITING; 187 | }), fs_error); 188 | }), fs_error); 189 | }; 190 | dir.getFile(name, {create: false}, abortable(function(file) { 191 | // delete file if it already exists 192 | file.remove(); 193 | save(); 194 | }), abortable(function(ex) { 195 | if (ex.code === ex.NOT_FOUND_ERR) { 196 | save(); 197 | } else { 198 | fs_error(); 199 | } 200 | })); 201 | }), fs_error); 202 | }), fs_error); 203 | } 204 | , FS_proto = FileSaver.prototype 205 | , saveAs = function(blob, name) { 206 | return new FileSaver(blob, name); 207 | } 208 | ; 209 | FS_proto.abort = function() { 210 | var filesaver = this; 211 | filesaver.readyState = filesaver.DONE; 212 | dispatch(filesaver, "abort"); 213 | }; 214 | FS_proto.readyState = FS_proto.INIT = 0; 215 | FS_proto.WRITING = 1; 216 | FS_proto.DONE = 2; 217 | 218 | FS_proto.error = 219 | FS_proto.onwritestart = 220 | FS_proto.onprogress = 221 | FS_proto.onwrite = 222 | FS_proto.onabort = 223 | FS_proto.onerror = 224 | FS_proto.onwriteend = 225 | null; 226 | 227 | return saveAs; 228 | }( 229 | typeof self !== "undefined" && self 230 | || typeof window !== "undefined" && window 231 | || this.content 232 | )); 233 | // `self` is undefined in Firefox for Android content script context 234 | // while `this` is nsIContentFrameMessageManager 235 | // with an attribute `content` that corresponds to the window 236 | 237 | if (typeof module !== "undefined" && module !== null) { 238 | module.exports = saveAs; 239 | } else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) { 240 | define([], function() { 241 | return saveAs; 242 | }); 243 | } 244 | -------------------------------------------------------------------------------- /lib/hammer.min.js: -------------------------------------------------------------------------------- 1 | /*! Hammer.JS - v2.0.8 - 2016-04-23 2 | * http://hammerjs.github.io/ 3 | * 4 | * Copyright (c) 2016 Jorik Tangelder; 5 | * Licensed under the MIT license */ 6 | !function(a,b,c,d){"use strict";function e(a,b,c){return setTimeout(j(a,c),b)}function f(a,b,c){return Array.isArray(a)?(g(a,c[b],c),!0):!1}function g(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",f=a.console&&(a.console.warn||a.console.log);return f&&f.call(a.console,e,d),b.apply(this,arguments)}}function i(a,b,c){var d,e=b.prototype;d=a.prototype=Object.create(e),d.constructor=a,d._super=e,c&&la(d,c)}function j(a,b){return function(){return a.apply(b,arguments)}}function k(a,b){return typeof a==oa?a.apply(b?b[0]||d:d,b):a}function l(a,b){return a===d?b:a}function m(a,b,c){g(q(b),function(b){a.addEventListener(b,c,!1)})}function n(a,b,c){g(q(b),function(b){a.removeEventListener(b,c,!1)})}function o(a,b){for(;a;){if(a==b)return!0;a=a.parentNode}return!1}function p(a,b){return a.indexOf(b)>-1}function q(a){return a.trim().split(/\s+/g)}function r(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;dc[b]}):d.sort()),d}function u(a,b){for(var c,e,f=b[0].toUpperCase()+b.slice(1),g=0;g1&&!c.firstMultiple?c.firstMultiple=D(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=E(d);b.timeStamp=ra(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=I(h,i),b.distance=H(h,i),B(c,b),b.offsetDirection=G(b.deltaX,b.deltaY);var j=F(b.deltaTime,b.deltaX,b.deltaY);b.overallVelocityX=j.x,b.overallVelocityY=j.y,b.overallVelocity=qa(j.x)>qa(j.y)?j.x:j.y,b.scale=g?K(g.pointers,d):1,b.rotation=g?J(g.pointers,d):0,b.maxPointers=c.prevInput?b.pointers.length>c.prevInput.maxPointers?b.pointers.length:c.prevInput.maxPointers:b.pointers.length,C(c,b);var k=a.element;o(b.srcEvent.target,k)&&(k=b.srcEvent.target),b.target=k}function B(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};b.eventType!==Ea&&f.eventType!==Ga||(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function C(a,b){var c,e,f,g,h=a.lastInterval||b,i=b.timeStamp-h.timeStamp;if(b.eventType!=Ha&&(i>Da||h.velocity===d)){var j=b.deltaX-h.deltaX,k=b.deltaY-h.deltaY,l=F(i,j,k);e=l.x,f=l.y,c=qa(l.x)>qa(l.y)?l.x:l.y,g=G(j,k),a.lastInterval=b}else c=h.velocity,e=h.velocityX,f=h.velocityY,g=h.direction;b.velocity=c,b.velocityX=e,b.velocityY=f,b.direction=g}function D(a){for(var b=[],c=0;ce;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:pa(c/b),y:pa(d/b)}}function F(a,b,c){return{x:b/a||0,y:c/a||0}}function G(a,b){return a===b?Ia:qa(a)>=qa(b)?0>a?Ja:Ka:0>b?La:Ma}function H(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function I(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function J(a,b){return I(b[1],b[0],Ra)+I(a[1],a[0],Ra)}function K(a,b){return H(b[0],b[1],Ra)/H(a[0],a[1],Ra)}function L(){this.evEl=Ta,this.evWin=Ua,this.pressed=!1,x.apply(this,arguments)}function M(){this.evEl=Xa,this.evWin=Ya,x.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function N(){this.evTarget=$a,this.evWin=_a,this.started=!1,x.apply(this,arguments)}function O(a,b){var c=s(a.touches),d=s(a.changedTouches);return b&(Ga|Ha)&&(c=t(c.concat(d),"identifier",!0)),[c,d]}function P(){this.evTarget=bb,this.targetIds={},x.apply(this,arguments)}function Q(a,b){var c=s(a.touches),d=this.targetIds;if(b&(Ea|Fa)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=s(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return o(a.target,i)}),b===Ea)for(e=0;e-1&&d.splice(a,1)};setTimeout(e,cb)}}function U(a){for(var b=a.srcEvent.clientX,c=a.srcEvent.clientY,d=0;d=f&&db>=g)return!0}return!1}function V(a,b){this.manager=a,this.set(b)}function W(a){if(p(a,jb))return jb;var b=p(a,kb),c=p(a,lb);return b&&c?jb:b||c?b?kb:lb:p(a,ib)?ib:hb}function X(){if(!fb)return!1;var b={},c=a.CSS&&a.CSS.supports;return["auto","manipulation","pan-y","pan-x","pan-x pan-y","none"].forEach(function(d){b[d]=c?a.CSS.supports("touch-action",d):!0}),b}function Y(a){this.options=la({},this.defaults,a||{}),this.id=v(),this.manager=null,this.options.enable=l(this.options.enable,!0),this.state=nb,this.simultaneous={},this.requireFail=[]}function Z(a){return a&sb?"cancel":a&qb?"end":a&pb?"move":a&ob?"start":""}function $(a){return a==Ma?"down":a==La?"up":a==Ja?"left":a==Ka?"right":""}function _(a,b){var c=b.manager;return c?c.get(a):a}function aa(){Y.apply(this,arguments)}function ba(){aa.apply(this,arguments),this.pX=null,this.pY=null}function ca(){aa.apply(this,arguments)}function da(){Y.apply(this,arguments),this._timer=null,this._input=null}function ea(){aa.apply(this,arguments)}function fa(){aa.apply(this,arguments)}function ga(){Y.apply(this,arguments),this.pTime=!1,this.pCenter=!1,this._timer=null,this._input=null,this.count=0}function ha(a,b){return b=b||{},b.recognizers=l(b.recognizers,ha.defaults.preset),new ia(a,b)}function ia(a,b){this.options=la({},ha.defaults,b||{}),this.options.inputTarget=this.options.inputTarget||a,this.handlers={},this.session={},this.recognizers=[],this.oldCssProps={},this.element=a,this.input=y(this),this.touchAction=new V(this,this.options.touchAction),ja(this,!0),g(this.options.recognizers,function(a){var b=this.add(new a[0](a[1]));a[2]&&b.recognizeWith(a[2]),a[3]&&b.requireFailure(a[3])},this)}function ja(a,b){var c=a.element;if(c.style){var d;g(a.options.cssProps,function(e,f){d=u(c.style,f),b?(a.oldCssProps[d]=c.style[d],c.style[d]=e):c.style[d]=a.oldCssProps[d]||""}),b||(a.oldCssProps={})}}function ka(a,c){var d=b.createEvent("Event");d.initEvent(a,!0,!0),d.gesture=c,c.target.dispatchEvent(d)}var la,ma=["","webkit","Moz","MS","ms","o"],na=b.createElement("div"),oa="function",pa=Math.round,qa=Math.abs,ra=Date.now;la="function"!=typeof Object.assign?function(a){if(a===d||null===a)throw new TypeError("Cannot convert undefined or null to object");for(var b=Object(a),c=1;ch&&(b.push(a),h=b.length-1):e&(Ga|Ha)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var Za={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},$a="touchstart",_a="touchstart touchmove touchend touchcancel";i(N,x,{handler:function(a){var b=Za[a.type];if(b===Ea&&(this.started=!0),this.started){var c=O.call(this,a,b);b&(Ga|Ha)&&c[0].length-c[1].length===0&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}}});var ab={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},bb="touchstart touchmove touchend touchcancel";i(P,x,{handler:function(a){var b=ab[a.type],c=Q.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}});var cb=2500,db=25;i(R,x,{handler:function(a,b,c){var d=c.pointerType==za,e=c.pointerType==Ba;if(!(e&&c.sourceCapabilities&&c.sourceCapabilities.firesTouchEvents)){if(d)S.call(this,b,c);else if(e&&U.call(this,c))return;this.callback(a,b,c)}},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var eb=u(na.style,"touchAction"),fb=eb!==d,gb="compute",hb="auto",ib="manipulation",jb="none",kb="pan-x",lb="pan-y",mb=X();V.prototype={set:function(a){a==gb&&(a=this.compute()),fb&&this.manager.element.style&&mb[a]&&(this.manager.element.style[eb]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return g(this.manager.recognizers,function(b){k(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),W(a.join(" "))},preventDefaults:function(a){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return void b.preventDefault();var d=this.actions,e=p(d,jb)&&!mb[jb],f=p(d,lb)&&!mb[lb],g=p(d,kb)&&!mb[kb];if(e){var h=1===a.pointers.length,i=a.distance<2,j=a.deltaTime<250;if(h&&i&&j)return}return g&&f?void 0:e||f&&c&Na||g&&c&Oa?this.preventSrc(b):void 0},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var nb=1,ob=2,pb=4,qb=8,rb=qb,sb=16,tb=32;Y.prototype={defaults:{},set:function(a){return la(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(f(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=_(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return f(a,"dropRecognizeWith",this)?this:(a=_(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(f(a,"requireFailure",this))return this;var b=this.requireFail;return a=_(a,this),-1===r(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(f(a,"dropRequireFailure",this))return this;a=_(a,this);var b=r(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function b(b){c.manager.emit(b,a)}var c=this,d=this.state;qb>d&&b(c.options.event+Z(d)),b(c.options.event),a.additionalEvent&&b(a.additionalEvent),d>=qb&&b(c.options.event+Z(d))},tryEmit:function(a){return this.canEmit()?this.emit(a):void(this.state=tb)},canEmit:function(){for(var a=0;af?Ja:Ka,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?Ia:0>g?La:Ma,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return aa.prototype.attrTest.call(this,a)&&(this.state&ob||!(this.state&ob)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=$(a.direction);b&&(a.additionalEvent=this.options.event+b),this._super.emit.call(this,a)}}),i(ca,aa,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&ob)},emit:function(a){if(1!==a.scale){var b=a.scale<1?"in":"out";a.additionalEvent=this.options.event+b}this._super.emit.call(this,a)}}),i(da,Y,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[hb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distanceb.time;if(this._input=a,!d||!c||a.eventType&(Ga|Ha)&&!f)this.reset();else if(a.eventType&Ea)this.reset(),this._timer=e(function(){this.state=rb,this.tryEmit()},b.time,this);else if(a.eventType&Ga)return rb;return tb},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===rb&&(a&&a.eventType&Ga?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=ra(),this.manager.emit(this.options.event,this._input)))}}),i(ea,aa,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&ob)}}),i(fa,aa,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:Na|Oa,pointers:1},getTouchAction:function(){return ba.prototype.getTouchAction.call(this)},attrTest:function(a){var b,c=this.options.direction;return c&(Na|Oa)?b=a.overallVelocity:c&Na?b=a.overallVelocityX:c&Oa&&(b=a.overallVelocityY),this._super.attrTest.call(this,a)&&c&a.offsetDirection&&a.distance>this.options.threshold&&a.maxPointers==this.options.pointers&&qa(b)>this.options.velocity&&a.eventType&Ga},emit:function(a){var b=$(a.offsetDirection);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),i(ga,Y,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[ib]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance=0)&&i(t,!s)}}),e("
").outerWidth(1).jquery||e.each(["Width","Height"],function(i,n){function s(t,i,n,s){return e.each(a,function(){i-=parseFloat(e.css(t,"padding"+this))||0,n&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),s&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var a="Width"===n?["Left","Right"]:["Top","Bottom"],o=n.toLowerCase(),r={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+n]=function(i){return i===t?r["inner"+n].call(this):this.each(function(){e(this).css(o,s(this,i)+"px")})},e.fn["outer"+n]=function(t,i){return"number"!=typeof t?r["outer"+n].call(this,t):this.each(function(){e(this).css(o,s(this,t,!0,i)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.support.selectstart="onselectstart"in document.createElement("div"),e.fn.extend({disableSelection:function(){return this.bind((e.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),e.extend(e.ui,{plugin:{add:function(t,i,n){var s,a=e.ui[t].prototype;for(s in n)a.plugins[s]=a.plugins[s]||[],a.plugins[s].push([i,n[s]])},call:function(e,t,i){var n,s=e.plugins[t];if(s&&e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType)for(n=0;s.length>n;n++)e.options[s[n][0]]&&s[n][1].apply(e.element,i)}},hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var n=i&&"left"===i?"scrollLeft":"scrollTop",s=!1;return t[n]>0?!0:(t[n]=1,s=t[n]>0,t[n]=0,s)}})})(jQuery);(function(t,e){var i=0,s=Array.prototype.slice,n=t.cleanData;t.cleanData=function(e){for(var i,s=0;null!=(i=e[s]);s++)try{t(i).triggerHandler("remove")}catch(o){}n(e)},t.widget=function(i,s,n){var o,a,r,h,l={},c=i.split(".")[0];i=i.split(".")[1],o=c+"-"+i,n||(n=s,s=t.Widget),t.expr[":"][o.toLowerCase()]=function(e){return!!t.data(e,o)},t[c]=t[c]||{},a=t[c][i],r=t[c][i]=function(t,i){return this._createWidget?(arguments.length&&this._createWidget(t,i),e):new r(t,i)},t.extend(r,a,{version:n.version,_proto:t.extend({},n),_childConstructors:[]}),h=new s,h.options=t.widget.extend({},h.options),t.each(n,function(i,n){return t.isFunction(n)?(l[i]=function(){var t=function(){return s.prototype[i].apply(this,arguments)},e=function(t){return s.prototype[i].apply(this,t)};return function(){var i,s=this._super,o=this._superApply;return this._super=t,this._superApply=e,i=n.apply(this,arguments),this._super=s,this._superApply=o,i}}(),e):(l[i]=n,e)}),r.prototype=t.widget.extend(h,{widgetEventPrefix:a?h.widgetEventPrefix||i:i},l,{constructor:r,namespace:c,widgetName:i,widgetFullName:o}),a?(t.each(a._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,r,i._proto)}),delete a._childConstructors):s._childConstructors.push(r),t.widget.bridge(i,r)},t.widget.extend=function(i){for(var n,o,a=s.call(arguments,1),r=0,h=a.length;h>r;r++)for(n in a[r])o=a[r][n],a[r].hasOwnProperty(n)&&o!==e&&(i[n]=t.isPlainObject(o)?t.isPlainObject(i[n])?t.widget.extend({},i[n],o):t.widget.extend({},o):o);return i},t.widget.bridge=function(i,n){var o=n.prototype.widgetFullName||i;t.fn[i]=function(a){var r="string"==typeof a,h=s.call(arguments,1),l=this;return a=!r&&h.length?t.widget.extend.apply(null,[a].concat(h)):a,r?this.each(function(){var s,n=t.data(this,o);return n?t.isFunction(n[a])&&"_"!==a.charAt(0)?(s=n[a].apply(n,h),s!==n&&s!==e?(l=s&&s.jquery?l.pushStack(s.get()):s,!1):e):t.error("no such method '"+a+"' for "+i+" widget instance"):t.error("cannot call methods on "+i+" prior to initialization; "+"attempted to call method '"+a+"'")}):this.each(function(){var e=t.data(this,o);e?e.option(a||{})._init():t.data(this,o,new n(a,this))}),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{disabled:!1,create:null},_createWidget:function(e,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this.bindings=t(),this.hoverable=t(),this.focusable=t(),s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:t.noop,_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(t.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:t.noop,widget:function(){return this.element},option:function(i,s){var n,o,a,r=i;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof i)if(r={},n=i.split("."),i=n.shift(),n.length){for(o=r[i]=t.widget.extend({},this.options[i]),a=0;n.length-1>a;a++)o[n[a]]=o[n[a]]||{},o=o[n[a]];if(i=n.pop(),1===arguments.length)return o[i]===e?null:o[i];o[i]=s}else{if(1===arguments.length)return this.options[i]===e?null:this.options[i];r[i]=s}return this._setOptions(r),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return this.options[t]=e,"disabled"===t&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!e).attr("aria-disabled",e),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(i,s,n){var o,a=this;"boolean"!=typeof i&&(n=s,s=i,i=!1),n?(s=o=t(s),this.bindings=this.bindings.add(s)):(n=s,s=this.element,o=this.widget()),t.each(n,function(n,r){function h(){return i||a.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof r?a[r]:r).apply(a,arguments):e}"string"!=typeof r&&(h.guid=r.guid=r.guid||h.guid||t.guid++);var l=n.match(/^(\w+)\s*(.*)$/),c=l[1]+a.eventNamespace,u=l[2];u?o.delegate(u,c,h):s.bind(c,h)})},_off:function(t,e){e=(e||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(e).undelegate(e)},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){t(e.currentTarget).addClass("ui-state-hover")},mouseleave:function(e){t(e.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){t(e.currentTarget).addClass("ui-state-focus")},focusout:function(e){t(e.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}})})(jQuery);(function(t){var e=!1;t(document).mouseup(function(){e=!1}),t.widget("ui.mouse",{version:"1.10.4",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.bind("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).bind("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):undefined}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&t(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(i){if(!e){this._mouseStarted&&this._mouseUp(i),this._mouseDownEvent=i;var s=this,n=1===i.which,a="string"==typeof this.options.cancel&&i.target.nodeName?t(i.target).closest(this.options.cancel).length:!1;return n&&!a&&this._mouseCapture(i)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){s.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(i)&&this._mouseDelayMet(i)&&(this._mouseStarted=this._mouseStart(i)!==!1,!this._mouseStarted)?(i.preventDefault(),!0):(!0===t.data(i.target,this.widgetName+".preventClickEvent")&&t.removeData(i.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return s._mouseMove(t)},this._mouseUpDelegate=function(t){return s._mouseUp(t)},t(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),i.preventDefault(),e=!0,!0)):!0}},_mouseMove:function(e){return t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button?this._mouseUp(e):this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){return t(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),!1},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})})(jQuery);(function(t){var e=5;t.widget("ui.slider",t.ui.mouse,{version:"1.10.4",widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"),this._refresh(),this._setOption("disabled",this.options.disabled),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var e,i,s=this.options,n=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),a="",o=[];for(i=s.values&&s.values.length||1,n.length>i&&(n.slice(i).remove(),n=n.slice(0,i)),e=n.length;i>e;e++)o.push(a);this.handles=n.add(t(o.join("")).appendTo(this.element)),this.handle=this.handles.eq(0),this.handles.each(function(e){t(this).data("ui-slider-handle-index",e)})},_createRange:function(){var e=this.options,i="";e.range?(e.range===!0&&(e.values?e.values.length&&2!==e.values.length?e.values=[e.values[0],e.values[0]]:t.isArray(e.values)&&(e.values=e.values.slice(0)):e.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?this.range.removeClass("ui-slider-range-min ui-slider-range-max").css({left:"",bottom:""}):(this.range=t("
").appendTo(this.element),i="ui-slider-range ui-widget-header ui-corner-all"),this.range.addClass(i+("min"===e.range||"max"===e.range?" ui-slider-range-"+e.range:""))):(this.range&&this.range.remove(),this.range=null)},_setupEvents:function(){var t=this.handles.add(this.range).filter("a");this._off(t),this._on(t,this._handleEvents),this._hoverable(t),this._focusable(t)},_destroy:function(){this.handles.remove(),this.range&&this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-widget ui-widget-content ui-corner-all"),this._mouseDestroy()},_mouseCapture:function(e){var i,s,n,a,o,r,l,h,u=this,c=this.options;return c.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),i={x:e.pageX,y:e.pageY},s=this._normValueFromMouse(i),n=this._valueMax()-this._valueMin()+1,this.handles.each(function(e){var i=Math.abs(s-u.values(e));(n>i||n===i&&(e===u._lastChangedValue||u.values(e)===c.min))&&(n=i,a=t(this),o=e)}),r=this._start(e,o),r===!1?!1:(this._mouseSliding=!0,this._handleIndex=o,a.addClass("ui-state-active").focus(),l=a.offset(),h=!t(e.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=h?{left:0,top:0}:{left:e.pageX-l.left-a.width()/2,top:e.pageY-l.top-a.height()/2-(parseInt(a.css("borderTopWidth"),10)||0)-(parseInt(a.css("borderBottomWidth"),10)||0)+(parseInt(a.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(e,o,s),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(t){var e={x:t.pageX,y:t.pageY},i=this._normValueFromMouse(e);return this._slide(t,this._handleIndex,i),!1},_mouseStop:function(t){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(t,this._handleIndex),this._change(t,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(t){var e,i,s,n,a;return"horizontal"===this.orientation?(e=this.elementSize.width,i=t.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(e=this.elementSize.height,i=t.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),s=i/e,s>1&&(s=1),0>s&&(s=0),"vertical"===this.orientation&&(s=1-s),n=this._valueMax()-this._valueMin(),a=this._valueMin()+s*n,this._trimAlignValue(a)},_start:function(t,e){var i={handle:this.handles[e],value:this.value()};return this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._trigger("start",t,i)},_slide:function(t,e,i){var s,n,a;this.options.values&&this.options.values.length?(s=this.values(e?0:1),2===this.options.values.length&&this.options.range===!0&&(0===e&&i>s||1===e&&s>i)&&(i=s),i!==this.values(e)&&(n=this.values(),n[e]=i,a=this._trigger("slide",t,{handle:this.handles[e],value:i,values:n}),s=this.values(e?0:1),a!==!1&&this.values(e,i))):i!==this.value()&&(a=this._trigger("slide",t,{handle:this.handles[e],value:i}),a!==!1&&this.value(i))},_stop:function(t,e){var i={handle:this.handles[e],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._trigger("stop",t,i)},_change:function(t,e){if(!this._keySliding&&!this._mouseSliding){var i={handle:this.handles[e],value:this.value()};this.options.values&&this.options.values.length&&(i.value=this.values(e),i.values=this.values()),this._lastChangedValue=e,this._trigger("change",t,i)}},value:function(t){return arguments.length?(this.options.value=this._trimAlignValue(t),this._refreshValue(),this._change(null,0),undefined):this._value()},values:function(e,i){var s,n,a;if(arguments.length>1)return this.options.values[e]=this._trimAlignValue(i),this._refreshValue(),this._change(null,e),undefined;if(!arguments.length)return this._values();if(!t.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(e):this.value();for(s=this.options.values,n=arguments[0],a=0;s.length>a;a+=1)s[a]=this._trimAlignValue(n[a]),this._change(null,a);this._refreshValue()},_setOption:function(e,i){var s,n=0;switch("range"===e&&this.options.range===!0&&("min"===i?(this.options.value=this._values(0),this.options.values=null):"max"===i&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),t.isArray(this.options.values)&&(n=this.options.values.length),t.Widget.prototype._setOption.apply(this,arguments),e){case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),s=0;n>s;s+=1)this._change(null,s);this._animateOff=!1;break;case"min":case"max":this._animateOff=!0,this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_value:function(){var t=this.options.value;return t=this._trimAlignValue(t)},_values:function(t){var e,i,s;if(arguments.length)return e=this.options.values[t],e=this._trimAlignValue(e);if(this.options.values&&this.options.values.length){for(i=this.options.values.slice(),s=0;i.length>s;s+=1)i[s]=this._trimAlignValue(i[s]);return i}return[]},_trimAlignValue:function(t){if(this._valueMin()>=t)return this._valueMin();if(t>=this._valueMax())return this._valueMax();var e=this.options.step>0?this.options.step:1,i=(t-this._valueMin())%e,s=t-i;return 2*Math.abs(i)>=e&&(s+=i>0?e:-e),parseFloat(s.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var e,i,s,n,a,o=this.options.range,r=this.options,l=this,h=this._animateOff?!1:r.animate,u={};this.options.values&&this.options.values.length?this.handles.each(function(s){i=100*((l.values(s)-l._valueMin())/(l._valueMax()-l._valueMin())),u["horizontal"===l.orientation?"left":"bottom"]=i+"%",t(this).stop(1,1)[h?"animate":"css"](u,r.animate),l.options.range===!0&&("horizontal"===l.orientation?(0===s&&l.range.stop(1,1)[h?"animate":"css"]({left:i+"%"},r.animate),1===s&&l.range[h?"animate":"css"]({width:i-e+"%"},{queue:!1,duration:r.animate})):(0===s&&l.range.stop(1,1)[h?"animate":"css"]({bottom:i+"%"},r.animate),1===s&&l.range[h?"animate":"css"]({height:i-e+"%"},{queue:!1,duration:r.animate}))),e=i}):(s=this.value(),n=this._valueMin(),a=this._valueMax(),i=a!==n?100*((s-n)/(a-n)):0,u["horizontal"===this.orientation?"left":"bottom"]=i+"%",this.handle.stop(1,1)[h?"animate":"css"](u,r.animate),"min"===o&&"horizontal"===this.orientation&&this.range.stop(1,1)[h?"animate":"css"]({width:i+"%"},r.animate),"max"===o&&"horizontal"===this.orientation&&this.range[h?"animate":"css"]({width:100-i+"%"},{queue:!1,duration:r.animate}),"min"===o&&"vertical"===this.orientation&&this.range.stop(1,1)[h?"animate":"css"]({height:i+"%"},r.animate),"max"===o&&"vertical"===this.orientation&&this.range[h?"animate":"css"]({height:100-i+"%"},{queue:!1,duration:r.animate}))},_handleEvents:{keydown:function(i){var s,n,a,o,r=t(i.target).data("ui-slider-handle-index");switch(i.keyCode){case t.ui.keyCode.HOME:case t.ui.keyCode.END:case t.ui.keyCode.PAGE_UP:case t.ui.keyCode.PAGE_DOWN:case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(i.preventDefault(),!this._keySliding&&(this._keySliding=!0,t(i.target).addClass("ui-state-active"),s=this._start(i,r),s===!1))return}switch(o=this.options.step,n=a=this.options.values&&this.options.values.length?this.values(r):this.value(),i.keyCode){case t.ui.keyCode.HOME:a=this._valueMin();break;case t.ui.keyCode.END:a=this._valueMax();break;case t.ui.keyCode.PAGE_UP:a=this._trimAlignValue(n+(this._valueMax()-this._valueMin())/e);break;case t.ui.keyCode.PAGE_DOWN:a=this._trimAlignValue(n-(this._valueMax()-this._valueMin())/e);break;case t.ui.keyCode.UP:case t.ui.keyCode.RIGHT:if(n===this._valueMax())return;a=this._trimAlignValue(n+o);break;case t.ui.keyCode.DOWN:case t.ui.keyCode.LEFT:if(n===this._valueMin())return;a=this._trimAlignValue(n-o)}this._slide(i,r,a)},click:function(t){t.preventDefault()},keyup:function(e){var i=t(e.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(e,i),this._change(e,i),t(e.target).removeClass("ui-state-active"))}}})})(jQuery); -------------------------------------------------------------------------------- /lib/stats.min.js: -------------------------------------------------------------------------------- 1 | // stats.js - http://github.com/mrdoob/stats.js 2 | var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; 3 | i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div"); 4 | k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display= 5 | "block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height= 6 | a+"px",m=b,r=0);return b},update:function(){l=this.end()}}}; 7 | -------------------------------------------------------------------------------- /vertex.glsl: -------------------------------------------------------------------------------- 1 | // -*- mode: c -*- 2 | 3 | varying vec2 merc; 4 | varying vec2 altUV; 5 | 6 | void main() { 7 | merc = uv; 8 | altUV = uv2; 9 | gl_Position = projectionMatrix * 10 | modelViewMatrix * 11 | vec4(position, 1.0); 12 | } 13 | --------------------------------------------------------------------------------