//if (!console) console = { log: function() {} }; var maptypes = {}; function splitArgs(s) { var r = s.split(' '); delete arguments[0]; var obj = {}; var index = 0; $.each(arguments, function(argumentIndex, argument) { if (!argumentIndex) return; var value = r[argumentIndex-1]; obj[argument] = value; }); return obj; } function swtch(value, options, defaultOption) { return (options[value] || defaultOption)(value); } function DynMapType() { } DynMapType.prototype = { onTileUpdated: function(tile, tileName) { var src = this.dynmap.getTileUrl(tileName); tile.attr('src', src); tile.show(); } }; function MinecraftClock(element) { this.create(element); } MinecraftClock.prototype = { element: null, timeout: null, time: null, create: function(element) { this.element = element; $(element).addClass('clock'); }, setTime: function(time) { if (this.timeout != null) { window.clearTimeout(this.timeout); this.timeout = null; } this.time = getMinecraftTime(time); this.element .addClass(this.time.day ? 'day' : 'night') .removeClass(this.time.night ? 'day' : 'night') .text(this.formatTime(this.time)); if (this.timeout == null) { var me = this; this.timeout = window.setTimeout(function() { me.timeout = null; me.setTime(me.time.servertime+(1000/60)); }, 700); } }, formatTime: function(time) { var formatDigits = function(n, digits) { var s = n.toString(); while (s.length < digits) { s = '0' + s; } return s; } return formatDigits(time.hours, 2) + ':' + formatDigits(time.minutes, 2); } }; function MinecraftTimeOfDay(element,elementsun,elementmoon) { this.create(element, elementsun, elementmoon); } MinecraftTimeOfDay.prototype = { element: null, elementsun: null, elementmoon: null, create: function(element,elementsun,elementmoon) { if (!element) element = $('
'); this.element = element; if (!elementsun) elementsun = $(''); this.elementsun = elementsun; this.elementsun.appendTo(this.element); if (!elementmoon) elementmoon = $(''); this.elementmoon = elementmoon; this.elementmoon.appendTo(this.elementsun); this.element.height(60); this.element.addClass('timeofday'); this.elementsun.height(60); this.elementsun.addClass('timeofday'); this.elementsun.addClass('sun'); this.elementmoon.height(60); this.elementmoon.addClass('timeofday'); this.elementmoon.addClass('moon'); this.elementmoon.html(" "); this.elementsun.css("background-position", (-120) + "px " + (-120) + "px"); this.elementmoon.css("background-position", (-120) + "px " + (-120) + "px"); return element; }, setTime: function(time) { var sunangle; if(time > 23100 || time < 12900) { //day mode var movedtime = time + 900; movedtime = (movedtime >= 24000) ? movedtime - 24000 : movedtime; //Now we have 0 -> 13800 for the day period //Devide by 13800*2=27600 instead of 24000 to compress day sunangle = ((movedtime)/27600 * 2 * Math.PI); } else { //night mode var movedtime = time - 12900; //Now we have 0 -> 10200 for the night period //Devide by 10200*2=20400 instead of 24000 to expand night sunangle = Math.PI + ((movedtime)/20400 * 2 * Math.PI); } var moonangle = sunangle + Math.PI; this.elementsun.css("background-position", (-50 * Math.cos(sunangle)) + "px " + (-50 * Math.sin(sunangle)) + "px"); this.elementmoon.css("background-position", (-50 * Math.cos(moonangle)) + "px " + (-50 * Math.sin(moonangle)) + "px"); } }; function MinecraftCompass(element) { this.element = element; } MinecraftCompass.prototype = { element: null, create: function(element) { if (!element) element = $(''); this.element = element; return element; }, initialize: function() { this.element.html(" "); this.element.height(120); } }; function DynMap(options) { var me = this; me.options = options; $.getJSON(me.options.updateUrl + 'configuration', function(configuration) { me.configure(configuration); me.initialize(); }) } DynMap.prototype = { registeredTiles: new Array(), markers: new Array(), chatPopups: new Array(), lasttimestamp: '0', followingPlayer: '', configure: function(configuration) { var me = this; $.extend(me.options, configuration); if (!me.options.maps) me.options.maps = {}; $.each(me.options.shownmaps, function(index, mapentry) { me.options.maps[mapentry.name] = maptypes[mapentry.type](mapentry); }); me.world = me.options.defaultworld; }, initialize: function() { var me = this; var container = $(me.options.container); var mapContainer; (mapContainer = $('')) .addClass('map') .appendTo(container); var map = this.map = new google.maps.Map(mapContainer.get(0), { zoom: 1, center: new google.maps.LatLng(0, 1), navigationControl: true, navigationControlOptions: { style: google.maps.NavigationControlStyle.DEFAULT }, scaleControl: false, mapTypeControl: false, streetViewControl: false, backgroundColor: 'none' }); google.maps.event.addListener(map, 'dragstart', function(mEvent) { me.followPlayer(''); }); // TODO: Enable hash-links. /*google.maps.event.addListener(map, 'zoom_changed', function() { me.updateLink(); }); google.maps.event.addListener(map, 'center_changed', function() { me.updateLink(); });*/ // The sidebar var sidebar = me.sidebar = $('') .addClass('sidebar') .appendTo(container); // The world list. var worldlist = me.worldlist = $('') .addClass('worldlist') .appendTo(sidebar); $.each(me.options.shownworlds, function(index, name) { var worldButton; $('') .addClass('worldrow') .append(worldButton = $('') .addClass('worldbutton') .addClass('world_' + name) .attr({ type: 'radio', name: 'world', value: name }) .attr('checked', me.options.defaultworld == name ? 'checked' : null) ) .append($('') .attr('for', 'worldbutton_' + name) .text(name) ) .click(function() { $('.worldbutton', worldlist).removeAttr('checked'); map.setMapTypeId('none'); me.world = name; // Another workaround for GMaps not being able to reload tiles. window.setTimeout(function() { map.setMapTypeId(me.options.defaultmap); }, 1); worldButton.attr('checked', 'checked'); }) .data('world', name) .appendTo(worldlist); }); // The map list. var maplist = me.maplist = $('') .addClass('maplist') .appendTo(sidebar); $.each(me.options.maps, function(name, mapType){ mapType.dynmap = me; map.mapTypes.set(name, mapType); var mapButton; $('') .addClass('maprow') .append(mapButton = $('') .addClass('mapbutton') .addClass('maptype_' + name) .attr({ type: 'radio', name: 'map', value: name }) .attr('checked', me.options.defaultmap == name ? 'checked' : null) ) .append($('') .attr('for', 'maptypebutton_' + name) .text(name) ) .click(function() { $('.mapbutton', maplist).removeAttr('checked'); map.setMapTypeId(name); mapButton.attr('checked', 'checked'); }) .data('maptype', mapType) .appendTo(maplist); }); map.setMapTypeId(me.options.defaultmap); // The Player List var playerlist = me.playerlist = $('') .addClass('playerlist') .appendTo(sidebar); // The clock var clock = me.clock = new MinecraftTimeOfDay( $('') .appendTo(sidebar) ); // The Compass var compass = me.compass = new MinecraftCompass( $('') .addClass('compass') .appendTo(sidebar) ); compass.initialize(); // TODO: Enable hash-links. /* var link; var linkbox = me.linkbox = $('') .addClass('linkbox') .append(link=$('')) .data('link', link) .appendTo(container);*/ var alertbox = me.alertbox = $('') .addClass('alertbox') .appendTo(container); setTimeout(function() { me.update(); }, me.options.updaterate); }, update: function() { var me = this; // TODO: is there a better place for this? this.cleanPopups(); $.getJSON(me.options.updateUrl + "world/" + me.world + "/" + me.lasttimestamp, function(update) { me.alertbox.hide(); me.lasttimestamp = update.timestamp; me.clock.setTime(update.servertime); var typeVisibleMap = {}; var newmarkers = {}; $.each(update.players, function(index, player) { var mi = { id: 'player_' + player.name, text: player.name, type: 'player', position: me.map.getProjection().fromWorldToLatLng(parseFloat(player.x), parseFloat(player.y), parseFloat(player.z)), visible: true }; me.updateMarker(mi); newmarkers[mi.id] = mi; }); $.each(update.updates, function(index, update) { swtch(update.type, { tile: function() { me.onTileUpdated(update.name); }, chat: function() { if (!me.options.showchatballoons) return; me.onPlayerChat(update.playerName, update.message); } }, function(type) { console.log('Unknown type ', value, '!'); }); }); for(var m in me.markers) { var marker = me.markers[m]; if(!(m in newmarkers)) { marker.remove(null); if (marker.playerRow) { marker.playerRow.remove(); } delete me.markers[m]; } } setTimeout(function() { me.update(); }, me.options.updaterate); }, function(request, statusText, ex) { me.alertbox .text('Could not update map') .show(); setTimeout(function() { me.update(); }, me.options.updaterate); } ); }, cleanPopups: function() { var POPUP_LIFE = 8000; var d = new Date(); var now = d.getTime(); for (var popupIndex in this.chatPopups) { var popup = this.chatPopups[popupIndex]; if (now - popup.popupTime > POPUP_LIFE) { popup.infoWindow.close(); popup.infoWindow = null; delete this.chatPopups[popupIndex]; } } }, onPlayerChat: function(playerName, message) { var me = this; var markers = me.markers; var chatPopups = this.chatPopups; var map = me.map; var mid = "player_" + playerName; var playerMarker = markers[mid]; if (playerMarker) { var popup = chatPopups[playerName]; if (!popup) popup = { lines: [ message ] }; else popup.lines[popup.lines.length] = message; var MAX_LINES = 5; if (popup.lines.length > MAX_LINES) { popup.lines = popup.lines.slice(1); } htmlMessage = '