// $Id: site.js 2952 2008-12-01 08:57:19Z jcigar $

Rodentia = {

    // Helper function to bookmark a page/link
    bookmark : function(title, url) {
        // Mozilla
        if (window.sidebar) {
            window.sidebar.addPanel(title, url, '');
        // IE
        } else if (window.external) {
            window.external.AddFavorite(url, title); 
        // Opera
        } else if (window.opera && window.print) {
            var blah = document.createElement('a');
            blah.setAttribute('href', url);
            blah.setAttribute('title', title);
            blah.setAttribute('rel', 'sidebar');
            blah.click();
        // Go die kthx;
        } else {
            alert('Sorry, browser unsupported. Please add the bookmark manually');
        }
    },

    // Helper function, which handle the spinner images
    _spinner : function(caller, evt) {
        if (caller.spinner && evt) {
            var spinner = $(caller.spinner);
            var replace = caller['spinner_'.concat(evt)];
            if (replace) {
                spinner.src = replace;
                spinner.show();
            } else {
                spinner.hide();
            }
        }
    },

    /*
     *  We try to avoid loading Google Map javascript if it's not necessary
     *  (because it's quite heavy). At this time of writing, the only way to do
     *  this is to put the callback function in the source URL (callback=...)
     *  and to put the async=2 in the parameters. For more information check this
     *  thread: http://groups.google.com/group/Google-Maps-API/browse_thread/thread/8863f57e0904a221/3f1577e7abed928b
     */

    _loadGoogleMapJS : function (callback) {
        // Create a new <script> and add it to the document body.
        var script = document.createElement('script');
        script.type = "text/javascript";
        script.src = "http://maps.google.be/maps?file=api&v=2.x&key=".concat(Rodentia.google_map_key).concat('&async=2&callback=').concat(callback);
        document.body.appendChild(script);
    },

    do_post : function(url, parameters, before, after) {
        var IFRAME = 'rodentia_do_post_iframe';
        var form = document.createElement('form');
        //form.hide(); // raises an error under Internet Explorer ...
        form.method = 'post';
        form.action = url;

        // We've to use an <iframe> because there is no onload event on a 
        // <form> element.
        // FIXME: doesn't work under Internet Explorer
        if (before || after) {
            if (before) before();
            var myframe = document.getElementById(IFRAME);
            if (!myframe) {
                var myframe = document.createElement('iframe');
                myframe.name = myframe.id = IFRAME;
                myframe.style.display = 'none';
            }

            form.target = IFRAME;
            document.body.appendChild(myframe);
        }

        if (after) myframe.onload = function() {
            after();
        };

        function _build_input(key, value) {
            var input = document.createElement('input');
            
            input.type = 'hidden';
            input.name = key;
            input.value = value;

            return input
        }

        for (var key in parameters) {
            if (key instanceof Array) {
                form.appendChild(_build_input(key, parameters[key].toString()));
            } else {
                form.appendChild(_build_input(key, parameters[key]));
            }
        }
        
        document.body.appendChild(form);
        form.submit();
        document.body.removeChild(form);
    },

    /*
     * Below are objects which handle all the various dynamic parts of the site
     * (taxonomy list, specimen list, picture list, etc). It is important that
     * each object has its own parameters set, so that there is no risks of
     * conflicts between them (for example if we use and offset=0 for a picture
     * list and an offset=50 for a specimen list at the same time, and that the
     * parameters is a global object, there will be conflicts. Of course we
     * could avoid this by having offset_picture=xxx and offset_specimen=xxx,
     * but it's cleaner if each object has its own one :-) )
     */

    SpecimenList : function(params) {
        this.parameters = params.parameters;
        this.container = params.container;
        this.url = params.url;
        this.loaded = false;
        this.spinner = params.spinner;
        this.spinner_visible = params.spinner_visible;
        this.spinner_hidden = params.spinner_hidden;
        this.spinner_loading = params.spinner_loading;

        this.update = function(params) {
            Rodentia._spinner(this, 'loading');
            var container = $(this.container);

            // Fields where default order should be desc by default
            var special_cases = ['basis_of_record', 'geo_ref', 'picture', 
                'sequence', 'measurements', 'type'];

            // Default limit of 50 per page
            if (params.limit) { 
                this.parameters.limit = params.limit;
            } else if (!this.parameters.limit) {
                this.parameters.limit = 50;
            }

            this.parameters.offset = params.offset ? params.offset : 0;

            // Order and sort
            if (params.order) {
                if ((special_cases.indexOf(params.order) != -1) && (params.order != this.parameters.order)) {
                    this.parameters.sort = 'desc';
                } else if (params.order == this.parameters.order) {
                    this.parameters.sort = this.parameters.sort == 'asc' ? 'desc' : 'asc';
                }
                this.parameters.order = params.order;
            } else {
                if (!this.parameters.order) this.parameters.order = 'name';
                if (!this.parameters.sort) this.parameters.sort = 'asc';
            }

            var my = this;
            new Ajax.Updater(container, this.url, {
                parameters : this.parameters,
                evalScripts : true,
                method : 'get',
                onSuccess : function(req) {
                    my.loaded = true;
                    container.show();
                },
                onFailure : function(req) {
                    alert('Sorry, an error occured');
                },
                onComplete : function(req) {
                    Rodentia._spinner(my, 'visible');
                }
            })
        } // this.update()

        this.toggle = function (params) {
            var container = $(this.container);
            if (container.visible()) {
                Rodentia._spinner(this, 'hidden');
                container.hide();
            } else if (!this.loaded) {
                this.update(params);
            } else {
                Rodentia._spinner(this, 'visible');
                container.show();
            }
        } // this.toggle()
    },

    SpecimenDistributionMap : function(params) {
        this.parameters = params.parameters;
        this.container = params.container;
        this.target = params.target;
        this.url = params.url;
        this.loaded = false;
        this.spinner = params.spinner;
        this.spinner_visible = params.spinner_visible;
        this.spinner_hidden = params.spinner_hidden;
        this.spinner_loading = params.spinner_loading;
        this.coords = params.coords;

        this.load = function(callback) {
            Rodentia._spinner(this, 'loading');

            if (typeof(GMap2) != 'function') {
                return Rodentia._loadGoogleMapJS(callback.concat('.load'));
            }

            if (GBrowserIsCompatible()) {
                var my = this;
                new Ajax.Request(this.url, {
                    parameters : this.parameters,
                    method : 'get',
                    onSuccess : function (req) {
                        var hash = req.responseText;
                        var geoXml = new GGeoXml(Rodentia.site_url.concat('/kmz/locality/').concat(hash), function() {
                            Rodentia._spinner(my, 'visible');
                        });
                        // TODO: guess size dynamically
                        var mapOpts = { 
                            'size' : new GSize(750, 450) 
                        };
                        var map = new GMap2($(my.target), mapOpts);
                        map.addMapType(G_PHYSICAL_MAP); // not enabled by default
                        map.addControl(new GLargeMapControl());
                        map.addControl(new GMapTypeControl());
                        map.addControl(new GScaleControl());
                        map.addControl(new GOverviewMapControl());
                        map.setCenter(new GLatLng(2, 20), 3);
                        map.addOverlay(geoXml);
                        map.setMapType(G_PHYSICAL_MAP);
                        //map.enableScrollWheelZoom();
                        $(my.container).show();
                        my.loaded = true;
                        // TODO: dirty ...
                        GEvent.addListener(map, 'mousemove', function(latlng) {
                            $(my.coords).innerHTML = '<b>Latitude:</b> '.concat(latlng.lat()).concat(' | <b>Longitude:</b> ').concat(latlng.lng());
                        });
                        my.map = map;
                    },
                    onFailure : function(req) {
                        alert('Sorry, an error occured');
                    }
                }) // Ajax.Request()
            } else {
                alert('Sorry, your browser is not compatible with Google Maps');
            }
        } // this.load()

        this.toggle = function (callback) {
            var container = $(this.container);
            if (container.visible()) {
                Rodentia._spinner(this, 'hidden');
                container.hide();
            } else if (!this.loaded) {
                this.load(callback);
            } else {
                Rodentia._spinner(this, 'visible');
                container.show();
            }
        }

    },

    TaxonomyList : function(params) {
        this.parameters = params.parameters;
        this.container = params.container;
        this.url = params.url;
        this.spinner = params.spinner;
        this.spinner_loading = params.spinner_loading;

        this.update = function(params) {
            Rodentia._spinner(this, 'loading');

            // Fields where default order should be desc by default
            var special_cases = ['specimen_has_type', 'specimen_has_pictures', 
                'specimen_has_sequences', 'has_description', 'has_publication'];

            // Order and sort
            if (params.order) {
                if ((special_cases.indexOf(params.order) != -1) && (params.order != this.parameters.order)) {
                    this.parameters.sort = 'desc';
                } else if (params.order == this.parameters.order) {
                    this.parameters.sort = this.parameters.sort == 'asc' ? 'desc' : 'asc';
                }
                this.parameters.order = params.order;
            } else {
                if (!this.parameters.order) this.parameters.order = 'name';
                if (!this.parameters.sort) this.parameters.sort = 'asc';
            }

            var my = this;
            new Ajax.Updater(this.container, this.url, {
                parameters : this.parameters,
                evalScripts : true,
                method : 'get',
                onFailure : function(req) {
                    alert('Sorry, an error occured');
                },
                onComplete : function(req) {
                    Rodentia._spinner(my, 'visible');
                }
            })
        } // this.update()
    },

    TaxonomyBrowser : function(params) {
        this.family_id = params.family_id;
        this.genus_id = params.genus_id;
        this.species_id = params.species_id;
        this.container = params.container;
        this.url = params.url;
        this.spinner = params.spinner;
        this.spinner_loading = params.spinner_loading;

        this.load = function(params) {
            Rodentia._spinner(this, 'loading');
            if (params.family_id) this.family_id = params.family_id;
            if (params.genus_id) this.genus_id = params.genus_id;
            if (params.species_id) this.species_id = params.species_id;

            var my = this;
            new Ajax.Updater(this.container, this.url, {
                parameters : {
                    'family_id' : my.family_id,
                    'genus_id' : my.genus_id,
                    'species_id' : my.species_id
                },
                method : 'get', 
                onSuccess : function(req) {
                    if (typeof(my.afterUpdate) == 'function') my.afterUpdate(req);
                },
                onFailure : function(req) {
                    alert('Sorry, an error occured');
                },
                onComplete : function(req) {
                    Rodentia._spinner(my, 'visible')
                }
            })
        }

        this.reset = function() {
            this.family_id = this.genus_id = this.species_id = null;
            this.load({});
        }
    },

    SelectionMap : function(params) {
        this.points = [];
        this.target = params.target;

        // Color && stuff like that
        this.fillColor = params.fillColor ? params.fillColor : '#ff6600';
        this.lineColor = params.lineColor ? params.lineColor : '#000000';
        this.opacity = params.opacity ? params.opacity : 0.5;
        this.lineWeight = params.lineWeight ? params.lineWeight : 2;
        this.marker = params.marker ? params.marker : undefined;
        
        // Load the JS, create the GMap object, add controls, etc
        this.load = function(callback) {
            if (typeof(GMap2) != 'function') {
                return Rodentia._loadGoogleMapJS(callback.concat('.load'));
            }

            if (GBrowserIsCompatible()) {
                var mapOpts = { 
                    'size' : new GSize(450, 220)
                };
                this.map = new GMap2($(this.target), mapOpts);
                this.map.setCenter(new GLatLng(2, 20), 2);
                this.map.addControl(new GSmallMapControl());
                this.map.addControl(new GMapTypeControl());
            } else {
                alert('Sorry, your browser is not compatible with Google Maps');
            }
        }

        // Called when the user clicks on the 'hand' icon
        this.select = function() {
            var mapDrag = this.map.getDragObject();
            mapDrag.setDraggableCursor('move');
            mapDrag.setDraggingCursor('move');

            this.map.enableDoubleClickZoom();
            GEvent.clearListeners(this.map, 'click');
        }

        this.clear = function() {
            this.map.clearOverlays();
            this.points = [];
        }

        // Convert the polygon data (GLatLng objects) to a string
        // Used to pass the data to the server. The string should be added
        // to the <form> through a hidden field.
        this.toUrl = function(precision) {
            if (!precision) precision = 5;

            var points = [];
            for (var i=0 ; i<this.points.length ; i++) {
                try {
                    points.push(this.points[i].toUrlValue(precision));
                } catch(e) {
                    ;
                }
            }
            // lat1,long1,lat2,long2,...
            return points.join()
        }

        // Called when the user click on the polygon icon
        this.polygon = function() {
            // Change cursor
            var mapDrag = this.map.getDragObject();
            mapDrag.setDraggableCursor('default');
            mapDrag.setDraggingCursor('move');

            this.map.disableDoubleClickZoom();

            var my = this;
            // Note: point is a GLatLng object
            GEvent.addListener(this.map, 'click', function(marker, point) {
                if (point && point.lat() && point.lng()) {
                    my.points.push(point);
                    my.drawArea(); // redraw shape
                }
            });
        }

        // Fill the polygon shape
        this.drawArea = function() {
            // Remove old shape
            if (this.shape) this.map.removeOverlay(this.shape);

            // If there's exactly one point, then it's the initial marker 
            // (the black dot)
            if (this.points.length == 1) {
                var markerOptions = {};

                if (this.marker) {
                    var markerIcon = new GIcon();
                    markerIcon.image = this.marker;
                    markerIcon.iconAnchor = new GPoint(5, 5);
                    markerIcon.iconSize = new GSize(12,12);
                    markerOptions.icon = markerIcon
                }

                var marker = new GMarker(this.points[0], markerOptions);

                var my = this;
                GEvent.addListener(marker, 'click', function() {
                    my.closeArea(marker)
                });
                this.map.addOverlay(marker);
            // Else, add (draw) the shape to the map
            } else {            
                // Create a new GPolygon object (points are in this.points)
                this.shape = new GPolygon(this.points, this.lineColor, 
                                          this.lineWeight, this.opacity, 
                                          this.fillColor, this.opacity);
                // ... and add it to the map
                this.map.addOverlay(this.shape);
            }
        }

        // Called when the user ends its selection by clicking on the 
        // black dot.
        this.closeArea = function(marker) {
            GEvent.clearListeners(marker, 'click');
            this.points.push(this.points[0]);
            this.drawArea();
            this.zoom();
            GEvent.clearListeners(this.map, 'click');
        }

        // Zoom to selection
        this.zoom = function() {
            if (this.shape) {
                var bounds = this.shape.getBounds();
                this.map.setCenter(bounds.getCenter());
                this.map.setZoom(this.map.getBoundsZoomLevel(bounds));
            }
        }        
    },

    PictureList : function(params) {
        this.parameters = params.parameters;
        this.container = params.container;
        this.url = params.url;
        this.spinner = params.spinner;
        this.spinner_loading = params.spinner_loading;

        this.update = function(params) {
            Rodentia._spinner(this, 'loading');

            if (params.orientation) {
                this.parameters.orientation = params.orientation;
            } else {
                delete(this.parameters.orientation);
            }

            if (params.what) {
                this.parameters.what = params.what;
            } else {
                delete(this.parameters.what);
            }
            
            var container = $(this.container);
            var my = this;
            new Ajax.Updater(container, this.url, {
                parameters : this.parameters,
                method : 'get',
                onFailure : function(req) {
                    alert('Sorry, an error occured');
                },
                onComplete : function(req) {
                    Rodentia._spinner(my, 'visible');
                }
            })
        }
    }
}
