From 19fcee24f748bd3a85998dbc3239b18ed4713f15 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Wed, 8 Feb 2012 21:07:53 -0500 Subject: [PATCH] Split up overviewer.js into several files --- overviewer_core/assetmanager.py | 12 + overviewer_core/data/js_src/models.js | 47 + overviewer_core/data/js_src/overviewer.js | 93 ++ overviewer_core/data/js_src/util.js | 416 +++++ overviewer_core/data/js_src/views.js | 186 +++ overviewer_core/data/web_assets/overviewer.js | 1441 ----------------- 6 files changed, 754 insertions(+), 1441 deletions(-) create mode 100644 overviewer_core/data/js_src/models.js create mode 100644 overviewer_core/data/js_src/overviewer.js create mode 100644 overviewer_core/data/js_src/util.js create mode 100644 overviewer_core/data/js_src/views.js delete mode 100644 overviewer_core/data/web_assets/overviewer.js diff --git a/overviewer_core/assetmanager.py b/overviewer_core/assetmanager.py index 75c8142..7322b70 100644 --- a/overviewer_core/assetmanager.py +++ b/overviewer_core/assetmanager.py @@ -129,6 +129,18 @@ directory. if not os.path.isdir(global_assets): global_assets = os.path.join(util.get_program_path(), "web_assets") util.mirror_dir(global_assets, self.outputdir) + + # create overviewer.js from the source js files + js_src = os.path.join(util.get_program_path(), "overviewer_core", "data", "js_src") + with open(os.path.join(self.outputdir, "overviewer.js"), "w") as fout: + # first copy in js_src/overviewer.js + with open(os.path.join(js_src, "overviewer.js")) as f: + fout.write(f.read()) + # now copy in the rest + for js in os.listdir(js_src): + if not js.endswith("overviewer.js"): + with open(os.path.join(js_src,js)) as f: + fout.write(f.read()) # do the same with the local copy, if we have it # TODO diff --git a/overviewer_core/data/js_src/models.js b/overviewer_core/data/js_src/models.js new file mode 100644 index 0000000..545cfae --- /dev/null +++ b/overviewer_core/data/js_src/models.js @@ -0,0 +1,47 @@ +overviewer.models = {}; + +/* WorldModel + * Primarily has a collection of TileSets + */ +overviewer.models.WorldModel = Backbone.Model.extend({ + initialize: function(attrs) { + attrs.tileSets = new overviewer.models.TileSetCollection(); + this.set(attrs); + } +}); + + +/* WorldCollection + * A collection of WorldModels + */ +overviewer.models.WorldCollection = Backbone.Collection.extend({ + model: overviewer.models.WorldModel +}); + + +/* TileSetModel + */ +overviewer.models.TileSetModel = Backbone.Model.extend({ + defaults: { + markers: [] , + }, + initialize: function(attrs) { + // this implies that the Worlds collection must be + // initialized before any TIleSetModels are created + attrs.world = overviewer.collections.worlds.get(attrs.world); + this.set(attrs); + }, +}); + +overviewer.models.TileSetCollection = Backbone.Collection.extend({ + model: overviewer.models.TileSetModel +}); + + +overviewer.models.GoogleMapModel = Backbone.Model.extend({ + initialize: function(attrs) { + attrs.currentWorldView = overviewer.collections.worldViews[0]; + this.set(attrs); + }, +}); + diff --git a/overviewer_core/data/js_src/overviewer.js b/overviewer_core/data/js_src/overviewer.js new file mode 100644 index 0000000..2f9d651 --- /dev/null +++ b/overviewer_core/data/js_src/overviewer.js @@ -0,0 +1,93 @@ +/* Overviewer.js + * + * Must be the first file included from index.html + */ + + +var overviewer = {}; + + +/** + * This holds the map, probably the most important var in this file + */ +overviewer.map = null; +overviewer.mapView = null; + + +overviewer.collections = { + /** + * MapTypes that aren't overlays will end up in here. + */ + 'mapTypes': {}, + /** + * The mapType names are in here. + */ + 'mapTypeIds': [], + /** + * This is the current infoWindow object, we keep track of it so that + * there is only one open at a time. + */ + 'infoWindow': null, + + 'worldViews': [] + }; + +overviewer.classes = { + /** + * Our custom projection maps Latitude to Y, and Longitude to X as + * normal, but it maps the range [0.0, 1.0] to [0, tileSize] in both + * directions so it is easier to position markers, etc. based on their + * position (find their position in the lowest-zoom image, and divide + * by tileSize) + */ + 'MapProjection' : function() { + this.inverseTileSize = 1.0 / overviewerConfig.CONST.tileSize; + }, + /** + * This is a mapType used only for debugging, to draw a grid on the screen + * showing the tile co-ordinates and tile path. Currently the tile path + * part does not work. + * + * @param google.maps.Size tileSize + */ + 'CoordMapType': function(tileSize) { + this.tileSize = tileSize; + } + +}; + + +overviewer.gmap = { + + /** + * Generate a function to get the path to a tile at a particular location + * and zoom level. + * + * @param string path + * @param string pathBase + * @param string pathExt + */ + 'getTileUrlGenerator': function(path, pathBase, pathExt) { + return function(tile, zoom) { + var url = path; + var urlBase = ( pathBase ? pathBase : '' ); + if(tile.x < 0 || tile.x >= Math.pow(2, zoom) || + tile.y < 0 || tile.y >= Math.pow(2, zoom)) { + url += '/blank'; + } else if(zoom == 0) { + url += '/base'; + } else { + for(var z = zoom - 1; z >= 0; --z) { + var x = Math.floor(tile.x / Math.pow(2, z)) % 2; + var y = Math.floor(tile.y / Math.pow(2, z)) % 2; + url += '/' + (x + 2 * y); + } + } + url = url + '.' + pathExt; + if(typeof overviewerConfig.map.cacheTag !== 'undefined') { + url += '?c=' + overviewerConfig.map.cacheTag; + } + return(urlBase + url); + } + } +}; diff --git a/overviewer_core/data/js_src/util.js b/overviewer_core/data/js_src/util.js new file mode 100644 index 0000000..cefeed5 --- /dev/null +++ b/overviewer_core/data/js_src/util.js @@ -0,0 +1,416 @@ +overviewer.util = { + /** + * General initialization function, called when the page is loaded. + * Probably shouldn't need changing unless some very different kind of new + * feature gets added. + */ + 'initialize': function() { + overviewer.util.initializeClassPrototypes(); + + overviewer.collections.worlds = new overviewer.models.WorldCollection(); + + $.each(overviewerConfig.worlds, function(index, el) { + var n = new overviewer.models.WorldModel({name: el, id:el}); + overviewer.collections.worlds.add(n); + }); + + $.each(overviewerConfig.tilesets, function(index, el) { + var newTset = new overviewer.models.TileSetModel(el); + overviewer.collections.worlds.get(el.world).get("tileSets").add(newTset); + }); + + overviewer.collections.worlds.each(function(world, index, list) { + var nv = new overviewer.views.WorldView({model: world}); + overviewer.collections.worldViews.push(nv); + }); + + overviewer.mapModel = new overviewer.models.GoogleMapModel({}); + overviewer.mapView = new overviewer.views.GoogleMapView({el: document.getElementById(overviewerConfig.CONST.mapDivId), model:overviewer.mapModel}); + + // any controls must be created after the GoogleMapView is created + // controls should be added in the order they should appear on screen, + // with controls on the outside of the page being added first + + var compass = new overviewer.views.CompassView({tagName: 'DIV'}); + compass.render(); + + var coordsdiv = new overviewer.views.CoordboxView({tagName: 'DIV'}); + coordsdiv.render(); + // Update coords on mousemove + google.maps.event.addListener(overviewer.map, 'mousemove', function (event) { + coordsdiv.updateCoords(event.latLng); + }); + + var worldSelector = new overviewer.views.WorldSelectorView({tagName:'DIV'}); + overviewer.collections.worlds.bind("add", worldSelector.render, worldSelector); + + // hook up some events + + overviewer.mapModel.bind("change:currentWorldView", overviewer.mapView.render, overviewer.mapView); + + overviewer.mapView.render(); + + /* + overviewer.util.initializeMapTypes(); + overviewer.util.initializeMap(); + overviewer.util.initializeMarkers(); + overviewer.util.initializeRegions(); + overviewer.util.createMapControls(); + */ + }, + /** + * This adds some methods to these classes because Javascript is stupid + * and this seems like the best way to avoid re-creating the same methods + * on each object at object creation time. + */ + 'initializeClassPrototypes': function() { + overviewer.classes.MapProjection.prototype.fromLatLngToPoint = function(latLng) { + var x = latLng.lng() * overviewerConfig.CONST.tileSize; + var y = latLng.lat() * overviewerConfig.CONST.tileSize; + return new google.maps.Point(x, y); + }; + + overviewer.classes.MapProjection.prototype.fromPointToLatLng = function(point) { + var lng = point.x * this.inverseTileSize; + var lat = point.y * this.inverseTileSize; + return new google.maps.LatLng(lat, lng); + }; + + overviewer.classes.CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) { + var div = ownerDocument.createElement('DIV'); + div.innerHTML = '(' + coord.x + ', ' + coord.y + ', ' + zoom + + ')' + '
'; + //TODO: figure out how to get the current mapType, I think this + //will add the maptile url to the grid thing once it works + + //div.innerHTML += overviewer.collections.mapTypes[0].getTileUrl(coord, zoom); + + //this should probably just have a css class + div.style.width = this.tileSize.width + 'px'; + div.style.height = this.tileSize.height + 'px'; + div.style.fontSize = '10px'; + div.style.borderStyle = 'solid'; + div.style.borderWidth = '1px'; + div.style.borderColor = '#AAAAAA'; + return div; + }; + }, + /** + * Quote an arbitrary string for use in a regex matcher. + * WTB parametized regexes, JavaScript... + * + * From http://kevin.vanzonneveld.net + * original by: booeyOH + * improved by: Ates Goral (http://magnetiq.com) + * improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + * bugfixed by: Onno Marsman + * example 1: preg_quote("$40"); + * returns 1: '\$40' + * example 2: preg_quote("*RRRING* Hello?"); + * returns 2: '\*RRRING\* Hello\?' + * example 3: preg_quote("\\.+*?[^]$(){}=!<>|:"); + * returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:' + */ + "pregQuote": function(str) { + return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"); + }, + /** + * Change the map's div's background color according to the mapType's bg_color setting + * + * @param string mapTypeId + * @return string + */ + 'getMapTypeBackgroundColor': function(id) { + return overviewerConfig.tilesets[id].bgcolor; + }, + /** + * Gee, I wonder what this does. + * + * @param string msg + */ + 'debug': function(msg) { + if (overviewerConfig.map.debug) { + console.log(msg); + } + }, + /** + * Simple helper function to split the query string into key/value + * pairs. Doesn't do any type conversion but both are lowercase'd. + * + * @return Object + */ + 'parseQueryString': function() { + var results = {}; + var queryString = location.search.substring(1); + var pairs = queryString.split('&'); + for (i in pairs) { + var pos = pairs[i].indexOf('='); + var key = pairs[i].substring(0,pos).toLowerCase(); + var value = pairs[i].substring(pos+1).toLowerCase(); + overviewer.util.debug( 'Found GET paramter: ' + key + ' = ' + value); + results[key] = value; + } + return results; + }, + 'getDefaultMapTypeId': function() { + return overviewer.collections.mapTypeIds[0]; + }, + /** + * helper to get map LatLng from world coordinates takes arguments in + * X, Y, Z order (arguments are *out of order*, because within the + * function we use the axes like the rest of Minecraft Overviewer -- + * with the Z and Y flipped from normal minecraft usage.) + * + * @param int x + * @param int z + * @param int y + * + * @return google.maps.LatLng + */ + 'fromWorldToLatLng': function(x, z, y, zoomLevels) { + // the width and height of all the highest-zoom tiles combined, + // inverted + var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * + Math.pow(2, zoomLevels)); + + if(overviewerConfig.map.north_direction == 'upper-left'){ + temp = x; + x = -y-1; + y = temp; + } else if(overviewerConfig.map.north_direction == 'upper-right'){ + x = -x-1; + y = -y-1; + } else if(overviewerConfig.map.north_direction == 'lower-right'){ + temp = x; + x = y; + y = -temp-1; + } + + // This information about where the center column is may change with + // a different drawing implementation -- check it again after any + // drawing overhauls! + + // point (0, 0, 127) is at (0.5, 0.0) of tile (tiles/2 - 1, tiles/2) + // so the Y coordinate is at 0.5, and the X is at 0.5 - + // ((tileSize / 2) / (tileSize * 2^zoomLevels)) + // or equivalently, 0.5 - (1 / 2^(zoomLevels + 1)) + var lng = 0.5 - (1.0 / Math.pow(2, zoomLevels + 1)); + var lat = 0.5; + + // the following metrics mimic those in + // chunk_render in src/iterate.c + + // each block on X axis adds 12px to x and subtracts 6px from y + lng += 12 * x * perPixel; + lat -= 6 * x * perPixel; + + // each block on Y axis adds 12px to x and adds 6px to y + lng += 12 * y * perPixel; + lat += 6 * y * perPixel; + + // each block down along Z adds 12px to y + lat += 12 * (128 - z) * perPixel; + + // add on 12 px to the X coordinate to center our point + lng += 12 * perPixel; + + return new google.maps.LatLng(lat, lng); + }, + /** + * The opposite of fromWorldToLatLng + * NOTE: X, Y and Z in this function are Minecraft world definitions + * (that is, X is horizontal, Y is altitude and Z is vertical). + * + * @param float lat + * @param float lng + * + * @return Array + */ + 'fromLatLngToWorld': function(lat, lng, zoomLevels) { + // Initialize world x/y/z object to be returned + var point = Array(); + point.x = 0; + point.y = 64; + point.z = 0; + + // the width and height of all the highest-zoom tiles combined, + // inverted + var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * + Math.pow(2, zoomLevels)); + + // Revert base positioning + // See equivalent code in fromWorldToLatLng() + lng -= 0.5 - (1.0 / Math.pow(2, zoomLevels + 1)); + lat -= 0.5; + + // I'll admit, I plugged this into Wolfram Alpha: + // a = (x * 12 * r) + (z * 12 * r), b = (z * 6 * r) - (x * 6 * r) + // And I don't know the math behind solving for for X and Z given + // A (lng) and B (lat). But Wolfram Alpha did. :) I'd welcome + // suggestions for splitting this up into long form and documenting + // it. -RF + point.x = (lng - 2 * lat) / (24 * perPixel) + point.z = (lng + 2 * lat) / (24 * perPixel) + + // Adjust for the fact that we we can't figure out what Y is given + // only latitude and longitude, so assume Y=64. + point.x += 64; + point.z -= 64; + + if(overviewerConfig.map.north_direction == 'upper-left'){ + temp = point.z; + point.z = -point.x; + point.x = temp; + } else if(overviewerConfig.map.north_direction == 'upper-right'){ + point.x = -point.x; + point.z = -point.z; + } else if(overviewerConfig.map.north_direction == 'lower-right'){ + temp = point.z; + point.z = point.x; + point.x = -temp; + } + + return point; + }, + /** + * Create the pop-up infobox for when you click on a region, this can't + * be done in-line because of stupid Javascript scoping problems with + * closures or something. + * + * @param google.maps.Polygon|google.maps.Polyline shape + */ + 'createRegionInfoWindow': function(shape) { + var infowindow = new google.maps.InfoWindow(); + google.maps.event.addListener(shape, 'click', function(event, i) { + if (overviewer.collections.infoWindow) { + overviewer.collections.infoWindow.close(); + } + // Replace our Info Window's content and position + var point = overviewer.util.fromLatLngToWorld(event.latLng.lat(),event.latLng.lng()); + var contentString = 'Region: ' + shape.name + '
' + + 'Clicked Location:
' + Math.round(point.x,1) + ', ' + point.y + + ', ' + Math.round(point.z,1) + + '
'; + infowindow.setContent(contentString); + infowindow.setPosition(event.latLng); + infowindow.open(overviewer.map); + overviewer.collections.infoWindow = infowindow; + }); + }, + /** + * Same as createRegionInfoWindow() + * + * @param google.maps.Marker marker + */ + 'createMarkerInfoWindow': function(marker) { + var windowContent = '

