1 /* This file is part of Syj, Copyright (c) 2010 Arnaud Renevier,
2 and is published under the AGPL license. */
4 // avoid openlayers alerts
5 OpenLayers.Console.userError = function(error) {
6 SYJView.messenger.setMessage(error, "error");
13 $("geom_title").observe('contentchange', this.enableSubmit.bindAsEventListener(this));
18 if (this.status === "enabled") {
22 $("geom_title").disabled = false;
23 $("geom_title").activate();
24 $("geomform").removeClassName("disabled");
25 this.status = "enabled";
30 if (this.status === "disabled") {
34 $("geom_title").blur();
35 $("geom_title").disabled = true;
36 $("geomform").addClassName("disabled");
37 this.status = "disabled";
41 enableSubmit: function() {
42 $("geom_submit").disabled = false;
43 $("geom_accept").disabled = false;
44 this.status = "partial";
48 disableSubmit: function() {
49 $("geom_submit").blur();
50 $("geom_submit").disabled = true;
51 $("geom_accept").blur();
52 $("geom_accept").disabled = true;
53 this.status = "partial";
58 var SYJDataUi = (function() {
60 getdeck = function() {
62 deck = new Deck("data_controls");
67 viewmode: function() {
68 getdeck().setIndex(0);
70 editmode: function() {
71 getdeck().setIndex(1);
76 OpenLayers.Handler.SyjModifiablePath = OpenLayers.Class(OpenLayers.Handler.ModifiablePath, {
77 mouseup: function(evt) {
78 // do not add a point when navigating
79 var mapControls = this.control.map.controls, idx, ctrl;
81 for (idx = mapControls.length; idx-->0; ) {
82 ctrl = mapControls[idx];
83 if (this.isCtrlNavigationActive(ctrl, evt)) {
87 return OpenLayers.Handler.ModifiablePath.prototype.mouseup.apply(this, arguments);
90 addPoints: function(pixel) {
91 // redraw before last point. As we have a special style for last point, we
92 // need to redraw before last point after adding a new point (otherwise, it
93 // keeps special style forever)
94 var oldpoint = this.point;
95 OpenLayers.Handler.ModifiablePath.prototype.addPoints.apply(this, arguments);
96 this.layer.drawFeature(oldpoint);
99 isCtrlNavigationActive: function(ctrl, evt) {
100 var tolerance = 4, xDiff, yDiff;
102 if (!(ctrl instanceof OpenLayers.Control.Navigation)) {
107 ctrl.zoomBox.active &&
108 ctrl.zoomBox.handler &&
109 ctrl.zoomBox.handler.active &&
110 ctrl.zoomBox.handler.dragHandler &&
111 ctrl.zoomBox.handler.dragHandler.start) {
116 !ctrl.dragPan.active ||
117 !ctrl.dragPan.handler ||
118 !ctrl.dragPan.handler.started ||
119 !ctrl.dragPan.handler.start) {
123 // if mouse moved 4 or less pixels, consider it has not moved.
126 xDiff = evt.xy.x - ctrl.dragPan.handler.start.x;
127 yDiff = evt.xy.y - ctrl.dragPan.handler.start.y;
129 if (Math.sqrt(Math.pow(xDiff,2) + Math.pow(yDiff,2)) <= tolerance) {
135 finalize: function(cancel) {
136 // do nothing. We don't want to finalize path
141 edit: new OpenLayers.StyleMap({
142 "default": new OpenLayers.Style({
143 pointRadius: "${radius}", // sized according to type attribute
144 fillColor: "#ffcc66",
145 strokeColor: "#ff9933",
147 strokeOpacity: "${opacity}",
148 fillOpacity: "${opacity}"
152 radius: function(feature) {
155 if (!(feature.geometry instanceof OpenLayers.Geometry.Point)) {
158 if (feature.type === "middle") {
161 features = feature.layer.features;
162 if (OpenLayers.Util.indexOf(features, feature) === 0) {
164 } else if (OpenLayers.Util.indexOf(features, feature) === features.length - 1) {
169 opacity: function (feature) {
170 if (feature.type === "middle") {
179 "select": new OpenLayers.Style({
180 externalGraphic: "icons/delete.png",
184 "select_for_canvas": new OpenLayers.Style({
186 fillColor: "lightblue"
191 view: new OpenLayers.StyleMap({
192 "default": new OpenLayers.Style({
200 var WGS84 = new OpenLayers.Projection("EPSG:4326");
201 var Mercator = new OpenLayers.Projection("EPSG:900913");
207 wkt: new OpenLayers.Format.WKT({ internalProjection: Mercator, externalProjection: WGS84 }),
208 needsFormResubmit: false,
213 var externalGraphic, baseURL, baseLayer, layerOptions, extent, hidemessenger;
215 // is svg context, opera does not resolve links with base element is svg context
216 externalGraphic = styleMap.edit.styles.select.defaultStyle.externalGraphic;
217 baseURL = document.getElementsByTagName("base")[0].href;
218 styleMap.edit.styles.select.defaultStyle.externalGraphic = baseURL + externalGraphic;
220 this.map = new OpenLayers.Map('map', {
222 new OpenLayers.Control.Navigation(),
223 new OpenLayers.Control.PanZoom(),
224 new OpenLayers.Control.Attribution()
229 baseLayer = new OpenLayers.Layer.OSM("OSM", null, { wrapDateLine: true , attribution: SyjStrings.osmAttribution });
231 layerOptions = {format: OpenLayers.Format.WKT,
233 styleMap: styleMap.view,
234 attribution: SyjStrings.geomAttribution };
236 this.viewLayer = new OpenLayers.Layer.Vector("View Layer", layerOptions);
237 this.map.addLayers([baseLayer, this.viewLayer]);
240 $("edit-btn").observe('click', function() {
241 $("geom_submit").value = SyjStrings.editAction;
242 this.messenger.hide();
248 if ($("create-btn")) {
249 $("create-btn").observe('click', function() {
250 $("geom_submit").value = SyjStrings.createAction;
251 this.messenger.hide();
253 this.mode = 'create';
257 if ($("clone-btn")) {
258 $("clone-btn").observe('click', function() {
259 $("geom_submit").value = SyjStrings.cloneAction;
260 $("geom_title").value = "";
261 this.messenger.hide();
263 this.mode = 'create';
267 $("geomform").ajaxize({
268 presubmit: this.prepareForm.bind(this),
269 onSuccess: this.saveSuccess.bind(this),
270 onFailure: this.saveFailure.bind(this)
274 this.messenger = $('message');
275 hidemessenger = this.messenger.empty();
276 new CloseBtn(this.messenger, {
282 this.messenger.hide();
285 if (typeof gInitialGeom !== "undefined" && typeof gInitialGeom.data !== "undefined") {
286 this.viewLayer.addFeatures([this.wkt.read(gInitialGeom.data)]);
287 extent = this.viewLayer.getDataExtent();
288 // XXX: ie has not guessed height of map main div yet during map
289 // initialisation. Now, it will read it correctly.
290 this.map.updateSize();
292 extent = new OpenLayers.Bounds(gMaxExtent.minlon, gMaxExtent.minlat, gMaxExtent.maxlon, gMaxExtent.maxlat)
293 .transform(WGS84, Mercator);
295 this.map.zoomToExtent(extent);
296 document.observe('simplebox:shown', this.observer.bindAsEventListener(this));
299 observer: function(evt) {
300 if (evt.eventName === "simplebox:shown" && evt.memo.element !== $("termsofusearea")) {
301 this.messenger.hide();
305 prepareForm: function(form) {
306 if (!LoginMgr.logged && !$("geom_accept").checked) {
307 this.messenger.setMessage(SyjStrings.acceptTermsofuseWarn, "warn");
308 $("geom_accept_container").highlight('#F08080');
309 $("geom_accept").activate();
313 var line, realPoints, idx;
315 line = new OpenLayers.Geometry.LineString();
316 realPoints = this.editControl.handler.realPoints;
317 for (idx = 0; idx < realPoints.length; idx++) {
318 line.addComponent(realPoints[idx].geometry.clone());
320 this.viewLayer.addFeatures(new OpenLayers.Feature.Vector(line));
324 $("geom_data").value = this.wkt.write(new OpenLayers.Feature.Vector(line));
325 if (this.mode === "edit" && typeof gLoggedInfo.pathid !== "undefined") {
326 $("geomform").setAttribute("action", "path/" + gLoggedInfo.pathid.toString() + '/update');
328 $("geomform").setAttribute("action", "path");
330 this.needsFormResubmit = false;
331 SyjSaveUI.disable.bind(SyjSaveUI).defer();
332 this.messenger.hide();
336 viewMode: function() {
337 var handler = this.editControl.handler;
338 OpenLayers.Handler.ModifiablePath.prototype.finalize.apply(handler, arguments);
339 // we need to recreate them on next createFeature; otherwise
340 // they'll reference destroyed features
341 delete(handler.handlers.drag);
342 delete(handler.handlers.feature);
343 this.editControl.deactivate();
346 editMode: function() {
347 var components, point0, point, pixels, pixel, idx;
349 this.initEditControl();
351 this.editControl.activate();
352 if (this.viewLayer.features.length) {
353 components = this.viewLayer.features[0].geometry.components;
354 point0 = components[0];
356 pixel = this.map.getPixelFromLonLat(new OpenLayers.LonLat(point0.x, point0.y));
357 this.editControl.handler.createFeature(pixel);
358 this.editControl.handler.lastUp = pixel;
360 for (idx = 1; idx < components.length; idx++) {
361 point = components[idx];
362 pixels.push(this.map.getPixelFromLonLat(new OpenLayers.LonLat(point.x, point.y)));
364 this.editControl.handler.addPoints(pixels);
366 this.unsavedRoute = {
367 features: this.viewLayer.features.invoke('clone'),
368 title: $("geom_title").value
372 this.viewLayer.destroyFeatures();
374 SYJDataUi.editmode();
375 if (this.editControl.handler.realPoints && this.editControl.handler.realPoints.length >= 2) {
376 SyjSaveUI.disableSubmit();
382 initEditControl: function() {
385 if (this.editControl) {
389 this.editControl = new OpenLayers.Control.DrawFeature(new OpenLayers.Layer.Vector(), OpenLayers.Handler.SyjModifiablePath, {
391 modify: function(f, line) {
392 if (!SYJView.unsavedRoute) {
393 SYJView.unsavedRoute = {};
395 if (this.handler.realPoints.length < 2) {
405 styleMap: styleMap.edit
409 this.map.addControl(this.editControl);
410 if (this.editControl.layer.renderer instanceof OpenLayers.Renderer.Canvas) {
411 // using externalGraphic with canvas renderer is definitively too buggy
412 styles = this.editControl.handler.layerOptions.styleMap.styles;
413 styles.select = styles.select_for_canvas;
415 new CloseBtn($("geomform"), {
417 marginRight: "-40px",
420 callback: function(form) {
423 SYJDataUi.viewmode();
424 this.messenger.hide();
426 if (this.unsavedRoute && typeof this.unsavedRoute.features !== "undefined") {
427 this.viewLayer.addFeatures(this.unsavedRoute.features);
429 if (this.unsavedRoute && typeof this.unsavedRoute.title !== "undefined") {
430 $("geom_title").value = this.unsavedRoute.title;
432 $("geom_title").value = "";
434 this.unsavedRoute = null;
439 saveSuccess: function(transport) {
440 this.unsavedRoute = null;
442 if (transport.responseJSON && (typeof transport.responseJSON.redirect === "string")) {
443 location = transport.responseJSON.redirect;
447 this.messenger.setMessage(SyjStrings.saveSuccess, "success");
448 SYJDataUi.viewmode();
449 document.title = $('geom_title').value;
452 saveFailure: function(transport) {
453 var httpCode = 0, message = "";
456 httpCode = transport.getStatus();
460 message = SyjStrings.notReachedError;
464 message = SyjStrings.requestError;
465 if (transport.responseJSON) {
466 switch (transport.responseJSON.message) {
468 message = SyjStrings.uniquePathError;
477 SYJLogin.messenger.setMessage(SyjStrings.loginNeeded, "warn");
478 SYJLogin.modalbox.show();
479 this.needsFormResubmit = true;
482 message = SyjStrings.gonePathError;
485 message = SyjStrings.serverError;
486 this.needsFormResubmit = true;
489 message = SyjStrings.unknownError;
494 // is some cases, we let the user resubmit, in some other cases, he
495 // needs to modify the path before submitting again
496 if (this.needsFormResubmit) {
500 this.messenger.setMessage(message, "error");
504 var SYJModalClass = Class.create({
508 this.area = $(this.type + '_area');
509 this.messenger = $(this.type + "_message");
510 this.modalbox = new SimpleBox(this.area, {
511 closeMethods: ["onescapekey", "onouterclick", "onbutton"]
514 $(this.type + "_control_anchor").observe("click", function(evt) {
515 this.modalbox.show();
517 }.bindAsEventListener(this));
519 document.observe('simplebox:shown', this.observer.bindAsEventListener(this));
520 document.observe('simplebox:hidden', this.observer.bindAsEventListener(this));
522 $(this.type + "form").ajaxize({
523 presubmit: this.presubmit.bind(this),
524 onSuccess: this.success.bind(this),
525 onFailure: this.failure.bind(this)
529 checkNotEmpty: function(input, message) {
530 if ($(input).value.strip().empty()) {
531 this.messenger.setMessage(message, "warn");
532 $(input).highlight('#F08080').activate();
538 observer: function(evt) {
539 var simplebox, input;
541 if (evt.eventName === "simplebox:shown" && evt.memo.element !== $("termsofusearea")) {
542 simplebox = evt.memo;
543 if (simplebox === this.modalbox) {
544 input = this.area.select('input[type="text"]')[0];
549 this.modalbox.hide();
552 } else if (evt.eventName === "simplebox:hidden" && evt.memo.element !== $("termsofusearea")) {
553 simplebox = evt.memo;
554 if (simplebox === this.modalbox) {
560 failure: function(transport) {
561 var httpCode = 0, message = SyjStrings.unknownError, input; // default message error
564 httpCode = transport.getStatus();
569 message = SyjStrings.notReachedError;
574 message = SyjStrings.requestError;
577 message = SyjStrings.serverError;
581 this.messenger.setMessage(message, "error");
582 input = this.area.select('input[type="text"]')[0];
583 input.highlight('#F08080').activate();
587 this.messenger.hide();
588 this.area.select('.message').invoke('setMessageStatus', null);
592 var SYJUserClass = Class.create(SYJModalClass, {
596 init: function($super) {
598 $("termsofusearea").hide();
600 $$("#user_termsofuse_anchor, #geom_termsofuse_anchor").invoke('observe', "click", function(evt) {
602 this.toubox = new SimpleBox($("termsofusearea"), {
603 closeMethods: ["onescapekey", "onouterclick", "onbutton"]
607 if (!$("termsofuseiframe").getAttribute("src")) {
608 $("termsofusearea").show();
609 $("termsofuseiframe").setAttribute("src", evt.target.href);
612 }.bindAsEventListener(this));
614 $$("#login_area_create > a").invoke('observe', 'click',
616 this.modalbox.show();
618 }.bindAsEventListener(this));
620 $("user_pseudo-desc").hide();
621 $("user_pseudo").observe('contentchange', function(evt) {
622 var value = evt.target.value;
623 PseudoChecker.reset();
624 if (value && !(value.match(/^[a-zA-Z0-9_.]+$/))) {
625 $("user_pseudo-desc").show().setMessageStatus("warn");
627 $("user_pseudo-desc").hide();
629 }).timedobserve(function() {
630 PseudoChecker.check();
633 $("user_password").observe('contentchange', function(evt) {
634 if (evt.target.value.length < 6) {
635 $("user_password-desc").setMessageStatus("warn");
637 $("user_password-desc").setMessageStatus("success");
639 }.bindAsEventListener(this));
641 $('account-create-anchor').insert({after: new Toggler('account-info').element});
644 presubmit: function() {
645 this.messenger.hide();
646 PseudoChecker.reset();
647 if (!(this.checkNotEmpty("user_pseudo", SyjStrings.userEmptyWarn))) {
651 if (!($("user_pseudo").value.match(/^[a-zA-Z0-9_.]+$/))) {
652 $("user_pseudo-desc").show().setMessageStatus("warn");
653 $("user_pseudo").highlight('#F08080').activate();
657 if (PseudoChecker.exists[$("user_pseudo").value]) {
658 PseudoChecker.availableMessage(false);
659 $("user_pseudo").highlight('#F08080').activate();
663 if (!(this.checkNotEmpty("user_password", SyjStrings.passwordEmptyWarn))) {
667 if ($("user_password").value.length < 6) {
668 $("user_password-desc").setMessageStatus("warn");
669 $("user_password").highlight('#F08080').activate();
673 if ($("user_password").value !== $("user_password_confirm").value) {
674 this.messenger.setMessage(SyjStrings.passwordNoMatchWarn, "warn");
675 $("user_password").highlight('#F08080').activate();
679 if (!(this.checkNotEmpty("user_email", SyjStrings.emailEmptyWarn))) {
683 if (!$("user_accept").checked) {
684 this.messenger.setMessage(SyjStrings.acceptTermsofuseWarn, "warn");
685 $("user_accept_container").highlight('#F08080');
686 $("user_accept").activate();
694 success: function(transport) {
696 SYJView.messenger.setMessage(SyjStrings.userSuccess, "success");
697 this.modalbox.hide();
698 if (SYJView.needsFormResubmit) {
699 SYJView.messenger.addMessage(SyjStrings.canResubmit);
700 $("geom_submit").activate();
704 failure: function($super, transport) {
705 var httpCode = 0, focusInput = null, message = "";
708 httpCode = transport.getStatus();
716 if (transport.responseJSON) {
717 switch (transport.responseJSON.message) {
719 message = SyjStrings.emailInvalidWarn;
720 focusInput = $("user_email");
723 PseudoChecker.availableMessage(false);
724 focusInput = $("user_pseudo");
727 message = SyjStrings.uniqueEmailError;
728 focusInput = $("user_email");
737 this.messenger.setMessage(message, "error");
739 focusInput.highlight('#F08080').activate();
746 var SYJUser = new SYJUserClass();
748 var SYJLoginClass = Class.create(SYJModalClass, {
751 init: function($super) {
755 presubmit: function() {
756 this.messenger.hide();
757 if (!(this.checkNotEmpty("login_user", SyjStrings.userEmptyWarn))) {
765 success: function(transport) {
766 if (!transport.responseJSON ||
767 typeof transport.responseJSON.iscreator !== "boolean" ||
768 typeof transport.responseJSON.pseudo !== "string"
770 this.messenger.setMessage(SyjStrings.unknownError, "error");
773 LoginMgr.login(transport.responseJSON.iscreator);
774 $$('.logged-pseudo').each(function(elt) {
775 $A(elt.childNodes).filter(function(node) {
776 return (node.nodeType === 3 || node.tagName.toLowerCase() === 'br');
777 }).each(function(node) {
778 node.nodeValue = node.nodeValue.replace('%s', transport.responseJSON.pseudo);
781 SYJView.messenger.setMessage(SyjStrings.loginSuccess, "success");
782 this.modalbox.hide();
783 if (SYJView.needsFormResubmit) {
784 SYJView.messenger.addMessage(SyjStrings.canResubmit);
785 $("geom_submit").activate();
789 failure: function($super, transport) {
790 var httpCode = 0, focusInput = null, message = "";
793 httpCode = transport.getStatus();
801 message = SyjStrings.loginFailure;
802 focusInput = $("login_user");
807 this.messenger.setMessage(message, "error");
809 focusInput.highlight('#F08080').activate();
817 var SYJLogin = new SYJLoginClass();
819 var SYJNewpwdClass = Class.create(SYJModalClass, {
822 presubmit: function() {
823 if (!(this.checkNotEmpty("newpwd_email", SyjStrings.emailEmptyWarn))) {
829 success: function(transport) {
830 SYJView.messenger.setMessage(SyjStrings.newpwdSuccess, "success");
831 this.modalbox.hide();
835 var SYJNewpwd = new SYJNewpwdClass();
837 var LoginMgr = Object.extend(gLoggedInfo, {
840 updateUI: function() {
841 if (!this.controlsdeck) {
842 this.controlsdeck = new Deck("login_controls");
845 this.controlsdeck.setIndex(1);
846 $$(".logged-hide").invoke('hide');
847 $$(".logged-show").invoke('show');
849 this.controlsdeck.setIndex(0);
850 $$(".logged-hide").invoke('show');
851 $$(".logged-show").invoke('hide');
855 if (this.iscreator && SYJView.mode === 'view') {
856 $("edit-btn").show();
858 $("edit-btn").hide();
863 login: function(aIsCreator) {
864 if (typeof aIsCreator === "boolean") {
865 this.iscreator = aIsCreator;
872 var PseudoChecker = {
879 message: function(str, status, throbber) {
881 if (!this.messageelt) {
882 row = new Element('tr');
883 // we can't use row.update('<td></td><td><div></div></td>')
884 // because gecko would mangle the <td>s
885 row.insert(new Element('td'))
886 .insert((new Element('td')).update(new Element('div')));
888 $("user_pseudo").up('tr').insert({after: row});
889 this.messageelt = new Element('span');
890 this.throbber = new Element("img", { src: "icons/pseudo-throbber.gif"});
891 row.down('div').insert(this.throbber).insert(this.messageelt);
894 this.throbber.show();
896 this.throbber.hide();
898 this.messageelt.up().setStyle({visibility: ''});
899 this.messageelt.className = status;
900 this.messageelt.update(str);
903 availableMessage: function(available) {
904 var message = available ? SyjStrings.availablePseudo: SyjStrings.unavailablePseudo,
905 status = available ? "success": "warn";
906 this.message(message, status, false);
912 this.req = this.currentvalue = null;
914 if (this.messageelt) {
915 this.messageelt.up().setStyle({visibility: 'hidden'});
920 var pseudo = $("user_pseudo").value;
924 if (!pseudo || !(pseudo.match(/^[a-zA-Z0-9_.]+$/))) {
928 if (typeof this.exists[pseudo] === "boolean") {
930 this.availableMessage(!this.exists[pseudo]);
934 this.message(SyjStrings.pseudoChecking, "", true);
936 this.currentvalue = pseudo;
937 this.req = new Ajax.TimedRequest('userexists/' + encodeURIComponent(pseudo), 20, {
938 onFailure: this.failure.bind(this),
939 onSuccess: this.success.bind(this)
943 failure: function(transport) {
944 var httpCode = 0, value = this.currentvalue;
947 httpCode = transport.getStatus();
950 if (httpCode === 404) {
951 this.exists[value] = false;
952 this.availableMessage(true);
957 success: function(transport) {
958 var httpCode = transport.getStatus(), value = this.currentvalue;
960 this.exists[value] = true;
961 this.availableMessage(false);
966 document.observe("dom:loaded", function() {
969 SYJDataUi.viewmode();
974 window.onbeforeunload = function() {
975 if (SYJView.unsavedRoute) {
976 return SyjStrings.unsavedConfirmExit;