X-Git-Url: https://dev.renevier.net/?p=syj.git;a=blobdiff_plain;f=public%2Fjs%2Fsyj.js;h=8f5f509020fd0f6251055213aa3d050169f963fe;hp=39ecd4f90bdf86b8321054ead2e08f823a28612d;hb=7504b7ccb77a798b20f4dbb003a90bcaee702124;hpb=6cc4b3cd4538430770c935bd3192f1f72d75d96b diff --git a/public/js/syj.js b/public/js/syj.js index 39ecd4f..8f5f509 100644 --- a/public/js/syj.js +++ b/public/js/syj.js @@ -1,11 +1,15 @@ -/* This file is part of Syj, Copyright (c) 2010 Arnaud Renevier, +/* This file is part of Syj, Copyright (c) 2010-2011 Arnaud Renevier, and is published under the AGPL license. */ +"use strict"; + // avoid openlayers alerts OpenLayers.Console.userError = function(error) { SYJView.messenger.setMessage(error, "error"); }; +OpenLayers.Layer.Vector.prototype.renderers = ["SVG2", "VML", "Canvas"]; + var SyjSaveUI = { status: "unknown", @@ -21,7 +25,7 @@ var SyjSaveUI = { this.enableSubmit(); $("geom_title").disabled = false; $("geom_title").activate(); - $("geomform").removeClassName("disabled"); + $$("#geom_accept_container, #geom_title_container").invoke('removeClassName', "disabled"); this.status = "enabled"; return this; }, @@ -33,7 +37,7 @@ var SyjSaveUI = { this.disableSubmit(); $("geom_title").blur(); $("geom_title").disabled = true; - $("geomform").addClassName("disabled"); + $$("#geom_accept_container, #geom_title_container").invoke('addClassName', "disabled"); this.status = "disabled"; return this; }, @@ -55,20 +59,82 @@ var SyjSaveUI = { } }; +var SYJPathLength = (function(){ + return { + update: function() { + var pathLength = 0, unit; + if (SYJView.mode === 'view') { + if (SYJView.viewLayer.features.length) { + pathLength = SYJView.viewLayer.features[0].geometry.getGeodesicLength(Mercator); + } + } else { + pathLength = SYJView.editControl.handler.line.geometry.getGeodesicLength(Mercator); + } + + if (pathLength === 0) { + $("path-length").hide(); + return; + } + $("path-length").show(); + + if (pathLength < 1000) { + // precision: 1 cm + pathLength = Math.round(pathLength * 100) / 100; + unit = 'm'; + } else { + // precision: 1 m + pathLength = Math.round(pathLength) / 1000; + unit = 'km'; + } + $("path-length-content").update(pathLength + ' ' + unit); + } + }; +}()); + var SYJDataUi = (function() { var deck = null, + infotoggler = null, getdeck = function() { - if (!deck) { - deck = new Deck("data_controls"); - } - return deck; - }; + if (!deck) { + deck = new Deck("data_controls"); + } + return deck; + }, + getinfotoggler = function() { + if (!infotoggler) { + infotoggler = new Toggler('path-infos-content'); + $("path-infos-toggler").insert({bottom: infotoggler.element}); + var anchor = $("path-infos-anchor"); + var parent = anchor.up('.menu-item'); + if (parent) { + anchor = parent; + } + anchor.observe('click', function(evt) { + evt.stop(); + infotoggler.toggle(evt); + }); + document.observe('toggler:open', function(evt) { + if (evt.memo === infotoggler) { + // XXX: update informations + } + }); + } + return infotoggler; + }; return { viewmode: function() { getdeck().setIndex(0); + if ($("path-infos")) { + getinfotoggler(); + getinfotoggler().close(); + $("path-infos").show(); + } }, editmode: function() { getdeck().setIndex(1); + if ($("path-infos")) { + $("path-infos").hide(); + } } }; }()); @@ -210,7 +276,7 @@ var SYJView = { mode: 'view', init: function() { - var externalGraphic, baseURL, baseLayer, layerOptions, extent, hidemessenger; + var externalGraphic, baseURL, baseLayer, layerOptions, hidemessenger; // is svg context, opera does not resolve links with base element is svg context externalGraphic = styleMap.edit.styles.select.defaultStyle.externalGraphic; @@ -226,7 +292,11 @@ var SYJView = { theme: null }); - baseLayer = new OpenLayers.Layer.OSM("OSM", null, { wrapDateLine: true , attribution: SyjStrings.osmAttribution }); + baseLayer = new OpenLayers.Layer.OSM("OSM", [ + 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png', + 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png', + 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png'], + { attribution: SyjStrings.osmAttribution }); layerOptions = {format: OpenLayers.Format.WKT, projection: WGS84, @@ -239,7 +309,7 @@ var SYJView = { if ($("edit-btn")) { $("edit-btn").observe('click', function() { $("geom_submit").value = SyjStrings.editAction; - this.messenger.hide(); + this.messenger.clearMessages(); this.editMode(); this.mode = 'edit'; }.bind(this)); @@ -248,7 +318,7 @@ var SYJView = { if ($("create-btn")) { $("create-btn").observe('click', function() { $("geom_submit").value = SyjStrings.createAction; - this.messenger.hide(); + this.messenger.clearMessages(); this.editMode(); this.mode = 'create'; }.bind(this)); @@ -258,7 +328,7 @@ var SYJView = { $("clone-btn").observe('click', function() { $("geom_submit").value = SyjStrings.cloneAction; $("geom_title").value = ""; - this.messenger.hide(); + this.messenger.clearMessages(); this.editMode(); this.mode = 'create'; }.bind(this)); @@ -284,21 +354,118 @@ var SYJView = { if (typeof gInitialGeom !== "undefined" && typeof gInitialGeom.data !== "undefined") { this.viewLayer.addFeatures([this.wkt.read(gInitialGeom.data)]); - extent = this.viewLayer.getDataExtent(); // XXX: ie has not guessed height of map main div yet during map // initialisation. Now, it will read it correctly. this.map.updateSize(); + this.map.zoomToExtent(this.viewLayer.getDataExtent()); } else { - extent = new OpenLayers.Bounds(gMaxExtent.minlon, gMaxExtent.minlat, gMaxExtent.maxlon, gMaxExtent.maxlat) - .transform(WGS84, Mercator); + this.initMaPos(gInitialPos); } - this.map.zoomToExtent(extent); + + $("map-overlay").hide(); + $("geom_upload").observe('change', function(evt) { + var file = null, reader = null, readerror = null; + if (window.FileList && window.FileReader) { + file = evt.target.files[0]; + reader = new FileReader(); + readerror = function() { + this.messenger.setMessage(SyjStrings.uploadFileError, "warn"); + }.bind(this); + reader.onload = function(evt) { + var data = null, results = null, engine = null, vector = null, i = 0, format = null, formats = ['KML', 'GPX', 'GeoJSON']; + + $("geom_upload_container").removeClassName("disabled"); + $("geom_upload").disabled = false; + if (evt.error) { + readerror(); + return; + } + data = evt.target.result; + + for (i = 0; i < formats.length; i++) { + format = formats[i]; + engine = new OpenLayers.Format[format]({ internalProjection: Mercator, externalProjection: WGS84 }); + try { + results = engine.read(data); + } catch(e) { + } + if (results && results.length) { + break; + } + } + if (!results || !results.length) { + readerror(); + return; + } + + vector = results[0]; + if (vector.geometry.CLASS_NAME !== "OpenLayers.Geometry.LineString") { + readerror(); + return; + } + // merge linestrings for gpx containting multiple trkseg elements. + if (format === 'GPX') { + for (i = 1; i < results.length; i++) { + vector.geometry.addComponents(results[i].geometry.components); + } + } + this.viewLayer.addFeatures([vector]); + this.map.zoomToExtent(this.viewLayer.getDataExtent()); + + if ($("edit-btn")) { + $("edit-btn").click(); + } else if ($("create-btn")) { + $("create-btn").click(); + } + + if (this.editControl.handler.realPoints.length < 2) { + SyjSaveUI.disable(); + } else { + SyjSaveUI.enable(); + } + + if (vector.data && vector.data.name) { + $("geom_title").value = vector.data.name; + } + }.bind(this); + $("geom_upload_container").addClassName("disabled"); + $("geom_upload").disabled = true; + reader.readAsText(file); + return; + } + $("map-overlay").show(); + SyjSaveUI.enable(); + this.editControl.deactivate(); + }.bind(this)); + document.observe('simplebox:shown', this.observer.bindAsEventListener(this)); + SYJPathLength.update(); + }, + + initMaPos: function (aPos) { + var extent = null, center = null, zoom = 0; + + if (aPos.hasOwnProperty('lon') && aPos.hasOwnProperty('lat') && aPos.hasOwnProperty('zoom')) { + center = new OpenLayers.LonLat(parseFloat(aPos.lon), parseFloat(aPos.lat)).transform(WGS84, Mercator); + zoom = parseInt(aPos.zoom, 10); + } else if (aPos.hasOwnProperty('minlon') && aPos.hasOwnProperty('minlat') + && aPos.hasOwnProperty('maxlon') && aPos.hasOwnProperty('maxlat')) { + extent = new OpenLayers.Bounds(aPos.minlon, aPos.minlat, aPos.maxlon, aPos.maxlat) + .transform(WGS84, Mercator); + } else { + extent = new OpenLayers.Bounds(-160, -70, 160, 70).transform(WGS84, Mercator); + } + + if (extent) { + this.map.zoomToExtent(extent); + } else { + this.map.setCenter(center, zoom); + } }, observer: function(evt) { if (evt.eventName === "simplebox:shown" && evt.memo.element !== $("termsofusearea")) { - this.messenger.hide(); + this.messenger.clearMessages(); } }, @@ -321,7 +488,12 @@ var SYJView = { this.viewMode(); - $("geom_data").value = this.wkt.write(new OpenLayers.Feature.Vector(line)); + if (line.components.length) { + $("geom_data").value = this.wkt.write(new OpenLayers.Feature.Vector(line)); + } else { + $("geom_data").value = ""; + } + if (this.mode === "edit" && typeof gLoggedInfo.pathid !== "undefined") { $("geomform").setAttribute("action", "path/" + gLoggedInfo.pathid.toString() + '/update'); } else { @@ -329,7 +501,7 @@ var SYJView = { } this.needsFormResubmit = false; SyjSaveUI.disable.bind(SyjSaveUI).defer(); - this.messenger.hide(); + this.messenger.clearMessages(); return true; }, @@ -389,15 +561,28 @@ var SYJView = { this.editControl = new OpenLayers.Control.DrawFeature(new OpenLayers.Layer.Vector(), OpenLayers.Handler.SyjModifiablePath, { callbacks: { modify: function(f, line) { - if (!SYJView.unsavedRoute) { - SYJView.unsavedRoute = {}; + SYJPathLength.update(); + + var npoints = this.handler.realPoints.length; + if (npoints === 0) { + $("geom_upload_container").show(); + SYJView.unsavedRoute = null; + } else { + if (!SYJView.unsavedRoute) { + SYJView.unsavedRoute = {}; + } } - if (this.handler.realPoints.length < 2) { + + if (npoints < 2) { SyjSaveUI.disable(); } else { SyjSaveUI.enable(); } - } + }, + create: function(f, line) { + this.messenger.clearMessages(); + $("geom_upload_container").hide(); + }.bind(this) }, handlerOptions: { @@ -412,33 +597,17 @@ var SYJView = { styles = this.editControl.handler.layerOptions.styleMap.styles; styles.select = styles.select_for_canvas; } - new CloseBtn($("geomform"), { - style : { - marginRight: "-40px", - marginTop: "-20px" - }, - callback: function(form) { - this.viewMode(); - this.mode = 'view'; - SYJDataUi.viewmode(); - this.messenger.hide(); - - if (this.unsavedRoute && typeof this.unsavedRoute.features !== "undefined") { - this.viewLayer.addFeatures(this.unsavedRoute.features); - } - if (this.unsavedRoute && typeof this.unsavedRoute.title !== "undefined") { - $("geom_title").value = this.unsavedRoute.title; - } else { - $("geom_title").value = ""; - } - this.unsavedRoute = null; - }.bind(this) - }); }, saveSuccess: function(transport) { - this.unsavedRoute = null; + // server sends and empty response on success. If we get a response, that + // probably means an error or warning has been printed by server. + if (!transport.responseJSON && transport.responseText.length) { + this.saveFailure(null, 500); + return; + } + this.unsavedRoute = null; if (transport.responseJSON && (typeof transport.responseJSON.redirect === "string")) { location = transport.responseJSON.redirect; return; @@ -449,12 +618,12 @@ var SYJView = { document.title = $('geom_title').value; }, - saveFailure: function(transport) { - var httpCode = 0, message = ""; - - if (transport) { - httpCode = transport.getStatus(); + saveFailure: function(transport, httpCode) { + var message = ""; + if (typeof httpCode === "undefined") { + httpCode = transport? transport.getStatus(): 0; } + switch (httpCode) { case 0: message = SyjStrings.notReachedError; @@ -462,15 +631,6 @@ var SYJView = { case 400: case 404: message = SyjStrings.requestError; - if (transport.responseJSON) { - switch (transport.responseJSON.message) { - case "uniquepath": - message = SyjStrings.uniquePathError; - break; - default: - break; - } - } break; case 403: message = ""; @@ -511,7 +671,12 @@ var SYJModalClass = Class.create({ closeMethods: ["onescapekey", "onouterclick", "onbutton"] }); - $(this.type + "_control_anchor").observe("click", function(evt) { + var anchor = $(this.type + '_control_anchor'); + var parent = anchor.up('.menu-item'); + if (parent) { + anchor = parent; + } + anchor.observe("click", function(evt) { this.modalbox.show(); evt.stop(); }.bindAsEventListener(this)); @@ -544,7 +709,7 @@ var SYJModalClass = Class.create({ input = this.area.select('input[type="text"]')[0]; (function () { input.activate(); - }).defer(); + }.defer()); } else { this.modalbox.hide(); } @@ -584,7 +749,7 @@ var SYJModalClass = Class.create({ }, reset: function() { - this.messenger.hide(); + this.messenger.clearMessages(); this.area.select('.message').invoke('setMessageStatus', null); } }); @@ -597,7 +762,11 @@ var SYJUserClass = Class.create(SYJModalClass, { $super(); $("termsofusearea").hide(); - $$("#user_termsofuse_anchor, #geom_termsofuse_anchor").invoke('observe', "click", function(evt) { + var touevt = (function(evt) { + if (evt.type === "keyup" && evt.keyCode !== 32) { // 32 = space + // allow opening box by pressing space + return; + } if (!this.toubox) { this.toubox = new SimpleBox($("termsofusearea"), { closeMethods: ["onescapekey", "onouterclick", "onbutton"] @@ -609,7 +778,11 @@ var SYJUserClass = Class.create(SYJModalClass, { $("termsofuseiframe").setAttribute("src", evt.target.href); } evt.stop(); - }.bindAsEventListener(this)); + }).bindAsEventListener(this); + + ["click", "keyup"].each(function (evtName) { + $$("#user_termsofuse_anchor, #geom_termsofuse_anchor").invoke('observe', evtName, touevt); + }) $$("#login_area_create > a").invoke('observe', 'click', function(evt) { @@ -642,7 +815,7 @@ var SYJUserClass = Class.create(SYJModalClass, { }, presubmit: function() { - this.messenger.hide(); + this.messenger.clearMessages(); PseudoChecker.reset(); if (!(this.checkNotEmpty("user_pseudo", SyjStrings.userEmptyWarn))) { return false; @@ -692,7 +865,14 @@ var SYJUserClass = Class.create(SYJModalClass, { }, success: function(transport) { - LoginMgr.login(); + if (!transport.responseJSON || + typeof transport.responseJSON.pseudo !== "string" + ) { + this.messenger.setMessage(SyjStrings.unknownError, "error"); + return; + } + + LoginMgr.login(transport.responseJSON.pseudo); SYJView.messenger.setMessage(SyjStrings.userSuccess, "success"); this.modalbox.hide(); if (SYJView.needsFormResubmit) { @@ -753,7 +933,7 @@ var SYJLoginClass = Class.create(SYJModalClass, { }, presubmit: function() { - this.messenger.hide(); + this.messenger.clearMessages(); if (!(this.checkNotEmpty("login_user", SyjStrings.userEmptyWarn))) { return false; } @@ -770,14 +950,8 @@ var SYJLoginClass = Class.create(SYJModalClass, { this.messenger.setMessage(SyjStrings.unknownError, "error"); return; } - LoginMgr.login(transport.responseJSON.iscreator); - $$('.logged-pseudo').each(function(elt) { - $A(elt.childNodes).filter(function(node) { - return (node.nodeType === 3 || node.tagName.toLowerCase() === 'br'); - }).each(function(node) { - node.nodeValue = node.nodeValue.replace('%s', transport.responseJSON.pseudo); - }); - }); + LoginMgr.login(transport.responseJSON.pseudo, transport.responseJSON.iscreator); + SYJView.messenger.setMessage(SyjStrings.loginSuccess, "success"); this.modalbox.hide(); if (SYJView.needsFormResubmit) { @@ -860,11 +1034,18 @@ var LoginMgr = Object.extend(gLoggedInfo, { } }, - login: function(aIsCreator) { + login: function(aPseudo, aIsCreator) { if (typeof aIsCreator === "boolean") { this.iscreator = aIsCreator; } this.logged = true; + $$('.logged-pseudo').each(function(elt) { + $A(elt.childNodes).filter(function(node) { + return (node.nodeType === 3 || node.tagName.toLowerCase() === 'br'); + }).each(function(node) { + node.nodeValue = node.nodeValue.replace('%s', aPseudo); + }); + }); this.updateUI(); } }); @@ -887,7 +1068,7 @@ var PseudoChecker = { $("user_pseudo").up('tr').insert({after: row}); this.messageelt = new Element('span'); - this.throbber = new Element("img", { src: "icons/pseudo-throbber.gif"}); + this.throbber = new Element("img", { src: "icons/throbber.gif"}); row.down('div').insert(this.throbber).insert(this.messageelt); } if (throbber) { @@ -962,6 +1143,150 @@ var PseudoChecker = { } }; +var Nominatim = (function() { + var presubmit = function() { + var input = $("nominatim-search"); + if (input.value.strip().empty()) { + $("nominatim-message").setMessage(SyjStrings.notEmptyField, "warn"); + input.activate(); + return false; + } + $("nominatim-suggestions").hide(); + $("nominatim-message").hide(); + $("nominatim-throbber").show(); + return true; + }; + + var zoomToExtent = function(bounds) { // we must call map.setCenter with forceZoomChange to true. See ol#2798 + var center = bounds.getCenterLonLat(); + if (this.baseLayer.wrapDateLine) { + var maxExtent = this.getMaxExtent(); + bounds = bounds.clone(); + while (bounds.right < bounds.left) { + bounds.right += maxExtent.getWidth(); + } + center = bounds.getCenterLonLat().wrapDateLine(maxExtent); + } + this.setCenter(center, this.getZoomForExtent(bounds), false, true); + }; + + var success = function(transport) { + $("nominatim-throbber").hide(); + + if (!transport.responseJSON || !transport.responseJSON.length) { + $("nominatim-message").setMessage(SyjStrings.noResult, 'error'); + $("nominatim-search").activate(); + return; + } + + var place = transport.responseJSON[0], + bbox = place.boundingbox; + + if (!bbox || bbox.length !== 4) { + $("nominatim-message").setMessage(SyjStrings.requestError, 'error'); + return; + } + + extent = new OpenLayers.Bounds(bbox[2], bbox[1], bbox[3], bbox[0]).transform(WGS84, Mercator); + zoomToExtent.call(SYJView.map, extent); + + $("nominatim-suggestions-list").update(); + + var clickhandler = function(bbox) { + return function(evt) { + evt.stop(); + var extent = new OpenLayers.Bounds(bbox[2], bbox[1], bbox[3], bbox[0]).transform(WGS84, Mercator); + $("nominatim-suggestions-list").select("li").invoke('removeClassName', 'current'); + evt.target.up('li').addClassName('current'); + SYJView.map.zoomToExtent(extent); + }; + }; + + var i; + for (i = 0; i < transport.responseJSON.length; i++) { + var item = transport.responseJSON[i]; + if (item.display_name && item.boundingbox && item.boundingbox.length === 4) { + var li = new Element("li"); + var anchor = new Element("a", { + href: "", + className: "nominatim-suggestions-link" + }); + + anchor.observe('click', clickhandler(item.boundingbox)); + Element.text(anchor, item.display_name); + + var icon = new Element("img", { + className: "nominatim-suggestions-icon", + src: item.icon || 'icons/world.png' + }); + li.insert(icon).insert(anchor); + $("nominatim-suggestions-list").insert(li); + if ($("nominatim-suggestions-list").childNodes.length >= 6) { + break; + } + } + } + + if ($("nominatim-suggestions-list").childNodes.length > 1) { + var bottomOffset = $('data_controls').measure('height') + 3; + $("nominatim-suggestions").setStyle({ + bottom: (document.viewport.getHeight() - $('data_controls').cumulativeOffset().top + 3).toString() + 'px' + }).show(); + $("nominatim-suggestions-list").select("li:first-child")[0].addClassName('current'); + } else { + $("nominatim-suggestions").hide(); + } + + }; + + var failure = function(transport) { + $("nominatim-throbber").hide(); + + var httpCode = 0, message = SyjStrings.unknownError, input; // default message error + + if (transport) { + httpCode = transport.getStatus(); + } + + switch (httpCode) { + case 0: + message = SyjStrings.notReachedError; + break; + case 400: + case 404: + message = SyjStrings.requestError; + break; + case 500: + message = SyjStrings.serverError; + break; + } + + $("nominatim-message").setMessage(message, 'error'); + }; + + return { + init: function() { + if (!$("nominatim-form")) { + return; + } + $("nominatim-controls").hide(); + $("nominatim-label").observe('click', function(evt) { + $("nominatim-controls").show(); + $("nominatim-search").activate(); + evt.stop(); + }); + + $("nominatim-form").ajaxize({ + presubmit: presubmit, + onSuccess: success, + onFailure: failure + }); + new CloseBtn($("nominatim-suggestions")); + + $$("#nominatim-message, #nominatim-suggestions, #nominatim-throbber").invoke('hide'); + } + }; +}()); document.observe("dom:loaded", function() { SYJLogin.init(); @@ -970,7 +1295,9 @@ document.observe("dom:loaded", function() { SYJView.init(); SYJNewpwd.init(); LoginMgr.updateUI(); + Nominatim.init(); }); + window.onbeforeunload = function() { if (SYJView.unsavedRoute) { return SyjStrings.unsavedConfirmExit;