' + marker.title.replace(/\n/g,'
') + '

'; + var infowindow = new google.maps.InfoWindow({ + 'content': windowContent + }); + google.maps.event.addListener(marker, 'click', function() { + if (overviewer.collections.infoWindow) { + overviewer.collections.infoWindow.close(); + } + infowindow.open(overviewer.map, marker); + overviewer.collections.infoWindow = infowindow; + }); + }, + 'initHash': function() { + if(window.location.hash.split("/").length > 1) { + overviewer.util.goToHash(); + // Clean up the hash. + overviewer.util.updateHash(); + + // Add a marker indicating the user-supplied position + var coordinates = overviewer.util.fromLatLngToWorld(overviewer.map.getCenter().lat(), + overviewer.map.getCenter().lng(), + overviewerConfig.tilesets[overviewer.currentTilesetId].zoomLevels); + overviewer.collections.markerDatas.push([{ + 'msg': 'Coordinates ' + Math.floor(coordinates.x) + ', ' + Math.floor(coordinates.y) + ', ' + Math.floor(coordinates.z), + 'x': coordinates.x, + 'y': coordinates.y, + 'z': coordinates.z, + 'type': 'querypos'}]); + } + }, + 'setHash': function(x, y, z, zoom, maptype) { + // remove the div prefix from the maptype (looks better) + if (maptype) + { + maptype = maptype.replace(overviewerConfig.CONST.mapDivId, ""); + } + window.location.replace("#/" + Math.floor(x) + "/" + Math.floor(y) + "/" + Math.floor(z) + "/" + zoom + "/" + maptype); + }, + 'updateHash': function() { + var coordinates = overviewer.util.fromLatLngToWorld(overviewer.map.getCenter().lat(), + overviewer.map.getCenter().lng(), + overviewerConfig.tilesets[overviewer.currentTilesetId].zoomLevels); + var zoom = overviewer.map.getZoom(); + var maptype = overviewer.map.getMapTypeId(); + if (zoom == overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom) { + zoom = 'max'; + } else if (zoom == overviewerConfig.tilesets[overviewer.currentTilesetId].minZoom) { + zoom = 'min'; + } else { + // default to (map-update friendly) negative zooms + zoom -= overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom; + } + overviewer.util.setHash(coordinates.x, coordinates.y, coordinates.z, zoom, maptype); + }, + 'goToHash': function() { + // Note: the actual data begins at coords[1], coords[0] is empty. + var coords = window.location.hash.split("/"); + var latlngcoords = overviewer.util.fromWorldToLatLng(parseInt(coords[1]), + parseInt(coords[2]), + parseInt(coords[3]), + overviewerConfig.tilesets[overviewer.currentTilesetId].zoomLevels); + var zoom; + var maptype = ''; + // The if-statements try to prevent unexpected behaviour when using incomplete hashes, e.g. older links + if (coords.length > 4) { + zoom = coords[4]; + } + if (coords.length > 5) { + maptype = coords[5]; + } + + if (zoom == 'max') { + zoom = overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom; + } else if (zoom == 'min') { + zoom = overviewerConfig.tilesets[overviewer.currentTilesetId].minZoom; + } else { + zoom = parseInt(zoom); + if (zoom < 0 && zoom + overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom >= 0) { + // if zoom is negative, treat it as a "zoom out from max" + zoom += overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom; + } else { + // fall back to default zoom + zoom = overviewerConfig.tilesets[overviewer.currentTilesetId].defaultZoom; + } + } + // If the maptype isn't set, set the default one. + if (maptype == '') { + // We can now set the map to use the 'coordinate' map type + overviewer.map.setMapTypeId(overviewer.util.getDefaultMapTypeId()); + } else { + // normalize the map type (this supports old-style, + // 'mcmapLabel' style map types, converts them to 'shortname' + if (maptype.lastIndexOf(overviewerConfig.CONST.mapDivId, 0) === 0) { + maptype = maptype.replace(overviewerConfig.CONST.mapDivId, ""); + for (i in overviewer.collections.mapTypes) { + var type = overviewer.collections.mapTypes[i]; + if (type.name == maptype) { + maptype = type.shortname; + break; + } + } + } + + overviewer.map.setMapTypeId(overviewerConfig.CONST.mapDivId + maptype); + } + + overviewer.map.setCenter(latlngcoords); + overviewer.map.setZoom(zoom); + } +}; diff --git a/overviewer_core/data/js_src/views.js b/overviewer_core/data/js_src/views.js new file mode 100644 index 0000000..903dbb5 --- /dev/null +++ b/overviewer_core/data/js_src/views.js @@ -0,0 +1,186 @@ +overviewer.views= {} + + +overviewer.views.WorldView = Backbone.View.extend({ + initialize: function(opts) { + console.log("WorldView::initialize()"); + console.log(this.model.get("tileSets")); + this.options.mapTypes = []; + this.options.mapTypeIds = []; + this.model.get("tileSets").each(function(tset, index, list) { + console.log(" eaching"); + console.log(" Working on tileset %s" , tset.get("name")); + var ops = { + getTileUrl: overviewer.gmap.getTileUrlGenerator(tset.get("path"), tset.get("base"), "png"), + 'tileSize': new google.maps.Size( + overviewerConfig.CONST.tileSize, + overviewerConfig.CONST.tileSize), + 'maxZoom': tset.get("maxZoom"), + 'minZoom': tset.get("minZoom"), + 'isPng': true + }; + var newMapType = new google.maps.ImageMapType(ops); + newMapType.name = tset.get("name"); + newMapType.shortname = tset.get("name"); + newMapType.alt = "Minecraft " + tset.get("name") + " Map"; + newMapType.projection = new overviewer.classes.MapProjection(); + + this.options.mapTypes.push(newMapType); + this.options.mapTypeIds.push(overviewerConfig.CONST.mapDivId + this.model.get("name") + tset.get("name")); + + }, this); + }, +}); + + + +overviewer.views.WorldSelectorView = Backbone.View.extend({ + initialize: function() { + // a div will have already been created for us, we just + // need to register it with the google maps control + var selectBox = document.createElement('select'); + $.each(overviewer.collections.worldViews, function(index, elem) { + var o = document.createElement("option"); + o.value = elem.model.get("name"); + o.innerHTML = elem.model.get("name"); + $(o).data("viewObj", elem); + selectBox.appendChild(o); + + }); + + this.el.appendChild(selectBox); + overviewer.map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.el); + }, + events: { + "change select": "changeWorld" + }, + changeWorld: function() { + console.log("change world!"); + var selectObj = this.$("select")[0]; + var selectedOption = selectObj.options[selectObj.selectedIndex]; + + overviewer.mapModel.set({currentWorldView: $(selectedOption).data("viewObj")}); + // + }, + render: function(t) { + console.log("WorldSelectorView::render() TODO implement this (low priority)"); + } +}); + + + +overviewer.views.CompassView = Backbone.View.extend({ + initialize: function() { + this.el.index=0; + var compassImg = document.createElement('IMG'); + compassImg.src = overviewerConfig.CONST.image.compass; + this.el.appendChild(compassImg); + + overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(this.el); + }, + render: function() { + } +}); + + +overviewer.views.CoordboxView = Backbone.View.extend({ + initialize: function() { + // Coords box + this.el.id = 'coordsDiv'; + this.el.innerHTML = 'coords here'; + overviewer.map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(this.el); + }, + updateCoords: function(latLng) { + var worldcoords = overviewer.util.fromLatLngToWorld(latLng.lat(), + latLng.lng(), + overviewer.mapModel.get("currentWorldView").model.get("tileSets").at(0).get("zoomLevels")); + this.el.innerHTML = "Coords: X " + Math.round(worldcoords.x) + ", Z " + Math.round(worldcoords.z); + } +}); + + + +/* GoogleMapView is responsible for dealing with the GoogleMaps API to create the + */ + +overviewer.views.GoogleMapView = Backbone.View.extend({ + initialize: function(opts) { + console.log(this); + this.options.map = null; + var curWorld = this.model.get("currentWorldView").model; + console.log("Current world:"); + console.log(curWorld); + + var curTset = curWorld.get("tileSets").at(0); + + /* + var defaultCenter = overviewer.util.fromWorldToLatLng( + overviewerConfig.map.center[0], + overviewerConfig.map.center[1], + overviewerConfig.map.center[2], + curTset.get("defaultZoom")); + */ + var lat = 0.62939453125;// TODO defaultCenter.lat(); + var lng = 0.38525390625; // TODO defaultCenter.lng(); + var mapcenter = new google.maps.LatLng(lat, lng); + + this.options.mapTypes=[]; + this.options.mapTypeIds=[]; + var opts = this.options; + + var mapOptions = {}; + // + curWorld.get("tileSets").each(function(elem, index, list) { + console.log("Setting up map for:"); + console.log(elem); + console.log("for %s generating url func with %s and %s", elem.get("name"), elem.get("path"), elem.get("base")); + + }); + // init the map with some default options. use the first tileset in the first world + this.options.mapOptions = { + zoom: curTset.get("defaultZoom"), + center: mapcenter, + panControl: true, + scaleControl: false, + mapTypeControl: true, + //mapTypeControlOptions: { + //mapTypeIds: this.options.mapTypeIds + //}, + mapTypeId: '', + streetViewControl: false, + overviewMapControl: true, + zoomControl: true, + backgroundColor: curTset.get("bgcolor") + }; + + + overviewer.map = new google.maps.Map(this.el, this.options.mapOptions); + + // register every ImageMapType with the map + $.each(overviewer.collections.worldViews, function( index, worldView) { + $.each(worldView.options.mapTypes, function(i_index, maptype) { + console.log("registered %s with the maptype registery", worldView.model.get("name") + maptype.shortname); + overviewer.map.mapTypes.set(overviewerConfig.CONST.mapDivId + + worldView.model.get("name") + maptype.shortname , maptype); + }); + }); + + }, + /* GoogleMapView::render() + * Should be called when the current world has changed in GoogleMapModel + */ + render: function() { + console.log("GoogleMapView::render()"); + var view = this.model.get("currentWorldView"); + this.options.mapOptions.mapTypeControlOptions = { + mapTypeIds: view.options.mapTypeIds}; + this.options.mapOptions.mapTypeId = view.options.mapTypeIds[0]; + overviewer.map.setOptions(this.options.mapOptions); + + + return this; + } + +}); + + diff --git a/overviewer_core/data/web_assets/overviewer.js b/overviewer_core/data/web_assets/overviewer.js deleted file mode 100644 index 03b50fc..0000000 --- a/overviewer_core/data/web_assets/overviewer.js +++ /dev/null @@ -1,1441 +0,0 @@ - -// Set up some Models and Views - -/* WorldModel - * Primarily has a collection of TileSets - */ -var WorldModel = Backbone.Model.extend({ -initialize: function(attrs) { -attrs.tileSets = new TileSetCollection(); -this.set(attrs); -} - }); -var WorldCollection = Backbone.Collection.extend({ -model: WorldModel -}); -var worlds = new WorldCollection(); - -/* WorldView - * Holds presentation info for a World, such as - * - ImageMapTypes - array of instances for each of the TileSets attached to this world - * - mapTypeIds - array of ids for each of the ImageMapTypes - */ -var WorldView = Backbone.View.extend({ -initialize: function(opts) { - console.log("WorldView::initialize()"); - console.log(this.model.get("tileSets")); - this.options.mapTypes = []; - this.options.mapTypeIds = []; - this.model.get("tileSets").each(function(tset, index, list) { - console.log(" eaching"); - console.log(" Working on tileset %s" , tset.get("name")); - var ops = { - getTileUrl: overviewer.gmap.getTileUrlGenerator(tset.get("path"), tset.get("base"), "png"), - - 'tileSize': new google.maps.Size( - overviewerConfig.CONST.tileSize, - overviewerConfig.CONST.tileSize), - 'maxZoom': tset.get("maxZoom"), - 'minZoom': tset.get("minZoom"), - 'isPng': true - - } - var newMapType = new google.maps.ImageMapType(ops); - newMapType.name = tset.get("name"); - newMapType.shortname = tset.get("name"); - newMapType.alt = "Minecraft " + tset.get("name") + " Map"; - newMapType.projection = new overviewer.classes.MapProjection(); - - this.options.mapTypes.push(newMapType); - this.options.mapTypeIds.push(overviewerConfig.CONST.mapDivId + this.model.get("name") + tset.get("name")); - - }, this); -}, - }); - - - -var TileSetModel = Backbone.Model.extend({ -defaults: { - markers: [] , -}, -initialize: function(attrs) { - // this implies that the Worlds collection must be - // initialized before any TIleSetModels are created - attrs.world = worlds.get(attrs.world); - - this.set(attrs); -}, -}); - -var TileSetCollection = Backbone.Collection.extend({ - model: TileSetModel -}); - - - -var GoogleMapModel = Backbone.Model.extend({ -initialize: function(attrs) { - attrs.currentWorldView = overviewer.collections.worldViews[0]; - this.set(attrs); -}, -}); - - -//////////////////////////////////////// -// Views -//////////////////////////////////////// - -var WorldSelectorView = Backbone.View.extend({ -initialize: function() { - // a div will have already been created for us, we just - // need to register it with the google maps control - var selectBox = document.createElement('select'); - $.each(overviewer.collections.worldViews, function(index, elem) { - var o = document.createElement("option"); - o.value = elem.model.get("name"); - o.innerHTML = elem.model.get("name"); - $(o).data("viewObj", elem); - selectBox.appendChild(o); - - }); - - this.el.appendChild(selectBox); - overviewer.map.controls[google.maps.ControlPosition.TOP_LEFT].push(this.el); -}, -events: { - "change select": "changeWorld" -}, -changeWorld: function() { - console.log("change world!"); - var selectObj = this.$("select")[0]; - var selectedOption = selectObj.options[selectObj.selectedIndex]; - - overviewer.mapModel.set({currentWorldView: $(selectedOption).data("viewObj")}); - // - }, -render: function(t) { - console.log("WorldSelectorView::render() TODO implement this (low priority)"); - -} -}); - -var CompassView = Backbone.View.extend({ -initialize: function() { - this.el.index=0; - var compassImg = document.createElement('IMG'); - compassImg.src = overviewerConfig.CONST.image.compass; - this.el.appendChild(compassImg); - - overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(this.el); -}, -render: function() { -} - }); - - -var CoordboxView = Backbone.View.extend({ -initialize: function() { - // Coords box -this.el.id = 'coordsDiv'; -this.el.innerHTML = 'coords here'; -overviewer.map.controls[google.maps.ControlPosition.BOTTOM_LEFT].push(this.el); -}, -updateCoords: function(latLng) { -var worldcoords = overviewer.util.fromLatLngToWorld(latLng.lat(), - latLng.lng(), -overviewer.mapModel.get("currentWorldView").model.get("tileSets").at(0).get("zoomLevels")); - this.el.innerHTML = "Coords: X " + Math.round(worldcoords.x) + ", Z " + Math.round(worldcoords.z); -} -} -); - -/* GoogleMapView is responsible for dealing with the GoogleMaps API to create the - */ -var GoogleMapView = Backbone.View.extend({ -initialize: function(opts) { - console.log(this); - this.options.map = null; - var curWorld = this.model.get("currentWorldView").model; - console.log("Current world:"); - console.log(curWorld); - - var curTset = curWorld.get("tileSets").at(0); - - /* - var defaultCenter = overviewer.util.fromWorldToLatLng( - overviewerConfig.map.center[0], - overviewerConfig.map.center[1], - overviewerConfig.map.center[2], - curTset.get("defaultZoom")); - */ - var lat = 0.62939453125;// TODO defaultCenter.lat(); - var lng = 0.38525390625; // TODO defaultCenter.lng(); - var mapcenter = new google.maps.LatLng(lat, lng); - - this.options.mapTypes=[]; - this.options.mapTypeIds=[]; - var opts = this.options; - - var mapOptions = {}; - // - curWorld.get("tileSets").each(function(elem, index, list) { - console.log("Setting up map for:"); - console.log(elem); - console.log("for %s generating url func with %s and %s", elem.get("name"), elem.get("path"), elem.get("base")); - - }); - // init the map with some default options. use the first tileset in the first world - this.options.mapOptions = { -zoom: curTset.get("defaultZoom"), -center: mapcenter, -panControl: true, -scaleControl: false, -mapTypeControl: true, -//mapTypeControlOptions: { - //mapTypeIds: this.options.mapTypeIds -//}, -mapTypeId: '', -streetViewControl: false, -overviewMapControl: true, -zoomControl: true, -backgroundColor: curTset.get("bgcolor") -}; - - - overviewer.map = new google.maps.Map(this.el, this.options.mapOptions); - - // register every ImageMapType with the map - $.each(overviewer.collections.worldViews, function( index, worldView) { - $.each(worldView.options.mapTypes, function(i_index, maptype) { - console.log("registered %s with the maptype registery", worldView.model.get("name") + maptype.shortname); - overviewer.map.mapTypes.set(overviewerConfig.CONST.mapDivId + - worldView.model.get("name") + maptype.shortname , maptype); - }); - }); - - -}, - /* GoogleMapView::render() - * Should be called when the current world has changed in GoogleMapModel - */ -render: function() { - console.log("GoogleMapView::render()"); - var view = this.model.get("currentWorldView"); - this.options.mapOptions.mapTypeControlOptions = { - mapTypeIds: view.options.mapTypeIds}; - this.options.mapOptions.mapTypeId = view.options.mapTypeIds[0]; - overviewer.map.setOptions(this.options.mapOptions); - - - return this; - } - -}); - - - -//////////////////////////////////////// -// Create stuff -// - - - - - -var overviewer = { - /** - * This holds the map, probably the most important var in this file - */ - 'map': null, - 'mapView': null, - /** - * The index of the currently displayed tileset. 0 (zero) is the default - */ - 'currentTilesetId': 0, - - /** - * These are collections of data used in various places - */ - 'collections': { - /** - * A list of lists of raw marker data objects, this will allow for an - * arbitrary number of marker data sources. This replaces the old - * markerData var from markers.js. Now you can add markers by including - * a file with: - * overviewer.collections.markerDatas.push([]); - */ - 'markerDatas': [], - /** - * The actual Marker objects are stored here. - */ - 'markers': {}, - /** - * Same as markerDatas, list of lists of raw region objects. - */ - 'regionDatas': [], - /** - * The actual Region objects. - */ - 'regions': {}, - /** - * Overlay mapTypes (like Spawn) will go in here. - */ - 'overlays': [], - /** - * MapTypes that aren't overlays will end up in here. - */ - 'mapTypes': {}, - /** - * The mapType names are in here. - */ - 'mapTypeIds': [], - /** - * This is the current infoWindow object, we keep track of it so that - * there is only one open at a time. - */ - 'infoWindow': null, - - 'worldViews': [] - }, - 'util': { - /** - * General initialization function, called when the page is loaded. - * Probably shouldn't need changing unless some very different kind of new - * feature gets added. - */ - 'initialize': function() { - overviewer.util.initializeClassPrototypes(); - - - $.each(overviewerConfig.worlds, function(index, el) { - var n = new WorldModel({name: el, id:el}); - worlds.add(n); - }); - - $.each(overviewerConfig.tilesets, function(index, el) { - var newTset = new TileSetModel(el); - worlds.get(el.world).get("tileSets").add(newTset); - }); - - worlds.each(function(world, index, list) { - var nv = new WorldView({model: world}); - overviewer.collections.worldViews.push(nv); - }); - - overviewer.mapModel = new GoogleMapModel({}); - overviewer.mapView = new GoogleMapView({el: document.getElementById(overviewerConfig.CONST.mapDivId), model:overviewer.mapModel}); - - // any controls must be created after the GoogleMapView is created - // controls should be added in the order they should appear on screen, - // with controls on the outside of the page being added first - - var compass = new CompassView({tagName: 'DIV'}); - compass.render(); - - var coordsdiv = new CoordboxView({tagName: 'DIV'}); - coordsdiv.render(); - // Update coords on mousemove - google.maps.event.addListener(overviewer.map, 'mousemove', function (event) { - coordsdiv.updateCoords(event.latLng); - }); - - var worldSelector = new WorldSelectorView({tagName:'DIV'}); - worlds.bind("add", worldSelector.render, worldSelector); - - // hook up some events - - overviewer.mapModel.bind("change:currentWorldView", overviewer.mapView.render, overviewer.mapView); - - overviewer.mapView.render(); - - /* - overviewer.util.initializeMapTypes(); - overviewer.util.initializeMap(); - overviewer.util.initializeMarkers(); - overviewer.util.initializeRegions(); - overviewer.util.createMapControls(); - */ - }, - /** - * This adds some methods to these classes because Javascript is stupid - * and this seems like the best way to avoid re-creating the same methods - * on each object at object creation time. - */ - 'initializeClassPrototypes': function() { - overviewer.classes.MapProjection.prototype.fromLatLngToPoint = function(latLng) { - var x = latLng.lng() * overviewerConfig.CONST.tileSize; - var y = latLng.lat() * overviewerConfig.CONST.tileSize; - return new google.maps.Point(x, y); - }; - - overviewer.classes.MapProjection.prototype.fromPointToLatLng = function(point) { - var lng = point.x * this.inverseTileSize; - var lat = point.y * this.inverseTileSize; - return new google.maps.LatLng(lat, lng); - }; - - overviewer.classes.CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) { - var div = ownerDocument.createElement('DIV'); - div.innerHTML = '(' + coord.x + ', ' + coord.y + ', ' + zoom + - ')' + '
'; - //TODO: figure out how to get the current mapType, I think this - //will add the maptile url to the grid thing once it works - - //div.innerHTML += overviewer.collections.mapTypes[0].getTileUrl(coord, zoom); - - //this should probably just have a css class - div.style.width = this.tileSize.width + 'px'; - div.style.height = this.tileSize.height + 'px'; - div.style.fontSize = '10px'; - div.style.borderStyle = 'solid'; - div.style.borderWidth = '1px'; - div.style.borderColor = '#AAAAAA'; - return div; - }; - }, - /** - * Quote an arbitrary string for use in a regex matcher. - * WTB parametized regexes, JavaScript... - * - * From http://kevin.vanzonneveld.net - * original by: booeyOH - * improved by: Ates Goral (http://magnetiq.com) - * improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - * bugfixed by: Onno Marsman - * example 1: preg_quote("$40"); - * returns 1: '\$40' - * example 2: preg_quote("*RRRING* Hello?"); - * returns 2: '\*RRRING\* Hello\?' - * example 3: preg_quote("\\.+*?[^]$(){}=!<>|:"); - * returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:' - */ - "pregQuote": function(str) { - return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"); - }, - /** - * Setup the varous mapTypes before we actually create the map. This used - * to be a bunch of crap down at the bottom of functions.js - */ - 'initializeMapTypes': function() { - var mapOptions = {}; - for (i in overviewerConfig.tilesets) { - var view = overviewerConfig.tilesets[i]; - overviewer.util.debug("initializeMapTypes: " + view.name); - var imageFormat = view.imgformat ? view.imgformat : 'png'; - - if (view.shortname == null) - view.shortname = view.name.replace(/\s+/g, ""); - - mapOptions[view.shortname] = { - 'getTileUrl': overviewer.gmap.getTileUrlGenerator(view.path, - view.base, imageFormat), - 'tileSize': new google.maps.Size( - overviewerConfig.CONST.tileSize, - overviewerConfig.CONST.tileSize), - 'maxZoom': view.maxZoom, - 'minZoom': view.minZoom, - 'isPng': imageFormat.toLowerCase() == 'png' - } - overviewer.collections.mapTypes[view.shortname] = new google.maps.ImageMapType( - mapOptions[view.shortname]); - overviewer.collections.mapTypes[view.shortname].name = view.name; - overviewer.collections.mapTypes[view.shortname].shortname = view.shortname; - overviewer.collections.mapTypes[view.shortname].alt = 'Minecraft ' + - view.name + ' Map'; - overviewer.collections.mapTypes[view.shortname].projection = - new overviewer.classes.MapProjection(); - if (view.overlay) { - overviewer.collections.overlays.push( - overviewer.collections.mapTypes[view.shortname]); - } else { - overviewer.collections.mapTypeIds.push( - overviewerConfig.CONST.mapDivId + view.shortname); - } - } - }, - /** - * This is where the magic happens. We setup the map with all it's - * options. The query string is also parsed here so we can know if - * we should be looking at a particular point on the map or just use - * the default view. - */ - 'initializeMap': function() { - // use the first map as the default one - - var defaultCenter = overviewer.util.fromWorldToLatLng( - overviewerConfig.map.center[0], - overviewerConfig.map.center[1], - overviewerConfig.map.center[2], - overviewerConfig.tilesets[overviewer.currentTilesetId].zoomLevels); - var lat = defaultCenter.lat(); - var lng = defaultCenter.lng(); - var zoom = overviewerConfig.tilesets[overviewer.currentTilesetId].defaultZoom; - var mapcenter; - var queryParams = overviewer.util.parseQueryString(); - if (queryParams.debug) { - overviewerConfig.map.debug=true; - } - if (queryParams.lat) { - lat = parseFloat(queryParams.lat); - } - if (queryParams.lng) { - lng = parseFloat(queryParams.lng); - } - if (queryParams.zoom) { - if (queryParams.zoom == 'max') { - zoom = overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom; - } else if (queryParams.zoom == 'min') { - zoom = overviewerConfig.tilesets[overviewer.currentTilesetId].minZoom; - } else { - zoom = parseInt(queryParams.zoom); - if (zoom < 0 && zoom + overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom >= 0) { - //if zoom is negative, try to treat as "zoom out from max zoom" - zoom += overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom; - } else { - //fall back to default zoom - zoom = overviewerConfig.tilesets[overviewer.currentTilesetId].defaultZoom; - } - } - } - if (queryParams.x && queryParams.y && queryParams.z) { - mapcenter = overviewer.util.fromWorldToLatLng(queryParams.x, - queryParams.y, - queryParams.z, - overviewerConfig.tilesets[overviewer.currentTilesetId].zoomLevels); - // Add a market indicating the user-supplied position - overviewer.collections.markerDatas.push([{ - 'msg': 'Coordinates ' + queryParams.x + ', ' + - queryParams.y + ', ' + queryParams.z, - 'y': parseFloat(queryParams.y), - 'x': parseFloat(queryParams.x), - 'z': parseFloat(queryParams.z), - 'type': 'querypos'}]); - } else { - mapcenter = new google.maps.LatLng(lat, lng); - } - var mapOptions = { - zoom: zoom, - center: mapcenter, - panControl: overviewerConfig.map.controls.pan, - scaleControl: false, - mapTypeControl: overviewerConfig.map.controls.mapType && - overviewer.collections.mapTypeIds.length > 1, - mapTypeControlOptions: { - mapTypeIds: overviewer.collections.mapTypeIds - }, - mapTypeId: overviewer.util.getDefaultMapTypeId(), - streetViewControl: false, - overviewMapControl: true, - zoomControl: overviewerConfig.map.controls.zoom, - backgroundColor: overviewer.util.getMapTypeBackgroundColor(overviewer.currentTilesetId) - }; - overviewer.map = new google.maps.Map(document.getElementById( - overviewerConfig.CONST.mapDivId), mapOptions); - - if (overviewerConfig.map.debug) { - overviewer.map.overlayMapTypes.insertAt(0, - new overviewer.classes.CoordMapType(new google.maps.Size( - overviewerConfig.CONST.tileSize, - overviewerConfig.CONST.tileSize))); - google.maps.event.addListener(overviewer.map, 'click', function(event) { - overviewer.util.debug('latLng: (' + event.latLng.lat() + - ', ' + event.latLng.lng() + ')'); - var pnt = overviewer.map.getProjection().fromLatLngToPoint(event.latLng); - overviewer.util.debug('point: ' + pnt); - var pxx = pnt.x * overviewerConfig.CONST.tileSize * - Math.pow(2, overviewerConfig.map.maxZoom); - var pxy = pnt.y * overviewerConfig.CONST.tileSize * - Math.pow(2, overviewerConfig.map.maxZoom); - overviewer.util.debug('pixel: (' + pxx + ', ' + pxy + ')'); - }); - } - - // Now attach the coordinate map type to the map's registry - for (i in overviewer.collections.mapTypes) { - overviewer.map.mapTypes.set(overviewerConfig.CONST.mapDivId + - overviewer.collections.mapTypes[i].shortname, - overviewer.collections.mapTypes[i]); - } - - // Jump to the hash if given - overviewer.util.initHash(); - - // Add live hash update listeners - // Note: It is important to add them after jumping to the hash - google.maps.event.addListener(overviewer.map, 'dragend', function() { - overviewer.util.updateHash(); - }); - - google.maps.event.addListener(overviewer.map, 'zoom_changed', function() { - overviewer.util.updateHash(); - }); - google.maps.event.addListener(overviewer.map, 'dblclick', function() { - overviewer.util.updateHash(); - }); - - // Make the link again whenever the map changes - /* TODO reimplement - google.maps.event.addListener(overviewer.map, 'maptypeid_changed', function() { - $('#'+overviewerConfig.CONST.mapDivId).css( - 'background-color', overviewer.util.getMapTypeBackgroundColor( - overviewer.map.getMapTypeId())); - //smuggled this one in here for maptypeid hash generation --CounterPillow - overviewer.util.updateHash(); - }); - */ - }, - /** - * Read through overviewer.collections.markerDatas and create Marker - * objects and stick them in overviewer.collections.markers . This - * should probably be done differently at some point so that we can - * support markers that change position more easily. - */ - 'initializeMarkers': function() { - //first, give all collections an empty array to work with - /* TODO reimplement - for (i in overviewerConfig.objectGroups.signs) { - overviewer.util.debug('Found sign group: ' + - overviewerConfig.objectGroups.signs[i].label); - overviewer.collections.markers[ - overviewerConfig.objectGroups.signs[i].label] = []; - } - */ - for (i in overviewer.collections.markerDatas) { - var markerData = overviewer.collections.markerDatas[i]; - for (j in markerData) { - var item = markerData[j]; - // a default: - var iconURL = ''; - if (item.type == 'spawn') { - // don't filter spawn, always display - var marker = new google.maps.Marker({ - 'position': overviewer.util.fromWorldToLatLng(item.x, - item.y, item.z), - 'map': overviewer.map, - 'title': jQuery.trim(item.msg), - 'icon': overviewerConfig.CONST.image.spawnMarker - }); - continue; - } - - if (item.type == 'querypos') { - // Set on page load if MC x/y/z coords are given in the - // query string - var marker = new google.maps.Marker({ - 'position': overviewer.util.fromWorldToLatLng(item.x, - item.y, item.z), - 'map': overviewer.map, - 'title': jQuery.trim(item.msg), - 'icon': overviewerConfig.CONST.image.queryMarker - }); - google.maps.event.addListener(marker, 'click', function(){ marker.setVisible(false); }); - - - continue; - } - - var matched = false; - for (j in overviewerConfig.objectGroups.signs) { - var signGroup = overviewerConfig.objectGroups.signs[j]; - var label = signGroup.label; - if (signGroup.match(item)) { - matched = true; - // can add custom types of images for externally defined - // item types, like 'command' here. - if (item.type == 'sign') { - iconURL = overviewerConfig.CONST.image.signMarker; - } - overviewer.util.debug('Sign icon: ' + signGroup.icon); - if (signGroup.icon) { - iconURL = signGroup.icon; - } - var marker = new google.maps.Marker({ - 'position': overviewer.util.fromWorldToLatLng(item.x, - item.y, item.z), - 'map': overviewer.map, - 'title': jQuery.trim(item.msg), - 'icon': iconURL, - 'visible': false - }); - item.marker = marker; - overviewer.util.debug(label); - overviewer.collections.markers[label].push(marker); - if (item.type == 'sign') { - overviewer.util.createMarkerInfoWindow(marker); - } - } - } - - if (!matched) { - // is this signpost doesn't match any of the groups in - // config.js, add it automatically to the "__others__" group - if (item.type == 'sign') { - iconURL = overviewerConfig.CONST.image.signMarker; - } - var marker = new google.maps.Marker({ - 'position': overviewer.util.fromWorldToLatLng(item.x, - item.y, item.z), - 'map': overviewer.map, - 'title': jQuery.trim(item.msg), - 'icon': iconURL, - 'visible': false - }); - item.marker = marker; - if (overviewer.collections.markers['__others__']) { - overviewer.collections.markers['__others__'].push(marker); - } else { - overviewer.collections.markers['__others__'] = [marker]; - } - if (item.type == 'sign') { - overviewer.util.createMarkerInfoWindow(marker, item); - } - } - } - } - }, - /** - * Same as initializeMarkers() for the most part. - */ - 'initializeRegions': function() { - /* TODO reimplement - for (i in overviewerConfig.objectGroups.regions) { - overviewer.collections.regions[overviewerConfig.objectGroups.regions[i].label] = []; - } - */ - for (i in overviewer.collections.regionDatas) { - var regionData = overviewer.collections.regionDatas[i]; - for (j in regionData) { - var region = regionData[j]; - // pull all the points out of the regions file. - var converted = new google.maps.MVCArray(); - for (k in region.path) { - var point = region.path[k]; - converted.push(overviewer.util.fromWorldToLatLng( - point.x, point.y, point.z)); - - } - - if (region.label) { - var name = region.label; - } else { - var name = "rawr"; - } - - if(region.opacity) { - var strokeOpacity = region.opacity; - var fillOpacity = region.opacity * 0.25; - } else { - var strokeOpacity = region.strokeOpacity; - var fillOpacity = region.fillOpacity; - } - - var shapeOptions = { - 'name': name, - 'geodesic': false, - 'map': null, - 'strokeColor': region.color, - 'strokeOpacity': strokeOpacity, - 'strokeWeight': overviewerConfig.CONST.regionStrokeWeight, - 'zIndex': j - }; - if (region.closed) { - shapeOptions["fillColor"] = region.color; - shapeOptions["fillOpacity"] = fillOpacity; - shapeOptions["paths"] = converted; - } else { - shapeOptions["path"] = converted; - } - - var matched = false; - - for (k in overviewerConfig.objectGroups.regions) { - var regionGroup = overviewerConfig.objectGroups.regions[k]; - var clickable = regionGroup.clickable; - var label = regionGroup.label; - - if (!regionGroup.match(region)) - continue; - matched = true; - - if (!region.label) { - clickable = false; // if it doesn't have a name, we dont have to show it. - } - - if (region.closed) { - var shape = new google.maps.Polygon(shapeOptions); - } else { - var shape = new google.maps.Polyline(shapeOptions); - } - - overviewer.collections.regions[label].push(shape); - - if (clickable) { - overviewer.util.createRegionInfoWindow(shape); - } - } - - // if we haven't matched anything, go ahead and add it - if (!matched) { - if (region.closed) { - var shape = new google.maps.Polygon(shapeOptions); - } else { - var shape = new google.maps.Polyline(shapeOptions); - } - - shape.setMap(overviewer.map); - } - } - } - }, - /** - * Change the map's div's background color according to the mapType's bg_color setting - * - * @param string mapTypeId - * @return string - */ - 'getMapTypeBackgroundColor': function(id) { - return overviewerConfig.tilesets[id].bgcolor; - }, - /** - * Gee, I wonder what this does. - * - * @param string msg - */ - 'debug': function(msg) { - if (overviewerConfig.map.debug) { - console.log(msg); - } - }, - /** - * Simple helper function to split the query string into key/value - * pairs. Doesn't do any type conversion but both are lowercase'd. - * - * @return Object - */ - 'parseQueryString': function() { - var results = {}; - var queryString = location.search.substring(1); - var pairs = queryString.split('&'); - for (i in pairs) { - var pos = pairs[i].indexOf('='); - var key = pairs[i].substring(0,pos).toLowerCase(); - var value = pairs[i].substring(pos+1).toLowerCase(); - overviewer.util.debug( 'Found GET paramter: ' + key + ' = ' + value); - results[key] = value; - } - return results; - }, - 'getDefaultMapTypeId': function() { - return overviewer.collections.mapTypeIds[0]; - }, - /** - * helper to get map LatLng from world coordinates takes arguments in - * X, Y, Z order (arguments are *out of order*, because within the - * function we use the axes like the rest of Minecraft Overviewer -- - * with the Z and Y flipped from normal minecraft usage.) - * - * @param int x - * @param int z - * @param int y - * - * @return google.maps.LatLng - */ - 'fromWorldToLatLng': function(x, z, y, zoomLevels) { - // the width and height of all the highest-zoom tiles combined, - // inverted - var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * - Math.pow(2, zoomLevels)); - - if(overviewerConfig.map.north_direction == 'upper-left'){ - temp = x; - x = -y-1; - y = temp; - } else if(overviewerConfig.map.north_direction == 'upper-right'){ - x = -x-1; - y = -y-1; - } else if(overviewerConfig.map.north_direction == 'lower-right'){ - temp = x; - x = y; - y = -temp-1; - } - - // This information about where the center column is may change with - // a different drawing implementation -- check it again after any - // drawing overhauls! - - // point (0, 0, 127) is at (0.5, 0.0) of tile (tiles/2 - 1, tiles/2) - // so the Y coordinate is at 0.5, and the X is at 0.5 - - // ((tileSize / 2) / (tileSize * 2^zoomLevels)) - // or equivalently, 0.5 - (1 / 2^(zoomLevels + 1)) - var lng = 0.5 - (1.0 / Math.pow(2, zoomLevels + 1)); - var lat = 0.5; - - // the following metrics mimic those in - // chunk_render in src/iterate.c - - // each block on X axis adds 12px to x and subtracts 6px from y - lng += 12 * x * perPixel; - lat -= 6 * x * perPixel; - - // each block on Y axis adds 12px to x and adds 6px to y - lng += 12 * y * perPixel; - lat += 6 * y * perPixel; - - // each block down along Z adds 12px to y - lat += 12 * (128 - z) * perPixel; - - // add on 12 px to the X coordinate to center our point - lng += 12 * perPixel; - - return new google.maps.LatLng(lat, lng); - }, - /** - * The opposite of fromWorldToLatLng - * NOTE: X, Y and Z in this function are Minecraft world definitions - * (that is, X is horizontal, Y is altitude and Z is vertical). - * - * @param float lat - * @param float lng - * - * @return Array - */ - 'fromLatLngToWorld': function(lat, lng, zoomLevels) { - // Initialize world x/y/z object to be returned - var point = Array(); - point.x = 0; - point.y = 64; - point.z = 0; - - // the width and height of all the highest-zoom tiles combined, - // inverted - var perPixel = 1.0 / (overviewerConfig.CONST.tileSize * - Math.pow(2, zoomLevels)); - - // Revert base positioning - // See equivalent code in fromWorldToLatLng() - lng -= 0.5 - (1.0 / Math.pow(2, zoomLevels + 1)); - lat -= 0.5; - - // I'll admit, I plugged this into Wolfram Alpha: - // a = (x * 12 * r) + (z * 12 * r), b = (z * 6 * r) - (x * 6 * r) - // And I don't know the math behind solving for for X and Z given - // A (lng) and B (lat). But Wolfram Alpha did. :) I'd welcome - // suggestions for splitting this up into long form and documenting - // it. -RF - point.x = (lng - 2 * lat) / (24 * perPixel) - point.z = (lng + 2 * lat) / (24 * perPixel) - - // Adjust for the fact that we we can't figure out what Y is given - // only latitude and longitude, so assume Y=64. - point.x += 64; - point.z -= 64; - - if(overviewerConfig.map.north_direction == 'upper-left'){ - temp = point.z; - point.z = -point.x; - point.x = temp; - } else if(overviewerConfig.map.north_direction == 'upper-right'){ - point.x = -point.x; - point.z = -point.z; - } else if(overviewerConfig.map.north_direction == 'lower-right'){ - temp = point.z; - point.z = point.x; - point.x = -temp; - } - - return point; - }, - /** - * Create and draw the various map controls and other related things - * like the compass, current view link, etc. - */ - 'createMapControls': function() { - // Spawn button - var homeControlDiv = document.createElement('DIV'); - var homeControl = new overviewer.classes.HomeControl(homeControlDiv); - $(homeControlDiv).addClass('customControl'); - homeControlDiv.index = 1; - if (overviewerConfig.map.controls.spawn) { - overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(homeControlDiv); - } - - - - // only need to create the control if there are items in the list. - // as defined in config.js - /* TODO reimplement - if (overviewerConfig.objectGroups.signs.length > 0) { - // signpost display control - var items = []; - for (i in overviewerConfig.objectGroups.signs) { - var signGroup = overviewerConfig.objectGroups.signs[i]; - // don't create an option for this group if empty - if (overviewer.collections.markers[signGroup.label].length == 0) { - continue; - } - - var iconURL = signGroup.icon; - if(!iconURL) { - iconURL = overviewerConfig.CONST.image.defaultMarker; - } - items.push({ - 'label': signGroup.label, - 'checked': signGroup.checked, - 'icon': iconURL, - 'action': function(n, item, checked) { - jQuery.each(overviewer.collections.markers[item.label], - function(i, elem) { - elem.setVisible(checked); - } - ); - overviewer.util.debug('Adding sign item: ' + item); - } - }); - } - - // only create drop down if there's used options - if (items.length > 0) { - overviewer.util.createDropDown('Markers', items); - } - } - */ - - // if there are any regions data, lets show the option to hide/show them. - /* TODO reimplement - if (overviewerConfig.objectGroups.regions.length > 0) { - // region display control - var items = []; - for (i in overviewerConfig.objectGroups.regions) { - var regionGroup = overviewerConfig.objectGroups.regions[i]; - items.push({ - 'label': regionGroup.label, - 'checked': regionGroup.checked, - 'action': function(n, item, checked) { - jQuery.each(overviewer.collections.regions[item.label], - function(i,elem) { - // Thanks to LeastWeasel for this line! - elem.setMap(checked ? overviewer.map : null); - }); - overviewer.util.debug('Adding region item: ' + item); - - } - }); - } - overviewer.util.createDropDown('Regions', items); - } - */ - - if (overviewerConfig.map.controls.overlays && overviewer.collections.overlays.length > 0) { - // overlay maps control - var items = []; - for (i in overviewer.collections.overlays) { - var overlay = overviewer.collections.overlays[i]; - items.push({ - 'label': overlay.name, - 'checked': false, - 'overlay': overlay, - 'action': function(i, item, checked) { - if (checked) { - overviewer.map.overlayMapTypes.push(item.overlay); - } else { - var idx_to_delete = -1; - overviewer.map.overlayMapTypes.forEach(function(e, j) { - if (e == item.overlay) { - idx_to_delete = j; - } - }); - if (idx_to_delete >= 0) { - overviewer.map.overlayMapTypes.removeAt(idx_to_delete); - } - } - } - }); - } - overviewer.util.createDropDown('Overlays', items); - } - - // call out to create search box, as it's pretty complicated - // TODO overviewer.util.createSearchBox(); - }, - /** - * Reusable method for creating drop-down menus - * - * @param string title - * @param array items - */ - 'createDropDown': function(title, items) { - var control = document.createElement('DIV'); - // let's let a style sheet do most of the styling here - $(control).addClass('customControl'); - - var controlText = document.createElement('DIV'); - controlText.innerHTML = title; - - var controlBorder = document.createElement('DIV'); - $(controlBorder).addClass('top'); - control.appendChild(controlBorder); - controlBorder.appendChild(controlText); - - var dropdownDiv = document.createElement('DIV'); - $(dropdownDiv).addClass('dropDown'); - control.appendChild(dropdownDiv); - dropdownDiv.innerHTML=''; - - // add the functionality to toggle visibility of the items - $(controlText).click(function() { - $(controlBorder).toggleClass('top-active'); - $(dropdownDiv).toggle(); - }); - - // add that control box we've made back to the map. - overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(control); - - for(i in items) { - // create the visible elements of the item - var item = items[i]; - overviewer.util.debug(item); - var itemDiv = document.createElement('div'); - var itemInput = document.createElement('input'); - itemInput.type='checkbox'; - - // give it a name - $(itemInput).data('label',item.label); - jQuery(itemInput).click((function(local_idx, local_item) { - return function(e) { - item.action(local_idx, local_item, e.target.checked); - }; - })(i, item)); - - // if its checked, its gotta do something, do that here. - if (item.checked) { - itemInput.checked = true; - item.action(i, item, item.checked); - } - dropdownDiv.appendChild(itemDiv); - itemDiv.appendChild(itemInput); - var textNode = document.createElement('text'); - if(item.icon) { - textNode.innerHTML = '' + item.label + '
'; - } else { - textNode.innerHTML = item.label + '
'; - } - - itemDiv.appendChild(textNode); - } - }, - /** - * Create search box and dropdown in the top right google maps area. - */ - 'createSearchBox': function() { - var searchControl = document.createElement("div"); - searchControl.id = "searchControl"; - - var searchInput = document.createElement("input"); - searchInput.type = "text"; - searchInput.value = "Sign Search"; - searchInput.title = "Sign Search"; - $(searchInput).addClass("inactive"); - - /* Hey dawg, I heard you like functions. - * So we defined a function inside your function. - */ - searchInput.onfocus = function() { - if (searchInput.value == "Sign Search") { - searchInput.value = ""; - $(searchInput).removeClass("inactive").addClass("active"); - } - }; - searchInput.onblur = function() { - if (searchInput.value == "") { - searchInput.value = "Sign Search"; - $(searchInput).removeClass("active").addClass("inactive"); - } - }; - - searchControl.appendChild(searchInput); - - var searchDropDown = document.createElement("div"); - searchDropDown.id = "searchDropDown"; - searchControl.appendChild(searchDropDown); - - $(searchInput).keyup(function(e) { - var newline_stripper = new RegExp("[\\r\\n]", "g") - if(searchInput.value.length !== 0) { - $(searchDropDown).fadeIn(); - - $(searchDropDown).empty(); - - overviewer.collections.markerDatas.forEach(function(marker_list) { - marker_list.forEach(function(sign) { - var regex = new RegExp(overviewer.util.pregQuote(searchInput.value), "mi"); - if(sign.msg.match(regex)) { - if(sign.marker !== undefined && sign.marker.getVisible()) { - var t = document.createElement("div"); - t.className = "searchResultItem"; - var i = document.createElement("img"); - i.src = sign.marker.getIcon(); - t.appendChild(i); - var s = document.createElement("span"); - - $(s).text(sign.msg.replace(newline_stripper, "")); - t.appendChild(s); - searchDropDown.appendChild(t); - $(t).click(function(e) { - $(searchDropDown).fadeOut(); - overviewer.map.setZoom(7); - overviewer.map.setCenter(sign.marker.getPosition()); - }); - - } - } - }); - }); - } else { - $(searchDropDown).fadeOut(); - } - }); - - if (overviewerConfig.map.controls.searchBox) { - overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(searchControl); - } - }, - /** - * Create the pop-up infobox for when you click on a region, this can't - * be done in-line because of stupid Javascript scoping problems with - * closures or something. - * - * @param google.maps.Polygon|google.maps.Polyline shape - */ - 'createRegionInfoWindow': function(shape) { - var infowindow = new google.maps.InfoWindow(); - google.maps.event.addListener(shape, 'click', function(event, i) { - if (overviewer.collections.infoWindow) { - overviewer.collections.infoWindow.close(); - } - // Replace our Info Window's content and position - var point = overviewer.util.fromLatLngToWorld(event.latLng.lat(),event.latLng.lng()); - var contentString = 'Region: ' + shape.name + '
' + - 'Clicked Location:
' + Math.round(point.x,1) + ', ' + point.y - + ', ' + Math.round(point.z,1) - + '
'; - infowindow.setContent(contentString); - infowindow.setPosition(event.latLng); - infowindow.open(overviewer.map); - overviewer.collections.infoWindow = infowindow; - }); - }, - /** - * Same as createRegionInfoWindow() - * - * @param google.maps.Marker marker - */ - 'createMarkerInfoWindow': function(marker) { - var windowContent = '

' + marker.title.replace(/\n/g,'
') + '

'; - var infowindow = new google.maps.InfoWindow({ - 'content': windowContent - }); - google.maps.event.addListener(marker, 'click', function() { - if (overviewer.collections.infoWindow) { - overviewer.collections.infoWindow.close(); - } - infowindow.open(overviewer.map, marker); - overviewer.collections.infoWindow = infowindow; - }); - }, - 'initHash': function() { - if(window.location.hash.split("/").length > 1) { - overviewer.util.goToHash(); - // Clean up the hash. - overviewer.util.updateHash(); - - // Add a marker indicating the user-supplied position - var coordinates = overviewer.util.fromLatLngToWorld(overviewer.map.getCenter().lat(), - overviewer.map.getCenter().lng(), - overviewerConfig.tilesets[overviewer.currentTilesetId].zoomLevels); - overviewer.collections.markerDatas.push([{ - 'msg': 'Coordinates ' + Math.floor(coordinates.x) + ', ' + Math.floor(coordinates.y) + ', ' + Math.floor(coordinates.z), - 'x': coordinates.x, - 'y': coordinates.y, - 'z': coordinates.z, - 'type': 'querypos'}]); - } - }, - 'setHash': function(x, y, z, zoom, maptype) { - // remove the div prefix from the maptype (looks better) - if (maptype) - { - maptype = maptype.replace(overviewerConfig.CONST.mapDivId, ""); - } - window.location.replace("#/" + Math.floor(x) + "/" + Math.floor(y) + "/" + Math.floor(z) + "/" + zoom + "/" + maptype); - }, - 'updateHash': function() { - var coordinates = overviewer.util.fromLatLngToWorld(overviewer.map.getCenter().lat(), - overviewer.map.getCenter().lng(), - overviewerConfig.tilesets[overviewer.currentTilesetId].zoomLevels); - var zoom = overviewer.map.getZoom(); - var maptype = overviewer.map.getMapTypeId(); - if (zoom == overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom) { - zoom = 'max'; - } else if (zoom == overviewerConfig.tilesets[overviewer.currentTilesetId].minZoom) { - zoom = 'min'; - } else { - // default to (map-update friendly) negative zooms - zoom -= overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom; - } - overviewer.util.setHash(coordinates.x, coordinates.y, coordinates.z, zoom, maptype); - }, - 'goToHash': function() { - // Note: the actual data begins at coords[1], coords[0] is empty. - var coords = window.location.hash.split("/"); - var latlngcoords = overviewer.util.fromWorldToLatLng(parseInt(coords[1]), - parseInt(coords[2]), - parseInt(coords[3]), - overviewerConfig.tilesets[overviewer.currentTilesetId].zoomLevels); - var zoom; - var maptype = ''; - // The if-statements try to prevent unexpected behaviour when using incomplete hashes, e.g. older links - if (coords.length > 4) { - zoom = coords[4]; - } - if (coords.length > 5) { - maptype = coords[5]; - } - - if (zoom == 'max') { - zoom = overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom; - } else if (zoom == 'min') { - zoom = overviewerConfig.tilesets[overviewer.currentTilesetId].minZoom; - } else { - zoom = parseInt(zoom); - if (zoom < 0 && zoom + overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom >= 0) { - // if zoom is negative, treat it as a "zoom out from max" - zoom += overviewerConfig.tilesets[overviewer.currentTilesetId].maxZoom; - } else { - // fall back to default zoom - zoom = overviewerConfig.tilesets[overviewer.currentTilesetId].defaultZoom; - } - } - // If the maptype isn't set, set the default one. - if (maptype == '') { - // We can now set the map to use the 'coordinate' map type - overviewer.map.setMapTypeId(overviewer.util.getDefaultMapTypeId()); - } else { - // normalize the map type (this supports old-style, - // 'mcmapLabel' style map types, converts them to 'shortname' - if (maptype.lastIndexOf(overviewerConfig.CONST.mapDivId, 0) === 0) { - maptype = maptype.replace(overviewerConfig.CONST.mapDivId, ""); - for (i in overviewer.collections.mapTypes) { - var type = overviewer.collections.mapTypes[i]; - if (type.name == maptype) { - maptype = type.shortname; - break; - } - } - } - - overviewer.map.setMapTypeId(overviewerConfig.CONST.mapDivId + maptype); - } - - overviewer.map.setCenter(latlngcoords); - overviewer.map.setZoom(zoom); - } - }, - /** - * The various classes needed in this file. - */ - 'classes': { - /** - * This is the button that centers the map on spawn. Not sure why we - * need a separate class for this and not some of the other controls. - * - * @param documentElement controlDiv - */ - 'HomeControl': function(controlDiv) { - controlDiv.style.padding = '5px'; - // Set CSS for the control border - var control = document.createElement('DIV'); - $(control).addClass('top'); - control.title = 'Click to center the map on the Spawn'; - controlDiv.appendChild(control); - - // Set CSS for the control interior - var controlText = document.createElement('DIV'); - controlText.innerHTML = 'Spawn'; - $(controlText).addClass('button'); - control.appendChild(controlText); - - // Setup the click event listeners: simply set the map to map center - // as definned below - google.maps.event.addDomListener(control, 'click', function() { - overviewer.map.panTo(overviewer.util.fromWorldToLatLng( - overviewerConfig.map.center[0], - overviewerConfig.map.center[1], - overviewerConfig.map.center[2])); - overviewer.util.updateHash(); - }); - }, - /** - * Our custom projection maps Latitude to Y, and Longitude to X as - * normal, but it maps the range [0.0, 1.0] to [0, tileSize] in both - * directions so it is easier to position markers, etc. based on their - * position (find their position in the lowest-zoom image, and divide - * by tileSize) - */ - 'MapProjection' : function() { - this.inverseTileSize = 1.0 / overviewerConfig.CONST.tileSize; - }, - /** - * This is a mapType used only for debugging, to draw a grid on the screen - * showing the tile co-ordinates and tile path. Currently the tile path - * part does not work. - * - * @param google.maps.Size tileSize - */ - 'CoordMapType': function(tileSize) { - this.tileSize = tileSize; - } - }, - /** - * Stuff that we give to the google maps code instead of using ourselves - * goes in here. - * - * Also, why do I keep writing these comments as if I'm multiple people? I - * should probably stop that. - */ - 'gmap': { - /** - * Generate a function to get the path to a tile at a particular location - * and zoom level. - * - * @param string path - * @param string pathBase - * @param string pathExt - */ - 'getTileUrlGenerator': function(path, pathBase, pathExt) { - return function(tile, zoom) { - var url = path; - var urlBase = ( pathBase ? pathBase : '' ); - if(tile.x < 0 || tile.x >= Math.pow(2, zoom) || - tile.y < 0 || tile.y >= Math.pow(2, zoom)) { - url += '/blank'; - } else if(zoom == 0) { - url += '/base'; - } else { - for(var z = zoom - 1; z >= 0; --z) { - var x = Math.floor(tile.x / Math.pow(2, z)) % 2; - var y = Math.floor(tile.y / Math.pow(2, z)) % 2; - url += '/' + (x + 2 * y); - } - } - url = url + '.' + pathExt; - if(typeof overviewerConfig.map.cacheTag !== 'undefined') { - url += '?c=' + overviewerConfig.map.cacheTag; - } - return(urlBase + url); - } - } - } -};