root/branches/landgate/Vespucci/Application.js @ 798

Revision 798, 20.3 kB (checked in by sbenthall, 21 months ago)

Rollback workflow: Application -> WFSVFilterLayer -> Transactions strategy

Line 
1Vespucci.Application = OpenLayers.Class({
2
3    /**
4     * API Method.
5     * Initialize the map. 
6     */
7    initialize: function(configuration) {
8        this.configuration = configuration;
9    },
10   
11    map: null,
12    configuration: null, 
13   
14    wfsLayer: null,
15
16    panZoomBar: null,
17    navigationControl: null,
18    selectControl: null,
19        moveControl: {},
20        drawControl: {},
21    permalinkControl: null,
22    selectionArgParser: null, 
23   
24    editingToolbar: null,
25   
26    sidebar: null,
27    topbar: null,
28    loginScreen: null,
29    loginHandler: null,
30   
31    requestQueue: null,
32   
33    isEditing: false,
34   
35    // Specify template div ids. During template loading,
36    // these strings will be replaced with actual Vespucci Templates.
37    templates: {
38        notesPopupEditTemplate: "notes-popup-edit-template",                                         
39        notesPopupViewTemplate: "notes-popup-view-template",
40        notesPopupMoveTemplate: "notes-popup-move-template",
41        notesPopupPermalinkTemplate: "notes-popup-permalink-template",
42        sidebarCommentsTemplate: "sidebar-comments-template",
43        sidebarFeatureHistoryTemplate: "sidebar-feature-history-template",
44        sidebarFeatureDiffTemplate: "sidebar-feature-diff-template",
45        sidebarHistoryFeedTemplate: "sidebar-historyfeed-template",
46        sidebarHistoryTemplate: "sidebar-history-template",
47        sidebarHelpTemplate: "sidebar-help-template",
48        sidebarLoadingTemplate: "sidebar-loading-template",
49        topbarTemplate: "topbar-template",
50        loginScreenTemplate: "login-screen-template",
51                sidebarAddNoteTemplate: "sidebar-add-notes-template"
52    },       
53   
54    /**
55     * API Method.
56     * Initialize the map, and all its pieces.
57     */
58    load: function() {
59       
60        // Set OpenLayers specific data.
61        OpenLayers.ProxyHost = this.configuration.proxy;
62        OpenLayers.ImgPath = Vespucci.ImgPath;
63        OpenLayers.IMAGE_RELOAD_ATTEMPTS = this.configuration.imageReloadAttempts;
64        OpenLayers.Popup.POPUP_MARGIN = this.configuration.popupMargins;
65       
66        // Set the class name of the map div so all items within the map
67        // get Vespucci specific CSS styles.
68        var map = document.getElementById(this.configuration.mapDivId);
69        map.className = this.configuration.mainVespucciCSSClass + " " + map.className;       
70       
71        this.loadImages();
72        this.loadRequestQueue();
73        this.loadTemplates();
74        this.loadMap();
75        this.loadLayers();
76        this.loadLoginHandler();
77       
78        if (this.configuration.defaultZoomLevel) {
79            this.map.zoomTo(this.configuration.defaultZoomLevel);
80        } else {
81            this.map.zoomToMaxExtent();
82        }
83       
84        var application = this;
85       
86        var finishLoading = function() {
87            application.loadTopbar();
88            application.loadControls();
89            application.loadSidebar();
90               
91            // Start the map in view mode.
92            if (application.configuration.startInViewMode) {
93                application.switchToViewMode();
94            } else {
95                application.switchToEditMode();
96            }
97           
98            application.showHome();
99        };
100       
101        var keepWaiting = function(){
102            return !application.templatesLoaded();
103        };
104       
105        Vespucci.Util.wait(keepWaiting, finishLoading);
106    },
107   
108    loadImages: function() {
109        for (var index = 0; index < this.configuration.icons.length; index++) {
110           
111            var src = this.configuration.icons[index];
112            var image = new Image();
113           
114            image.onerror = function() {
115                OpenLayers.Console.error("Trouble preloading image '" + src + "'.");
116            };
117           
118            image.src = src;
119        }
120    },
121   
122    loadRequestQueue: function() {
123        this.requestQueue = new Vespucci.RequestQueue(); 
124    },
125   
126    loadTemplates: function() {
127       
128        var application = this;
129       
130        /*
131         * Should we refactor a Request to handle more than simply
132         * WFS requests? See onFailure below.
133         */
134        function onSuccess(response) {
135           
136            var templateContainer = document.createElement("DIV");
137            templateContainer.className = application.configuration.templateContainerCSSClass;
138           
139            templateContainer.innerHTML = response.responseText;
140           
141            // Use the document to turn the HTML markup into DOM elements, turn
142            // turn those DOM elements into Jugl templates, then finally remove
143            // the DOM elements from the body.
144            document.body.appendChild(templateContainer);
145           
146            for (var name in application.templates) {
147                var id = application.templates[name];
148                var element = document.getElementById(id);
149               
150                application.templates[name] = new Jugl.Template(element);
151            }
152           
153            document.body.removeChild(templateContainer);
154        }
155       
156        function onFailure(response) {
157            OpenLayers.Console.error("Could not retrieve template file.");
158        }
159       
160        var request = new OpenLayers.Ajax.Request(this.configuration.templateFile,
161                                    {
162                                        method: "get",
163                                        onSuccess: onSuccess,
164                                        onFailure: onFailure
165                                    });
166    }, 
167   
168    loadMap: function() {
169       
170        var restrictedExtent = (this.configuration.restrictExtent ? this.configuration.maxExtent : null);
171       
172        var options = {
173            projection: this.configuration.projection,
174            units: "m",
175            maxExtent: this.configuration.maxExtent,
176            restrictedExtent: restrictedExtent,
177            maxResolution: this.configuration.maxResolution,
178            controls: [],
179            numZoomLevels: this.configuration.numZoomLevels,
180            theme: Vespucci._getScriptLocation() + "Vespucci/Theme/map.css"
181        };
182       
183        this.map = new OpenLayers.Map(this.configuration.mapDivId, options);
184    },
185   
186    loadLayers: function() {
187        var wms = new OpenLayers.Layer.WMS(
188            this.configuration.wmsLayerName,
189            this.configuration.wmsUrl,
190            this.configuration.wmsOptions
191        );       
192       
193        var styleMap = this.createStyleMap();
194
195        this.wfsLayer = new Vespucci.WFSVFilterLayer(this.configuration, 
196                                               this.configuration.wfsLayerName,
197                                               this.configuration.wfsvUrl,
198                                               {
199                                                    typename: this.getWFSTypeString(),
200                                                    featureNS : this.configuration.namespaceUri,
201                                                    srsName: this.configuration.projection
202                                               },
203                                               {
204                                                    extractAttributes: true,
205                                                    yOrdering: true,
206                                                    styleMap: styleMap
207                                               });
208       
209        this.map.addLayers([wms, this.wfsLayer]);   
210    },
211   
212    createStyleMap: function(){
213       
214        var rules = [];
215       
216        for(type in this.configuration.featureStyles){
217            rules.push(new Vespucci.Rule.FeatureTypeRule(type,this.configuration.featureStyles[type]));
218           
219            if(this.configuration.subtypes[type]){
220               
221                for(var i = 0; i < this.configuration.subtypes[type].length; i++){
222                    rules.push(new Vespucci.Rule.SubtypeRule(type, this.configuration.subtypes[type][i]));
223                }
224            }
225           
226        }
227       
228        var style = new OpenLayers.Style();
229       
230        style.addRules(rules);
231       
232        var styleMap = new OpenLayers.StyleMap({'default' : style, 'select' : {}});
233       
234        return styleMap;
235    },
236       
237   
238    loadControls: function() {
239       
240        var application = this;
241       
242        var selectOptions = {
243            onSelect: function(feature) {feature.selected(application);}
244        };
245       
246                this.map.events.register("zoomend", application, function(){
247            var popup = this.getPopup();
248            if(popup){
249                this.map.setCenter(popup.lonlat);
250            }
251        });
252
253                for(var name in application.configuration.moveControls){
254                        var moveHandler = application.configuration.moveControls[name];
255                       
256                        moveStyle = OpenLayers.Util.extend({}, application.configuration.featureStyles[name]);
257           
258                var moveControlOptions = {
259                handlerOptions: {
260                        layer: this.wfsLayer,
261                        style: moveStyle
262                }
263                };
264                       
265                        this.moveControl[name] = new Vespucci.MoveFeature(this.wfsLayer, moveHandler, moveControlOptions);             
266                                     
267                this.moveControl[name].featureMoved = function(feature){
268                                // Deactivate the draw control, since a feature was just added.
269                                this.deactivate();
270                                               
271                                // Go straight into editing after moving the feature.
272                                feature.edit(application);
273                        }                               
274                }
275               
276               
277                for (var type in application.configuration.addControls) {
278                        // Remember: This is just the handler class. DrawFeature takes care of
279                        // instantiating it for us.
280                        var handler = application.configuration.addControls[type];
281                       
282                        var style = null;
283                        if (application.configuration.featureStyles[type]) {
284                                style = OpenLayers.Util.extend({}, application.configuration.featureStyles[type]);
285                        }
286                       
287                        var controlOptions = {
288                                handlerOptions: {
289                                        layer: application.wfsLayer,
290                                        style: style
291                                }
292                        };
293                       
294                        this.drawControl[type] = new Vespucci.DrawVespucciFeature(type,application.configuration, application.wfsLayer, handler, controlOptions);
295                       
296                        this.drawControl[type].featureAdded = function(feature){
297                                feature.state = OpenLayers.State.INSERT;
298                               
299                                // Go straight into editing after adding the feature.
300                                feature.edit(application);
301                        };
302                       
303                        // Slight hack, but it allows us to referecnce controls by name.
304            // EDIT: Do we need this now that we can reference draw controls by type?   
305                        this.drawControl[type].name = type;
306                }
307               
308        this.panZoomBar = new OpenLayers.Control.PanZoomBar();
309        this.selectControl = new OpenLayers.Control.SelectFeature(this.wfsLayer, selectOptions);
310        this.navigationControl = new OpenLayers.Control.Navigation();
311        this.selectionParser = new Vespucci.SelectionArgParser(this);     
312        this.permalinkControl = new Vespucci.FeaturePermalink();
313       
314        this.map.addControl(this.panZoomBar);
315        this.map.addControl(this.navigationControl);
316                this.map.addControl(this.selectControl);
317                for(var name in this.moveControl){
318                this.map.addControl(this.moveControl[name]);
319                }
320                for(type in this.drawControl){
321                        this.map.addControl(this.drawControl[type]);
322                }               
323        this.map.addControl(this.selectionParser);
324        this.map.addControl(this.permalinkControl);
325    },
326   
327    loadSidebar: function() {
328        this.sidebar = new Vespucci.Sidebar(this); 
329    },
330   
331    loadTopbar: function() {
332        this.topbar = new Vespucci.Topbar(this);
333    },
334   
335    loadLoginHandler: function() {
336        this.loginHandler = new this.configuration.loginHandlerClass(this); 
337    },
338   
339    templatesLoaded: function() {
340        for (var name in this.templates) {
341            if (typeof this.templates[name] == "string") {
342                return false;
343            }
344        }
345       
346        return true;
347    },
348   
349    /**
350     * Switch the map into edit mode. Those shows the editing toolbar, allows features to be dragged,
351     * and allows access to the editing popup.
352     */
353        //Do we need this?  Check with Cholmes to see if this is something we should
354        //care about.
355    switchToEditMode: function() {
356                this.selectControl.deactivate();
357        this.selectControl.activate();
358        this.isEditing = true;           
359    },
360   
361    /**
362     * Switch from the edit mode to the viewing mode. This code assumes that the application is in the
363     * edit state before calling.
364     */
365    switchToViewMode: function() {
366        this.isEditing = false;   
367       
368        this.navigationControl.activate();
369        this.selectControl.activate();
370    },
371   
372    /**
373     * Method: updateMargins
374     * Called by other classes to let the application know the
375     * margins have changed.
376     */
377    updateMargins: function() {
378        var margins = this.configuration.margins;
379       
380        // Set this in case the panZoomBar isn't loaded yet.
381        OpenLayers.Control.PanZoom.Y = this.configuration.margins.top;
382       
383        if (this.panZoomBar) {
384            this.panZoomBar.moveTo(new OpenLayers.Pixel(margins.left, margins.top));
385        }
386       
387        if (this.sidebar) {
388            this.sidebar.calculatePosition();
389        }
390    },
391   
392    getWFSTypeString: function() {
393        var output = "";
394        for (var typeIndex = 0; typeIndex < this.configuration.wfsLayerTypes.length; typeIndex++) {
395            if (output != "") output += ",";
396            output = this.configuration.namespace 
397                + ":" + this.configuration.wfsLayerTypes[typeIndex];
398        }
399        return output;
400    },
401   
402    getWFSParams: function(index){
403        index = index || 0;
404       
405        return {
406            featureNS : this.configuration.namespaceUri,
407            featureType : this.configuration.wfsLayerTypes[index],
408            featurePrefix : this.configuration.namespace
409        }   
410    },
411   
412    addPopup: function(popup) {
413       
414        // This is here because the map's removePopup() function
415        // doesn't call the popups hide() command.
416        for(var i=0; i < this.map.popups.length; i++) {
417            this.map.popups[i].close();
418        }
419       
420        this.map.addPopup(popup,true);
421    },       
422   
423    /**
424     * Method: getPopup
425     *
426     * Get the currently opened popup. Returns null if a popup
427     * is not opened.
428     */
429    getPopup: function() {
430        return this.map.popups[0];
431    },
432       
433    // Request related functions.
434   
435    // TODO: Make these work through the vector layer's Save(?) strategy.
436    //     
437   
438    updateFeature: function(feature, onSuccess, onFailureOrCancel) {
439        /*
440        var request = new Vespucci.Request.UpdateFeatureRequest(this, feature);
441        request.send(onSuccess, onFailureOrCancel, onFailureOrCancel);
442        */
443        feature.state = OpenLayers.State.UPDATE;
444        this.wfsLayer.commitFeature(feature, onSuccess, onFailureOrCancel);
445    },
446   
447    insertFeature: function(feature, onSuccess, onFailureOrCancel) {
448        /*
449        var request = new Vespucci.Request.InsertFeatureRequest(this, feature);
450        request.send(onSuccess, onFailureOrCancel, onFailureOrCancel);
451        */
452       
453           //BROKEN because of the callbacks
454        feature.state = OpenLayers.State.INSERT;
455        this.wfsLayer.commitFeature(feature, onSuccess, onFailureOrCancel);
456    },
457   
458    deleteFeature: function(feature, onSuccess, onFailureOrCancel) {
459        feature.state = OpenLayers.State.DELETE;
460        this.wfsLayer.commitFeature(feature, onSuccess, onFailureOrCancel);
461    },
462
463    rollback: function(feature, options, onSuccess, onFailureOrCancel){
464        this.wfsLayer.rollback(feature, options, onSuccess, onFailureOrCancel);
465    },
466
467    // Window functions (encapsulation of dialogs/dialog-type classes).
468
469    showSidebar: function() {
470        this.sidebar.show();
471    },
472
473        showHistoryFeed : function(){
474        this.loadView(new Vespucci.SidebarView.HistoryFeedView(this)); 
475       
476        // Always show the sidebar for the history feed.
477        this.showSidebar();
478    },
479   
480    showHome: function() {
481        this.loadHomeViewInSidebar();
482
483        // Always show the sidebar when showing help.
484        this.showSidebar(); 
485    },
486   
487    loadHomeViewInSidebar: function() {
488        this.loadView(new Vespucci.SidebarView.HelpView(this));
489    },
490   
491    contextualShowHome: function(){
492        //messy for now
493        if(this.sidebar.view.hasFeatureContext()){
494            this.loadHomeViewInSidebar();
495        } 
496    },
497       
498        showAddFeature: function(type) {
499                this.loadView(new Vespucci.SidebarView.AddFeatureView(this, type, this.drawControl[type]));
500                this.showSidebar();
501        },
502   
503    showHistory: function() {
504        this.loadView(new Vespucci.SidebarView.HistoryView(this)); 
505    },
506   
507    showLoginScreen: function(onSuccess, onCancel) {
508       
509        var application = this;
510       
511        // If there is a successful login, ever, we want to update the topbar.
512        var updateTopBarOnSuccess = function() {
513            application.topbar.updateLoginLinkText();
514            if (onSuccess) {
515                onSuccess();
516            }
517        }
518        if(!this.loginScreen){
519            this.loginScreen = new Vespucci.LoginScreen(this, updateTopBarOnSuccess, onCancel);   
520        }
521    },
522   
523    loadCommentsInSidebar: function(feature, scrollPosition){
524        this.loadView(new Vespucci.SidebarView.CommentsView(this, feature, scrollPosition));
525    },
526   
527    loadFeatureHistoryInSidebar: function(feature, scrollPosition){
528        this.loadView(new Vespucci.SidebarView.FeatureHistoryView(this, feature, scrollPosition));
529    },
530       
531    loadFeatureDiff: function(feature, fromFeatureVersion, toFeatureVersion){
532        this.loadView(new Vespucci.SidebarView.FeatureDiffView(this, feature, fromFeatureVersion, toFeatureVersion));
533    },
534   
535    /**
536     * Load a given view into the sidebar.
537     */
538    loadView: function(view) {
539        this.sidebar.loadView(view);
540    },
541       
542        // Login functions (encapsulation of this.loginHandler).
543       
544        login: function(username, password, onValidate, onInvalid) {
545                this.loginHandler.validate(username, password, onValidate, onInvalid);
546        },
547       
548        logout: function() {
549                this.loginHandler.logout();
550        this.topbar.updateLoginLinkText();
551        },
552       
553        isLoggedIn: function() {
554        return this.loginHandler.isLoggedIn();
555    },
556   
557    showPleaseLoginError: function() {
558        //alert("You do not have permission to perform this action. Please login and try again.") 
559    },
560   
561    showForbiddenError: function() {
562        alert("You do not have permission to perform that action. If you feel you have received this message in error please contact an administrator.") 
563    },
564
565    // Functions for enabling/disabling the zoom wheel.
566       
567    /**
568     * Enable the wheel handler of the navigation control.
569     */
570    enableZoomWheel: function(){
571        this.navigationControl.enableZoomWheel();
572    },
573   
574    /**
575     * Disable the wheel handler of the navigation control.
576     * This allows the wheel to be used for scrolling (for instance,
577     * in the sidebar).
578     */
579    disableZoomWheel : function(){
580        this.navigationControl.disableZoomWheel();
581    }, 
582   
583        // Layer functions.
584       
585    refreshFeatures: function() {
586        this.wfsLayer.redraw();
587    },
588   
589    unselectFeature: function(feature) {
590        if(feature.geometry){
591            this.selectControl.unselect(feature);
592        }
593    },
594   
595    removeFeatures : function(features){
596        this.wfsLayer.removeFeatures(features); 
597    },
598   
599    addFeatures: function(features) {
600        this.wfsLayer.addFeatures(features);
601    },
602           
603    getFeatureByFid: function(fid) {
604        return this.wfsLayer.getFeatureByFid(fid);
605    },
606   
607    hasFeature: function(feature) {
608        return this.getFeatureByFid(feature.getFID()) != null;
609    },
610       
611        //Control activation functions
612       
613        moveFeature: function(feature){
614                this.wfsLayer.eraseFeatures([feature]);
615                this.moveControl[feature.type].activate(feature);
616        },
617   
618    // Permalinking
619   
620    /**
621     * Method: getPermalink
622     * Get the current permalink. If a feature is passed, the permalink
623     * will be centered around the feature, and, when accessed, the feature
624     * will be selected.
625     *
626     * {<Vespucci.Feature>} feature
627     */
628    getPermalink: function(feature) {
629        var title = document.title;
630       
631        if (feature) {
632            title = feature.getTitle();
633        }
634       
635        var url = this.permalinkControl.createUrl(feature);
636       
637        return url;
638    },
639
640    CLASS_NAME: "Vespucci.Application"
641});
Note: See TracBrowser for help on using the browser.