<commit_before>mol.modules.map.layers = function(mol) {

    mol.map.layers = {};

    mol.map.layers.LayerEngine = mol.mvp.Engine.extend(
        {
            init: function(proxy, bus, map) {
                this.proxy = proxy;
                this.bus = bus;
                this.map = map;
            },

            start: function() {
                this.display = new mol.map.layers.LayerListDisplay('.map_container');
                this.fireEvents();
                this.addEventHandlers();
				this.initSortable();
				this.display.toggle(false);
            },

            /**
             * Handler a layer-opacity event. This handler only does something
             * when the event.opacity is undefined. This is to support layer
             * toggling with opacity only (instead of removing overlays from
             * the map). In this case, the opacity from the layer widget is
             * bubbled to a new layer-opacity event that gets fired on the bus.
             */
            addEventHandlers: function() {
                var self = this;
                this.display.removeAll.click (
                    function(event) {

                        $(self.display).find(".close").trigger("click");
                    }
                );
                this.display.toggleAll.click (
                    function(event) {
                        _.each(
                            $(self.display).find(".toggle"),
                            function(checkbox){
                                    //$(checkbox).attr('checked', !$(checkbox).attr('checked'));
                                    checkbox.click({currentTarget : this})
                            }
                        );
                    }
                );
                this.bus.addHandler(
                    'layer-opacity',
                    function(event) {
                        var layer = event.layer,
                            l = self.display.getLayer(layer),
                            opacity = event.opacity,
                            params = {},
                            e = null;

                        if (opacity === undefined) {
                            params = {
                                layer: layer,
                                opacity: parseFloat(l.find('.opacity').slider("value"))
                            },
                            e = new mol.bus.Event('layer-opacity', params);
                            self.bus.fireEvent(e);
                        }
                    }
                );

                this.bus.addHandler(
                    'add-layers',
                    function(event) {
                        _.each(
                            event.layers,
                            function(layer) { // Removes duplicate layers.
                                if (self.display.getLayer(layer).length > 0) {
                                    event.layers = _.without(event.layers, layer);
                                }
                            }
                        );
                        self.addLayers(event.layers);
                    }
                );
                this.bus.addHandler(
                    'layer-display-toggle',
                    function(event) {
                        var params = null,
                        e = null;

                        if (event.visible === undefined) {
                            self.display.toggle();
                            params = {visible: self.display.is(':visible')};
                        } else {
                            self.display.toggle(event.visible);
                        }
                    }
                );
            },

            /**
             * Fires the 'add-map-control' event. The mol.map.MapEngine handles
             * this event and adds the display to the map.
             */
            fireEvents: function() {
                var params = {
                        display: this.display,
                        slot: mol.map.ControlDisplay.Slot.TOP,
                        position: google.maps.ControlPosition.TOP_RIGHT
                    },
                    event = new mol.bus.Event('add-map-control', params);

                this.bus.fireEvent(event);
            },

            /**
             * Sorts layers so that they're grouped by name. Within each named
             * group, they are sorted by type: points, protectedarea, range,
             * ecoregion.
             *
             * @layers array of layer objects {name, type}
             */
            sortLayers: function(layers) {
                var sorted = [],
                    names_map = {};

                _.sortBy( // Layer names sorted alphabetically.
                    _.each(layers,
                          function(layer) {
                              names_map[layer.name] = layer.name; // Gather unique names.
                          })
                );

                _.each(_.keys(names_map),
                       function(name) {
                           var group = _.groupBy(_.groupBy(layers, "name")[name], "type");

                           _.each(
                               ['points', 'protectedarea', 'range', 'ecoregion'],
                               function(type) {
                                   if (group[type]) {
                                       sorted.push(group[type][0]);
                                   }
                               }
                           );
                       });

                return sorted;

            },

            /**
             * Handler for layer opacity changes via UI. It fires a layer-opacity
             * event on the bus, passing in the layer object and its opacity.
             */
            opacityHandler: function(layer, l) {
                return function(event) {
                    var params = {},
                        e = null;

                    l.toggle.attr('checked', true);

                    params = {
                        layer: layer,
                        opacity: parseFloat(l.opacity.slider("value"))
                    },

                    e = new mol.bus.Event('layer-opacity', params);

                    self.bus.fireEvent(e);
                };
            },

            /**
             * Adds layer widgets to the map. The layers parameter is an array
             * of layer objects {id, name, type, source}.
             */

            addLayers: function(layers) {
                var all = [],
                    layerIds = [],
                    sortedLayers = this.sortLayers(layers);

                _.each(
                    sortedLayers,
                    function(layer) {
                        var l = this.display.addLayer(layer),
                            self = this,
                            opacity = null;

                        self.bus.fireEvent(new mol.bus.Event('show-layer-display-toggle'));

                        // Set initial opacity based on layer type.
                        switch (layer.type) {
                        case 'points':
                            opacity = 1.0;
                            break;
                        case 'ecoregion':
                            opacity = .25;
                            break;
                        case 'protectedarea':
                            opacity = 1.0;
                            break;
                        case 'range':
                            opacity = .5;
                            break;
                        }

                        //disable interactivity to start
                        self.map.overlayMapTypes.forEach(
                                    function(mt) {
                                        mt.interaction.remove();
                                        mt.interaction.clickAction = "";
                                    }
                        );

                        // Hack so that at the end we can fire opacity event with all layers.
                        all.push({layer:layer, l:l, opacity:opacity});

                        // Opacity slider change handler.
                        l.opacity.bind("slide",self.opacityHandler(layer, l));
                        l.opacity.slider("value",opacity);

                        // Close handler for x button fires a 'remove-layers' event.
                        l.close.click(
                            function(event) {
                                var params = {
                                      layers: [layer]
                                    },
                                    e = new mol.bus.Event('remove-layers', params);

                                self.bus.fireEvent(e);
                                l.remove();
                                // Hide the layer widge toggle in the main menu if no layers exist
                                if(self.map.overlayMapTypes.length == 0) {
                                    self.bus.fireEvent(new mol.bus.Event('hide-layer-display-toggle'));
                                    self.display.toggle(false);
                                }
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        );

                        // Click handler for zoom button fires 'layer-zoom-extent'
                        // and 'show-loading-indicator' events.
                        l.zoom.click(
                            function(event) {
                                var params = {
                                        layer: layer,
                                        auto_bound: true
                                    },
                                    e = new mol.bus.Event('layer-zoom-extent', params),
                                    le = new mol.bus.Event('show-loading-indicator',{source : "map"});

                                self.bus.fireEvent(e);
                                self.bus.fireEvent(le);
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        );
                        l.layer.click(
                            function(event) {
                                $(l.layer).focus();
                                if($(this).hasClass('selected')) {
                                    $(this).removeClass('selected');
                                } else {
                                    $(self.display).find('.selected').removeClass('selected');
                                    $(this).addClass('selected');
                                }

                                self.map.overlayMapTypes.forEach(
                                    function(mt) {
                                        if(mt.name == layer.id && $(l.layer).hasClass('selected')) {
                                            mt.interaction.add();
                                            mt.interaction.clickAction = "full"
                                        } else {
                                            mt.interaction.remove();
                                            mt.interaction.clickAction = "";
                                        }
                                    }
                                )
                                event.stopPropagation();
                                event.cancelBubble = true;

                            }
                        );

                        // Click handler for info button fires 'metadata-toggle'
                        l.info.click(
                            function(event) {
                                self.bus.fireEvent(new mol.bus.Event('metadata-toggle', {params : { layer: layer}}));
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        );
                        l.toggle.attr('checked', true);

                        // Click handler for the toggle button.
                        l.toggle.click(
                            function(event) {
                                var showing = $(event.currentTarget).is(':checked'),
                                    params = {
                                        layer: layer,
                                        showing: showing
                                    },
                                    e = new mol.bus.Event('layer-toggle', params);

                                self.bus.fireEvent(e);
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        );
                        l.source.click(
                            function(event) {
                                self.bus.fireEvent(new mol.bus.Event('metadata-toggle', {params : { provider: layer.source, type: layer.type, _class: layer._class}}));
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        );
                        l.type.click(
                            function(event) {
                                self.bus.fireEvent(new mol.bus.Event('metadata-toggle', {params : { type: layer.type}}));
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        )
                        self.display.toggle(true);

                    },
                    this
                );

                // All of this stuff ensures layer orders are correct on map.
                layerIds = _.map(
                    sortedLayers,
                    function(layer) {
                        return layer.id;
                    },
                    this);
                this.bus.fireEvent(new mol.bus.Event('reorder-layers', {layers:layerIds}));

                // And this stuff ensures correct initial layer opacities on the map.
                _.each(
                    all.reverse(), // Reverse so that layers on top get rendered on top.
                    function(item) {
                        this.opacityHandler(item.layer, item.l)();
                    },
                    this
                );
            },

			   /**
			    * Add sorting capability to LayerListDisplay, when a result is
             * drag-n-drop, and the order of the result list is changed,
             * then the map will re-render according to the result list's order.
			    **/
			   initSortable: function() {
				    var self = this,
					     display = this.display;

				    display.list.sortable(
                    {
					        update: function(event, ui) {
						          var layers = [],
						          params = {},
                            e = null;

						          $(display.list).find('.layerContainer').each(
                                function(i, el) {
							               layers.push($(el).attr('id'));
						              }
                            );

                            params.layers = layers;
						          e = new mol.bus.Event('reorder-layers', params);
						          self.bus.fireEvent(e);
					         }
				        }
                    );

			   }
        }
    );

    mol.map.layers.LayerDisplay = mol.mvp.View.extend(
        {
            init: function(layer) {
                var html = '' +
                    '
' +
                    '
' +
                    '    <button class="source" title="Layer Source: {5}"></button>' +
                    '    <button class="type" title="Layer Type: {6}"></button>' +
                    '
' +
                    '
{4} features
' +
                    '
{2}
' +
                    '
{3}
' +
                    '
' +
                    '    <input class="keycatcher" type="text" />' +
                    '    <button title="Remove layer." class="close">x</button>' +
                    '    <button title="Zoom to layer extent." class="zoom">z</button>' +
                    '    <button title="Layer metadata info." class="info">i</button>' +
                    '    <input class="toggle" type="checkbox">' +
                    '

' +
                    '
' +
                    '

' +
                    '
';

                this._super(html.format(layer.source, layer.type, layer.name, layer.names, layer.feature_count, layer.source_title, layer.type_title));
                this.attr('id', layer.id);
                this.opacity = $(this).find('.opacity').slider({value: 0.5, min: 0, max:1, step: 0.02, animate:"slow"});
                this.toggle = $(this).find('.toggle').button();
                this.zoom = $(this).find('.zoom');
                this.info = $(this).find('.info');
                this.close = $(this).find('.close');
                this.type = $(this).find('.type');
                this.source = $(this).find('.source');
                this.layer = $(this).find('.layer');
                this.keycatcher = $(this).find('.keycatcher');
                this.layerObj = layer;

            }
        }
    );

    mol.map.layers.LayerListDisplay = mol.mvp.View.extend(
        {
            init: function() {
                var html = '' +
                    '
' +
                    '
' +
                    '
' +
                    '           Layers ' +
                   /* '           none' +
                    '           all' +*/
                    '
' +
                    '
' +
                    '
' +
                    '
' +
                    '
' +
                    '
' +
                    '           <button class="removeAll">Remove All Layers</button>' +
                    '           <button class="toggleAll">Toggle All Layers</button>' +
                    '
' +
                    '
' +
                    '
';

                this._super(html);
                this.list = $(this).find("#sortable");
                this.removeAll = $(this).find(".removeAll");
                this.toggleAll = $(this).find(".toggleAll");
                this.open = false;
                this.views = {};
                this.layers = [];

            },

            getLayer: function(layer) {
                return $(this).find('#{0}'.format(layer.id));
            },

			   getLayerById: function(id) {
				    return _.find(this.layers, function(layer){ return layer.id === id; });
			   },

            addLayer: function(layer) {
                var ld = new mol.map.layers.LayerDisplay(layer);
                this.list.append(ld);
				this.layers.push(layer);
                return ld;
            },

            render: function(howmany, order) {
				    var self = this;
                this.updateLayerNumber();
                return this;
            },

            updateLayerNumber: function() {
                var t = 0;
                _(this.layers).each(function(a) {
					if(a.enabled) t++;
				});
                $(this).find('.layer_number').html(t + " LAYER"+ (t>1?'S':''));
            },

            sortLayers: function() {
                var order = [];
                $(this).find('.layerContainer').each(function(i, el) {
					order.push($(el).attr('id'));
				});
                this.bus.emit("map:reorder_layers", order);
            },

            open: function(e) {
                if(e) e.preventDefault();
                this.el.addClass('open');
                this.el.css("z-index","100");
                this.open = true;
            },

            close: function(e) {
                this.el.removeClass('open');
                this.el.css("z-index","10");
                this.open = false;
            },

            sort_by: function(layers_order) {
                this.layers.sort(function(a, b) {
                                     return _(layers_order).indexOf(a.name) -
                                         _(layers_order).indexOf(b.name);
                                 });
                this.open = true;
                this.hiding();
            },

            hiding: function(e) {
                var layers = null;

                if (!this.open) {
                    return;
                }

                // put first what are showing
                this.layers.sort(
                    function(a, b) {
                        if (a.enabled && !b.enabled) {
                            return -1;
                        } else if (!a.enabled && b.enabled) {
                            return 1;
                        }
                        return 0;
                    }
                );
                layers = _(this.layers).pluck('name');
                this.bus.emit("map:reorder_layers", layers);
                this.order = layers;
                this.render(3);
                this.close();
            }
        }
    );
};
<commit_msg>Turning off the "i" button.<commit_after>mol.modules.map.layers = function(mol) {

    mol.map.layers = {};

    mol.map.layers.LayerEngine = mol.mvp.Engine.extend(
        {
            init: function(proxy, bus, map) {
                this.proxy = proxy;
                this.bus = bus;
                this.map = map;
            },

            start: function() {
                this.display = new mol.map.layers.LayerListDisplay('.map_container');
                this.fireEvents();
                this.addEventHandlers();
				this.initSortable();
				this.display.toggle(false);
            },

            /**
             * Handler a layer-opacity event. This handler only does something
             * when the event.opacity is undefined. This is to support layer
             * toggling with opacity only (instead of removing overlays from
             * the map). In this case, the opacity from the layer widget is
             * bubbled to a new layer-opacity event that gets fired on the bus.
             */
            addEventHandlers: function() {
                var self = this;
                this.display.removeAll.click (
                    function(event) {

                        $(self.display).find(".close").trigger("click");
                    }
                );
                this.display.toggleAll.click (
                    function(event) {
                        _.each(
                            $(self.display).find(".toggle"),
                            function(checkbox){
                                    //$(checkbox).attr('checked', !$(checkbox).attr('checked'));
                                    checkbox.click({currentTarget : this})
                            }
                        );
                    }
                );
                this.bus.addHandler(
                    'layer-opacity',
                    function(event) {
                        var layer = event.layer,
                            l = self.display.getLayer(layer),
                            opacity = event.opacity,
                            params = {},
                            e = null;

                        if (opacity === undefined) {
                            params = {
                                layer: layer,
                                opacity: parseFloat(l.find('.opacity').slider("value"))
                            },
                            e = new mol.bus.Event('layer-opacity', params);
                            self.bus.fireEvent(e);
                        }
                    }
                );

                this.bus.addHandler(
                    'add-layers',
                    function(event) {
                        _.each(
                            event.layers,
                            function(layer) { // Removes duplicate layers.
                                if (self.display.getLayer(layer).length > 0) {
                                    event.layers = _.without(event.layers, layer);
                                }
                            }
                        );
                        self.addLayers(event.layers);
                    }
                );
                this.bus.addHandler(
                    'layer-display-toggle',
                    function(event) {
                        var params = null,
                        e = null;

                        if (event.visible === undefined) {
                            self.display.toggle();
                            params = {visible: self.display.is(':visible')};
                        } else {
                            self.display.toggle(event.visible);
                        }
                    }
                );
            },

            /**
             * Fires the 'add-map-control' event. The mol.map.MapEngine handles
             * this event and adds the display to the map.
             */
            fireEvents: function() {
                var params = {
                        display: this.display,
                        slot: mol.map.ControlDisplay.Slot.TOP,
                        position: google.maps.ControlPosition.TOP_RIGHT
                    },
                    event = new mol.bus.Event('add-map-control', params);

                this.bus.fireEvent(event);
            },

            /**
             * Sorts layers so that they're grouped by name. Within each named
             * group, they are sorted by type: points, protectedarea, range,
             * ecoregion.
             *
             * @layers array of layer objects {name, type}
             */
            sortLayers: function(layers) {
                var sorted = [],
                    names_map = {};

                _.sortBy( // Layer names sorted alphabetically.
                    _.each(layers,
                          function(layer) {
                              names_map[layer.name] = layer.name; // Gather unique names.
                          })
                );

                _.each(_.keys(names_map),
                       function(name) {
                           var group = _.groupBy(_.groupBy(layers, "name")[name], "type");

                           _.each(
                               ['points', 'protectedarea', 'range', 'ecoregion'],
                               function(type) {
                                   if (group[type]) {
                                       sorted.push(group[type][0]);
                                   }
                               }
                           );
                       });

                return sorted;

            },

            /**
             * Handler for layer opacity changes via UI. It fires a layer-opacity
             * event on the bus, passing in the layer object and its opacity.
             */
            opacityHandler: function(layer, l) {
                return function(event) {
                    var params = {},
                        e = null;

                    l.toggle.attr('checked', true);

                    params = {
                        layer: layer,
                        opacity: parseFloat(l.opacity.slider("value"))
                    },

                    e = new mol.bus.Event('layer-opacity', params);

                    self.bus.fireEvent(e);
                };
            },

            /**
             * Adds layer widgets to the map. The layers parameter is an array
             * of layer objects {id, name, type, source}.
             */

            addLayers: function(layers) {
                var all = [],
                    layerIds = [],
                    sortedLayers = this.sortLayers(layers);

                _.each(
                    sortedLayers,
                    function(layer) {
                        var l = this.display.addLayer(layer),
                            self = this,
                            opacity = null;

                        self.bus.fireEvent(new mol.bus.Event('show-layer-display-toggle'));

                        // Set initial opacity based on layer type.
                        switch (layer.type) {
                        case 'points':
                            opacity = 1.0;
                            break;
                        case 'ecoregion':
                            opacity = .25;
                            break;
                        case 'protectedarea':
                            opacity = 1.0;
                            break;
                        case 'range':
                            opacity = .5;
                            break;
                        }

                        //disable interactivity to start
                        self.map.overlayMapTypes.forEach(
                                    function(mt) {
                                        mt.interaction.remove();
                                        mt.interaction.clickAction = "";
                                    }
                        );

                        // Hack so that at the end we can fire opacity event with all layers.
                        all.push({layer:layer, l:l, opacity:opacity});

                        // Opacity slider change handler.
                        l.opacity.bind("slide",self.opacityHandler(layer, l));
                        l.opacity.slider("value",opacity);

                        // Close handler for x button fires a 'remove-layers' event.
                        l.close.click(
                            function(event) {
                                var params = {
                                      layers: [layer]
                                    },
                                    e = new mol.bus.Event('remove-layers', params);

                                self.bus.fireEvent(e);
                                l.remove();
                                // Hide the layer widge toggle in the main menu if no layers exist
                                if(self.map.overlayMapTypes.length == 0) {
                                    self.bus.fireEvent(new mol.bus.Event('hide-layer-display-toggle'));
                                    self.display.toggle(false);
                                }
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        );

                        // Click handler for zoom button fires 'layer-zoom-extent'
                        // and 'show-loading-indicator' events.
                        l.zoom.click(
                            function(event) {
                                var params = {
                                        layer: layer,
                                        auto_bound: true
                                    },
                                    e = new mol.bus.Event('layer-zoom-extent', params),
                                    le = new mol.bus.Event('show-loading-indicator',{source : "map"});

                                self.bus.fireEvent(e);
                                self.bus.fireEvent(le);
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        );
                        l.layer.click(
                            function(event) {
                                $(l.layer).focus();
                                if($(this).hasClass('selected')) {
                                    $(this).removeClass('selected');
                                } else {
                                    $(self.display).find('.selected').removeClass('selected');
                                    $(this).addClass('selected');
                                }

                                self.map.overlayMapTypes.forEach(
                                    function(mt) {
                                        if(mt.name == layer.id && $(l.layer).hasClass('selected')) {
                                            mt.interaction.add();
                                            mt.interaction.clickAction = "full"
                                        } else {
                                            mt.interaction.remove();
                                            mt.interaction.clickAction = "";
                                        }
                                    }
                                )
                                event.stopPropagation();
                                event.cancelBubble = true;

                            }
                        );

                        // Click handler for info button fires 'metadata-toggle'
                        l.info.click(
                            function(event) {
                                self.bus.fireEvent(new mol.bus.Event('metadata-toggle', {params : { layer: layer}}));
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        );
                        l.toggle.attr('checked', true);

                        // Click handler for the toggle button.
                        l.toggle.click(
                            function(event) {
                                var showing = $(event.currentTarget).is(':checked'),
                                    params = {
                                        layer: layer,
                                        showing: showing
                                    },
                                    e = new mol.bus.Event('layer-toggle', params);

                                self.bus.fireEvent(e);
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        );
                        l.source.click(
                            function(event) {
                                self.bus.fireEvent(new mol.bus.Event('metadata-toggle', {params : { provider: layer.source, type: layer.type, _class: layer._class}}));
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        );
                        l.type.click(
                            function(event) {
                                self.bus.fireEvent(new mol.bus.Event('metadata-toggle', {params : { type: layer.type}}));
                                event.stopPropagation();
                                event.cancelBubble = true;
                            }
                        )
                        self.display.toggle(true);

                    },
                    this
                );

                // All of this stuff ensures layer orders are correct on map.
                layerIds = _.map(
                    sortedLayers,
                    function(layer) {
                        return layer.id;
                    },
                    this);
                this.bus.fireEvent(new mol.bus.Event('reorder-layers', {layers:layerIds}));

                // And this stuff ensures correct initial layer opacities on the map.
                _.each(
                    all.reverse(), // Reverse so that layers on top get rendered on top.
                    function(item) {
                        this.opacityHandler(item.layer, item.l)();
                    },
                    this
                );
            },

			   /**
			    * Add sorting capability to LayerListDisplay, when a result is
             * drag-n-drop, and the order of the result list is changed,
             * then the map will re-render according to the result list's order.
			    **/
			   initSortable: function() {
				    var self = this,
					     display = this.display;

				    display.list.sortable(
                    {
					        update: function(event, ui) {
						          var layers = [],
						          params = {},
                            e = null;

						          $(display.list).find('.layerContainer').each(
                                function(i, el) {
							               layers.push($(el).attr('id'));
						              }
                            );

                            params.layers = layers;
						          e = new mol.bus.Event('reorder-layers', params);
						          self.bus.fireEvent(e);
					         }
				        }
                    );

			   }
        }
    );

    mol.map.layers.LayerDisplay = mol.mvp.View.extend(
        {
            init: function(layer) {
                var html = '' +
                    '
' +
                    '
' +
                    '    <button class="source" title="Layer Source: {5}"></button>' +
                    '    <button class="type" title="Layer Type: {6}"></button>' +
                    '
' +
                    '
{4} features
' +
                    '
{2}
' +
                    '
{3}
' +
                    '
' +
                    '    <input class="keycatcher" type="text" />' +
                    '    <button title="Remove layer." class="close">x</button>' +
                    '    <button title="Zoom to layer extent." class="zoom">z</button>' +
                    //'    <button title="Layer metadata info." class="info">i</button>' +
                    '    <input class="toggle" type="checkbox">' +
                    '

' +
                    '
' +
                    '

' +
                    '
';

                this._super(html.format(layer.source, layer.type, layer.name, layer.names, layer.feature_count, layer.source_title, layer.type_title));
                this.attr('id', layer.id);
                this.opacity = $(this).find('.opacity').slider({value: 0.5, min: 0, max:1, step: 0.02, animate:"slow"});
                this.toggle = $(this).find('.toggle').button();
                this.zoom = $(this).find('.zoom');
                this.info = $(this).find('.info');
                this.close = $(this).find('.close');
                this.type = $(this).find('.type');
                this.source = $(this).find('.source');
                this.layer = $(this).find('.layer');
                this.keycatcher = $(this).find('.keycatcher');
                this.layerObj = layer;

            }
        }
    );

    mol.map.layers.LayerListDisplay = mol.mvp.View.extend(
        {
            init: function() {
                var html = '' +
                    '
' +
                    '
' +
                    '
' +
                    '           Layers ' +
                   /* '           none' +
                    '           all' +*/
                    '
' +
                    '
' +
                    '
' +
                    '
' +
                    '
' +
                    '
' +
                    '           <button class="removeAll">Remove All Layers</button>' +
                    '           <button class="toggleAll">Toggle All Layers</button>' +
                    '
' +
                    '
' +
                    '
';

                this._super(html);
                this.list = $(this).find("#sortable");
                this.removeAll = $(this).find(".removeAll");
                this.toggleAll = $(this).find(".toggleAll");
                this.open = false;
                this.views = {};
                this.layers = [];

            },

            getLayer: function(layer) {
                return $(this).find('#{0}'.format(layer.id));
            },

			   getLayerById: function(id) {
				    return _.find(this.layers, function(layer){ return layer.id === id; });
			   },

            addLayer: function(layer) {
                var ld = new mol.map.layers.LayerDisplay(layer);
                this.list.append(ld);
				this.layers.push(layer);
                return ld;
            },

            render: function(howmany, order) {
				    var self = this;
                this.updateLayerNumber();
                return this;
            },

            updateLayerNumber: function() {
                var t = 0;
                _(this.layers).each(function(a) {
					if(a.enabled) t++;
				});
                $(this).find('.layer_number').html(t + " LAYER"+ (t>1?'S':''));
            },

            sortLayers: function() {
                var order = [];
                $(this).find('.layerContainer').each(function(i, el) {
					order.push($(el).attr('id'));
				});
                this.bus.emit("map:reorder_layers", order);
            },

            open: function(e) {
                if(e) e.preventDefault();
                this.el.addClass('open');
                this.el.css("z-index","100");
                this.open = true;
            },

            close: function(e) {
                this.el.removeClass('open');
                this.el.css("z-index","10");
                this.open = false;
            },

            sort_by: function(layers_order) {
                this.layers.sort(function(a, b) {
                                     return _(layers_order).indexOf(a.name) -
                                         _(layers_order).indexOf(b.name);
                                 });
                this.open = true;
                this.hiding();
            },

            hiding: function(e) {
                var layers = null;

                if (!this.open) {
                    return;
                }

                // put first what are showing
                this.layers.sort(
                    function(a, b) {
                        if (a.enabled && !b.enabled) {
                            return -1;
                        } else if (!a.enabled && b.enabled) {
                            return 1;
                        }
                        return 0;
                    }
                );
                layers = _(this.layers).pluck('name');
                this.bus.emit("map:reorder_layers", layers);
                this.order = layers;
                this.render(3);
                this.close();
            }
        }
    );
};
<|endoftext|>


<commit_before>'use strict'

import { Component }   from 'react'
import { Link }        from 'react-router'
import DocumentTitle   from 'react-document-title'
import { decodeToken } from 'jsontokens'
import {
  makeAuthResponse, makeECPrivateKey, Person,
  getAuthRequestFromURL, redirectUserToApp, fetchAppManifest
} from 'blockstack'
import queryString from 'query-string'

import Header          from '../components/Header'
import Footer          from '../components/Footer'

class AuthPage extends Component {

  constructor(props) {
    super(props)

    this.state = {
      authRequest: null,
      appURI: null
    }

    this.signIn = this.signIn.bind(this)
    this.getAuthRequest = this.getAuthRequest.bind(this)
  }

  componentWillMount() {
    this.getAuthRequest()
  }

  getAuthRequest() {
    const authRequest = getAuthRequestFromURL()
    fetchAppManifest(authRequest).then(appManifest => {
      this.setState({
        authRequest: authRequest,
        appManifest: appManifest
      })
    }).catch((e) => {
      console.log(e.stack)
    })
  }

  signIn() {
    const privateKey = makeECPrivateKey()
    const profile = {
      '@type': 'Person',
      'name': 'Anonymous',
      'image': [
        {
          '@type': 'ImageObject',
          'name': 'avatar',
          'contentUrl': 'https://s3.amazonaws.com/onename/avatar-placeholder.png'
        }
      ]
    }
    const authResponse = makeAuthResponse(privateKey, profile)
    redirectUserToApp(this.state.authRequest, authResponse)
  }

  render() {
    return (
      <DocumentTitle title="Sign In with Blockstack">

                  Sign in with Blockstack

                    You're here because you clicked "Sign in with
                    Blockstack" and you don't have the Blockstack app.

                    <Link to="/download" className="btn btn-outline-primary">
                      Download Blockstack
                    </Link>

                    - or -

                    <Link className="btn btn-outline-primary"
                          onClick={this.signIn}>
                      Quick Sign In
                    </Link>

                    Note: If you already have Blockstack,
                    go to your settings page in the app and
                    enable the auth protocol handler,
                    then go to the app and try signing in again.

      </DocumentTitle>
    )
  }

}

export default AuthPage<commit_msg>fix quick sign in<commit_after>'use strict'

import { Component }   from 'react'
import { Link }        from 'react-router'
import DocumentTitle   from 'react-document-title'
import { decodeToken, SECP256K1Client, TokenSigner } from 'jsontokens'
import {
  makeAuthResponse, makeECPrivateKey, Person,
  getAuthRequestFromURL, redirectUserToApp, fetchAppManifest,
  publicKeyToAddress, makeDIDFromAddress, makeUUID4, nextMonth
} from 'blockstack'
import queryString from 'query-string'
import { ECPair, address as baddress, crypto as bcrypto } from 'bitcoinjs-lib'

import Header          from '../components/Header'
import Footer          from '../components/Footer'

class AuthPage extends Component {

  constructor(props) {
    super(props)

    this.state = {
      authRequest: null,
      appURI: null
    }

    this.signIn = this.signIn.bind(this)
    this.getAuthRequest = this.getAuthRequest.bind(this)
  }

  componentWillMount() {
    this.getAuthRequest()
  }

  getAuthRequest() {
    const authRequest = getAuthRequestFromURL()
    this.setState({
      authRequest: authRequest
    })

    /*
    fetchAppManifest(authRequest).then(appManifest => {

    }).catch((e) => {
      console.log(e.stack)
    })*/
  }

  signIn() {
    const privateKey = makeECPrivateKey()
    const profile = {
      '@type': 'Person',
      'name': 'Anonymous',
      'image': [
        {
          '@type': 'ImageObject',
          'name': 'avatar',
          'contentUrl': 'https://s3.amazonaws.com/onename/avatar-placeholder.png'
        }
      ]
    }
    const publicKey = SECP256K1Client.derivePublicKey(privateKey)
    const publicKeyBuffer = new Buffer(publicKey, 'hex')
    const address = baddress.toBase58Check(bcrypto.hash160(publicKeyBuffer), 0x00)
    const payload = {
      jti: makeUUID4(),
      iat: Math.floor(new Date().getTime()/1000), // JWT times are in seconds
      exp: Math.floor(nextMonth().getTime()/1000), // JWT times are in seconds
      iss: makeDIDFromAddress(address),
      public_keys: [publicKey],
      profile: profile,
      username: null
    }
    const tokenSigner = new TokenSigner('ES256k', privateKey)
    const authResponse = tokenSigner.sign(payload)
    redirectUserToApp(this.state.authRequest, authResponse)
  }

  render() {
    return (
      <DocumentTitle title="Sign In with Blockstack">

                  Sign in with Blockstack

                    You're here because you clicked "Sign in with
                    Blockstack" and you don't have the Blockstack app.

                    <Link to="/download" className="btn btn-outline-primary">
                      Download Blockstack
                    </Link>

                    - or -

                    <Link className="btn btn-outline-primary"
                          onClick={this.signIn}>
                      Quick Sign In
                    </Link>

                    Note: If you already have Blockstack,
                    go to your settings page in Blockstack and
                    enable the auth protocol handler,
                    then go to the app and try signing in again.

      </DocumentTitle>
    )
  }

}

export default AuthPage<|endoftext|>


<commit_before>$(document).ready(function(){
    // feature detection for drag&drop upload
    var isAdvancedUpload = function()
    {
        var div = document.createElement( 'div' );
        return ( ( 'draggable' in div ) || ( 'ondragstart' in div && 'ondrop' in div ) ) && 'FormData' in window && 'FileReader' in window;
    }();

    var $dropZones =  $('.integrated-dropzone');

    $.each($dropZones, function() {
        var $dropZone = $(this);

        // drag&drop files if the feature is available
        if( isAdvancedUpload )
        {

            $dropZone.addClass( 'has-advanced-upload' ) // letting the CSS part to know drag&drop is supported by the browser
                .on( 'drag dragstart dragend dragover dragenter dragleave drop', function( e )
                {
                    // preventing the unwanted behaviours
                    e.preventDefault();
                    e.stopPropagation();
                })
                .on( 'dragover dragenter', function() //
                {
                    $dropZone.addClass( 'is-dragover' );
                })
                .on( 'dragleave dragend drop', function()
                {
                    $dropZone.removeClass( 'is-dragover' );
                })
                .on( 'drop', function( e )
                {
                    $("input[type='file']", $dropZone).prop("files",  e.originalEvent.dataTransfer.files); // the files that were dropped
                    $("input[type='file']", $dropZone).trigger('change');
                });
        }

        var filer_default_opts = {
            showThumbs: true,
            limit: 1,
            appendTo: ".integrated-dropzone",
            afterRender: function (item, box) {
                if (this.files && this.files.length) {
                    $(box).hide();
                }
            },
            afterShow: function (item, box, a) {
                $(box).hide();
                $dropZone.find('.remove-file').val(0);
            },
            onEmpty: function (box) {
                $(box).show();
                $dropZone.find('.remove-file').val(1);
            },
            theme: "dragdropbox",
            templates: {
                box: '

',
                item: '
\

\

\

\

\

\

\

\
												{{fi-name}}\
												{{fi-size2}}\

\

\

\
									{{fi-image}}\

\

\

\

\

\

\

\

\

',
                itemAppend: '
\

\

\

\

\

\

\

\
        												{{fi-name}}\

\

\

\
                                            {{fi-image}}\

\

\

\

\

\

\

\

\

\

',
                canvasImage: true,
                removeConfirmation: true,
                _selectors: {
                    list: '.jFiler-items-list',
                    item: '.jFiler-item',
                    progressBar: '.bar',
                    remove: '.jFiler-item-trash-action'
                }
            }
        };

        //merge default options with options passed in view
        $.extend(filer_default_opts, $dropZone.data('options'));

        $('input[type="file"]', $dropZone).filer(filer_default_opts);

        //validate form
        $dropZone.closest('form').submit(function () {
            if ($dropZone.hasClass('required') && $dropZone.find('.dropzone-empty').is(':visible')) {
                $dropZone.addClass('empty-error');
                $('html, body').animate({
                    scrollTop: $dropZone.offset().top
                }, 1000);
                return false;
            }
        });
    });
});
<commit_msg>Auto fill title on file upload<commit_after>$(document).ready(function(){
    // feature detection for drag&drop upload
    var isAdvancedUpload = function()
    {
        var div = document.createElement( 'div' );
        return ( ( 'draggable' in div ) || ( 'ondragstart' in div && 'ondrop' in div ) ) && 'FormData' in window && 'FileReader' in window;
    }();

    var $dropZones =  $('.integrated-dropzone');

    $.each($dropZones, function() {
        var $dropZone = $(this);

        // drag&drop files if the feature is available
        if( isAdvancedUpload )
        {

            $dropZone.addClass( 'has-advanced-upload' ) // letting the CSS part to know drag&drop is supported by the browser
                .on( 'drag dragstart dragend dragover dragenter dragleave drop', function( e )
                {
                    // preventing the unwanted behaviours
                    e.preventDefault();
                    e.stopPropagation();
                })
                .on( 'dragover dragenter', function() //
                {
                    $dropZone.addClass( 'is-dragover' );
                })
                .on( 'dragleave dragend drop', function()
                {
                    $dropZone.removeClass( 'is-dragover' );
                })
                .on( 'drop', function( e )
                {
                    $("input[type='file']", $dropZone).prop("files",  e.originalEvent.dataTransfer.files); // the files that were dropped
                    $("input[type='file']", $dropZone).trigger('change');
                });
        }

        var filer_default_opts = {
            showThumbs: true,
            limit: 1,
            appendTo: ".integrated-dropzone",
            afterRender: function (item, box) {
                if (this.files && this.files.length) {
                    $(box).hide();
                }
            },
            afterShow: function (item, box, a) {
                $(box).hide();
                $dropZone.find('.remove-file').val(0);
            },
            onSelect: function (item) {
                if ($('#integrated_content_title').val() == '') {
                    $('#integrated_content_title').val(item.name.split('.').slice(0, -1).join('.'));
                }
            },
            onEmpty: function (box) {
                $(box).show();
                $dropZone.find('.remove-file').val(1);
            },
            theme: "dragdropbox",
            templates: {
                box: '

',
                item: '
\

\

\

\

\

\

\

\
												{{fi-name}}\
												{{fi-size2}}\

\

\

\
									{{fi-image}}\

\

\

\

\

\

\

\

\

',
                itemAppend: '
\

\

\

\

\

\

\

\
        												{{fi-name}}\

\

\

\
                                            {{fi-image}}\

\

\

\

\

\

\

\

\

\

',
                canvasImage: true,
                removeConfirmation: true,
                _selectors: {
                    list: '.jFiler-items-list',
                    item: '.jFiler-item',
                    progressBar: '.bar',
                    remove: '.jFiler-item-trash-action'
                }
            }
        };

        //merge default options with options passed in view
        $.extend(filer_default_opts, $dropZone.data('options'));

        $('input[type="file"]', $dropZone).filer(filer_default_opts);

        //validate form
        $dropZone.closest('form').submit(function () {
            if ($dropZone.hasClass('required') && $dropZone.find('.dropzone-empty').is(':visible')) {
                $dropZone.addClass('empty-error');
                $('html, body').animate({
                    scrollTop: $dropZone.offset().top
                }, 1000);
                return false;
            }
        });
    });
});
<|endoftext|>


<commit_before>module.exports = {
    getCellHash: function(cell) {
        return cell.get('x') + ':' + cell.get('y');
    }
}
<commit_msg>Remove Utils mixin<commit_after><|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Mixin.create({

    _filters: Ember.inject.service('filters'),

    appliedFilterKeys: Ember.computed('filterKeys', function () {
       const filterKeys = this.get('filterKeys') || '';
       return filterKeys.split(',').invoke('trim');
    }),

    filters: Ember.computed('_filters.items.[]', '_filterArray', function () {
        const _filterArray = this.get('appliedFilterKeys');
        const _filters = this.get('_filters.items').filter(item => {
            return _filterArray.indexOf(item.key) !== -1;
        });

        let filterObject = {};
        _filters.forEach(filter => {
            filterObject[filter.key] = filter.value;
        });

        return filterObject;
    })
});
<commit_msg>Update filterable to cohere with linter<commit_after>import Ember from 'ember';

export default Ember.Mixin.create({

    _filters: Ember.inject.service('filters'),

    appliedFilterKeys: Ember.computed('filterKeys', function () {
        const filterKeys = this.get('filterKeys') || '';
        return filterKeys.split(',').invoke('trim');
    }),

    filters: Ember.computed('_filters.items.[]', '_filterArray', function () {
        const _filterArray = this.get('appliedFilterKeys');
        const _filters = this.get('_filters.items').filter(item => {
            return _filterArray.indexOf(item.key) !== -1;
        });

        let filterObject = {};
        _filters.forEach(filter => {
            filterObject[filter.key] = filter.value;
        });

        return filterObject;
    }),
});
<|endoftext|>


<commit_before>var application = require("application");
application.start({ moduleName: "views/create-activity/create-activity" });
<commit_msg>oops lol<commit_after>var application = require("application");
application.start({ moduleName: "views/login/login" });
<|endoftext|>


<commit_before>{}
<commit_msg>remove sprockets config file since we'll probably just remove sprockets<commit_after><|endoftext|>


<commit_before>/* global define */

define([
  'underscore',
  'backbone',
  'moment',
  'models/base-model',
  'models/user-model',
  'models/settings-model',
  'models/term-model'
], function (_, Backbone, moment, BaseModel, User, Settings, Term) {
  'use strict';

  var Post = BaseModel.extend({
    defaults: {
      ID             : null,
      title          : '',
      status         : 'draft',
      type           : 'post',
      parent         : 0,
      author         : new User(),
      content        : '',
      link           : '',
      date           : new Date(),
      date_gmt       : new Date(),
      modified       : new Date(),
      format         : 'standard',
      slug           : '',
      guid           : '',
      excerpt        : '',
      menu_order     : 0,
      comment_status : 'open',
      ping_status    : 'open',
      sticky         : false,
      date_tz        : 'Etc/UTC',
      modified_tz    : 'Etc/UTC',
      featured_image : null,
      terms          : {},
      post_meta      : {},
      meta           : {}
    },

    idAttribute: 'ID',
    urlRoot: Settings.get('api_url') + '/posts',

    url: function () {
      var query = '';

      if (this.get('ID')) {
        query = '/' + this.get('ID');
      } else {
        query = '/b3:slug:' + this.get('slug');
      }

      return this.urlRoot + query;
    },

    getTerm: function (taxonomy, term) {
      var terms = this.get('terms')[taxonomy],
          index = _.findIndex(terms, function (elem) {
            return elem.slug === term;
          });

      return index > -1 ? new Term(terms[index]) : new Term();
    },

    getFormattedDate: function () {
      var date = moment(this.get('date'));
      return {
        year:     date.year(),
        monthnum: date.months() + 1,
        day:      date.date()
      };
    }
  });

  return Post;
});
<commit_msg>Partial match slug instead of total match<commit_after>/* global define */

define([
  'underscore',
  'backbone',
  'moment',
  'models/base-model',
  'models/user-model',
  'models/settings-model',
  'models/term-model'
], function (_, Backbone, moment, BaseModel, User, Settings, Term) {
  'use strict';

  var Post = BaseModel.extend({
    defaults: {
      ID             : null,
      title          : '',
      status         : 'draft',
      type           : 'post',
      parent         : 0,
      author         : new User(),
      content        : '',
      link           : '',
      date           : new Date(),
      date_gmt       : new Date(),
      modified       : new Date(),
      format         : 'standard',
      slug           : '',
      guid           : '',
      excerpt        : '',
      menu_order     : 0,
      comment_status : 'open',
      ping_status    : 'open',
      sticky         : false,
      date_tz        : 'Etc/UTC',
      modified_tz    : 'Etc/UTC',
      featured_image : null,
      terms          : {},
      post_meta      : {},
      meta           : {}
    },

    idAttribute: 'ID',
    urlRoot: Settings.get('api_url') + '/posts',

    url: function () {
      var query = '';

      if (this.get('ID')) {
        query = '/' + this.get('ID');
      } else {
        query = '/b3:slug:' + this.get('slug');
      }

      return this.urlRoot + query;
    },

    getTerm: function (taxonomy, term) {
      var terms = this.get('terms')[taxonomy],
          index = _.findIndex(terms, function (elem) {
            return term.indexOf(elem.slug) > -1;
          });

      return index > -1 ? new Term(terms[index]) : new Term();
    },

    getFormattedDate: function () {
      var date = moment(this.get('date'));
      return {
        year:     date.year(),
        monthnum: date.months() + 1,
        day:      date.date()
      };
    }
  });

  return Post;
});
<|endoftext|>


<commit_before>define([
    'views/widgets/TopBar',
    'views/widgets/Tabulator',
    'views/widgets/TabulatorButton',
    'models/TabulatorModel',
    'views/widgets/icd/IcdCodePane',
    'views/widgets/icd-years/IcdYearsPane',
    'views/widgets/chop/ChopCodePane',
    'views/widgets/drg/DrgCodePane',
    'views/widgets/top3/Top3DiagnosesPane'
], function(
    TopBar,
    Tabulator,
    TabulatorButton,
    TabulatorModel,
    IcdCodePane,
    IcdYearsPane,
    ChopCodePane,
    DrgCodePane,
    Top3DiagnosesPane
){

    "use strict";
    function App() {
        var _this = this;

        _this.initialize = function(){
            // we use topbar to have tab buttons there
            var topBar = new TopBar().title('Eonum');

            var languageTabulator = new TabulatorModel();
            var languageModels = _.map(Multiglot.languages, function(language){
                var tab = languageTabulator.addTab(language.name).onSelected(function(){ Multiglot.setLanguage(language.code) });
                if (Multiglot.language === language.code) tab.select();
                return tab;
            });
            topBar.addRightAll(_.map(languageModels,
                function(tab){return new TabulatorButton().class('language').model(tab)}));

            // first we define tabulator model and script 3 tabs. select first one by default
            var tabulatorModel = new TabulatorModel();
            var tabIcd = tabulatorModel.addTab(Multiglot.translations.tab_icd).render(function(){return new IcdCodePane().load()});
            var tabYearIcd = tabulatorModel.addTab(Multiglot.translations.tab_icd_per_year).render(function(){return new IcdYearsPane().load()});
            var tabChop = tabulatorModel.addTab(Multiglot.translations.tab_chop).render(function(){return new ChopCodePane().load()});
            var tabDrg = tabulatorModel.addTab(Multiglot.translations.tab_drg).render(function(){return new DrgCodePane().load()}).select();
            var tabTop3 = tabulatorModel.addTab(Multiglot.translations.tab_top_3).render(function(){return new Top3DiagnosesPane()});

            // then create corresponding button views and attach tab models
            topBar.addLeftAll(_.map([tabIcd,tabYearIcd,tabChop,tabDrg, tabTop3],
                function(tab){return new TabulatorButton().model(tab)}));

            // and finally add everything to the dom
            $('body').append($('')).find('header').append(topBar);
            $('body').append(new Tabulator().styled(function(tab){tab.class('absolute')}).model(tabulatorModel));
        };

        _this.initialize();
    }

    return App
});
<commit_msg>icd as default tab<commit_after>define([
    'views/widgets/TopBar',
    'views/widgets/Tabulator',
    'views/widgets/TabulatorButton',
    'models/TabulatorModel',
    'views/widgets/icd/IcdCodePane',
    'views/widgets/icd-years/IcdYearsPane',
    'views/widgets/chop/ChopCodePane',
    'views/widgets/drg/DrgCodePane',
    'views/widgets/top3/Top3DiagnosesPane'
], function(
    TopBar,
    Tabulator,
    TabulatorButton,
    TabulatorModel,
    IcdCodePane,
    IcdYearsPane,
    ChopCodePane,
    DrgCodePane,
    Top3DiagnosesPane
){

    "use strict";
    function App() {
        var _this = this;

        _this.initialize = function(){
            // we use topbar to have tab buttons there
            var topBar = new TopBar().title('Eonum');

            var languageTabulator = new TabulatorModel();
            var languageModels = _.map(Multiglot.languages, function(language){
                var tab = languageTabulator.addTab(language.name).onSelected(function(){ Multiglot.setLanguage(language.code) });
                if (Multiglot.language === language.code) tab.select();
                return tab;
            });
            topBar.addRightAll(_.map(languageModels,
                function(tab){return new TabulatorButton().class('language').model(tab)}));

            // first we define tabulator model and script 3 tabs. select first one by default
            var tabulatorModel = new TabulatorModel();
            var tabIcd = tabulatorModel.addTab(Multiglot.translations.tab_icd).render(function(){return new IcdCodePane().load()}).select();
            var tabYearIcd = tabulatorModel.addTab(Multiglot.translations.tab_icd_per_year).render(function(){return new IcdYearsPane().load()});
            var tabChop = tabulatorModel.addTab(Multiglot.translations.tab_chop).render(function(){return new ChopCodePane().load()});
            var tabDrg = tabulatorModel.addTab(Multiglot.translations.tab_drg).render(function(){return new DrgCodePane().load()});
            var tabTop3 = tabulatorModel.addTab(Multiglot.translations.tab_top_3).render(function(){return new Top3DiagnosesPane()});

            // then create corresponding button views and attach tab models
            topBar.addLeftAll(_.map([tabIcd,tabYearIcd,tabChop,tabDrg, tabTop3],
                function(tab){return new TabulatorButton().model(tab)}));

            // and finally add everything to the dom
            $('body').append($('')).find('header').append(topBar);
            $('body').append(new Tabulator().styled(function(tab){tab.class('absolute')}).model(tabulatorModel));
        };

        _this.initialize();
    }

    return App
});
<|endoftext|>


<commit_before>var Hpi = function() {
  "use strict";

  var _currentSelection = {};

  /** Module initialisation */
  var init = function() {
    initControls();
    bindEvents();
  };

  /** Event handling */
  var bindEvents = function() {
    $("form.search").on( "submit", onSearchSubmit );
    $("form.search").on( "keydown", "input", onSeachInput );
  };

  /** Widget and control initialisation */
  var initControls = function() {
    $("button#action_search").hide();

    // ensure we know which input field the user is entering text into
    $("form.search input").each( function( i, elem ) {
      var searchId = ($(elem).attr( "name" ) == "search1") ? "loc" : "loc_compare";

      $(elem).autocomplete({
        source: HpiSearch.regionNames,
        select: function( e, ui ) {
          onAutocompleteSelect( searchId, e, ui );
        }
      });
    } );

  };

  /** User has submitted a search on the search form */
  var onSearchSubmit = function( e ) {
    if (e) {e.preventDefault();}
    if (_.keys(_currentSelection).length > 0) {
      drawPreview();
    }
  };

  /** User has selected one of the autocomplete options */
  var onAutocompleteSelect = function( searchId, e, ui ) {
    _currentSelection[searchId] = ui.item.label;
    drawPreview();
  };

  /** User has started typing into an input field */
  var onSeachInput = function( e ) {
    delete _currentSelection[$(e.currentTarget).attr( "name" )];
  };

  var drawPreview = function() {
    HpiPreview.updatePreview( _currentSelection );
  };

  return {
    init: init
  }
}();

$( Hpi.init );
<commit_msg>Make preview form immediately responsive to user input if JavaScript<commit_after>var Hpi = function() {
  "use strict";

  var _currentSelection = {};

  /** Module initialisation */
  var init = function() {
    initControls();
    bindEvents();
  };

  /** Event handling */
  var bindEvents = function() {
    $("form.search").on( "submit", onSearchSubmit );
    $("form.search").on( "keydown", "input", onSeachInput );
    $("form.preview").on( "click", "input", onChangePreviewSettings );
  };

  /** Widget and control initialisation */
  var initControls = function() {
    $("button#action_search").hide();
    $("form.preview input[type=submit]").hide();

    // ensure we know which input field the user is entering text into
    $("form.search input").each( function( i, elem ) {
      var searchId = ($(elem).attr( "name" ) == "search1") ? "loc" : "loc_compare";

      $(elem).autocomplete({
        source: HpiSearch.regionNames,
        select: function( e, ui ) {
          onAutocompleteSelect( searchId, e, ui );
        }
      });
    } );

  };

  /** User has submitted a search on the search form */
  var onSearchSubmit = function( e ) {
    if (e) {e.preventDefault();}
    if (_.keys(_currentSelection).length > 0) {
      drawPreview();
    }
  };

  /** User has selected one of the autocomplete options */
  var onAutocompleteSelect = function( searchId, e, ui ) {
    _currentSelection[searchId] = ui.item.label;
    drawPreview();
  };

  /** User has started typing into an input field */
  var onSeachInput = function( e ) {
    delete _currentSelection[$(e.currentTarget).attr( "name" )];
  };

  /** User has clicked to change some of the preview settings */
  var onChangePreviewSettings = function( e ) {
    drawPreview();
  };

  var drawPreview = function() {
    HpiPreview.updatePreview( _currentSelection );
  };

  return {
    init: init
  }
}();

$( Hpi.init );
<|endoftext|>


<commit_before>$(document).ready(function() {

// =====================================================
// Sets the lang attribute of html to the language selected
// =====================================================
if($('article').attr('lang')){
    $("html").attr('lang', $("article").attr('lang'));
}

// =====================================================
// Remove hidden attribute from js-visible elements
// =====================================================
$('.js-visible').removeAttr('hidden');

// =====================================================
// Initialise show-hide-content
// Toggles additional content based on radio/checkbox input state
// =====================================================
var showHideContent = new GOVUK.ShowHideContent()
showHideContent.init()

// =====================================================
// Combine two values to display a third
// e.g. used on Gifts details
// =====================================================
  combinedValue();

// =====================================================
// Check for hashed url and jump to input if needed
// The non-error-list focus will not focus on the input for iOS due to security restrictions
// The in-page link-click focus setting is not affected
// =====================================================
    var hash = window.location.hash.substring(1);
    if(hash > ""){
        hashJump(hash);
    }

    // error summary jump links
    $('.error-list a').on('click', function(e){
        var linkhash = $(this).attr("href").replace('#','');
        hashJump(linkhash);
    });

// =====================================================
// Declaration
// =====================================================
    function runDec(btn){
      if(btn[0].checked){
          $(".toDisableButton").prop( "disabled", false );
      }else{
          $(".toDisableButton").prop( "disabled", true );
      }
    }
    if($("#isDeclared").length > 0){
        runDec($("#isDeclared"));

        $(".toDisableButton").attr('id', 'accept-button');
        $("#isDeclared").attr('aria-controls', 'accept-button');
        $("#isDeclared").click(function(){
            runDec($(this));
        });
    }

// =====================================================
// reset an input when a radio button changes value
// =====================================================
  $("[data-clear-target]").click(function(){
      clearInput($(this));
  });

// =====================================================
// polyfill for inline name wrapping on IE < 10
// Fixes where IE doesn't wrap if the container is not display: block
// This finds the inline name and passes a class to the parent container
// Not used by default as causes ugly wrapping of non-name copy
// Wrapped in browser UA sniffing as feature detection would not work here
// =====================================================
if(typeof window.navigator != "undefined" && typeof window.navigator.userAgent != "undefined"){
    if(window.navigator.userAgent.indexOf("MSIE 9") > 0 || window.navigator.userAgent.indexOf("MSIE 8") > 0){
        $('span.copy--restricted').not("a > .copy--restricted").each(function(){
            var wrapItem = $(this);
            var parentContents = wrapItem.parent().html();
            wrapItem.parent().html('
' + parentContents + '
');

        });
    }
}

// =====================================================
// Country code autocomplete
// =====================================================
if($('[data-enhanced-select]').length > 0){
    var countryCode = new Autobox($('select'));
}

// =====================================================
// Submit trigger
// Used on Gifts section
// Should be replaced by links without the POST through
// =======================================================
  $(".js-submitButtonAction").click(function(){
      var submitValue = $(this).attr("data-submitValue");
      $("form").append("<input type='hidden' name='action' value='"+submitValue+"'>");
      $("form").trigger('submit');
  });

// =====================================================
// Handle number inputs
// =====================================================
  numberInputs();

// =====================================================
// Handle the IHT UR panel dismiss link functionality
// =====================================================
    var cookieData=GOVUK.getCookie("iht_ur_panel");
    if (cookieData == null) {
        $("#ur-panel").addClass("banner-panel--show");
    }

    $(".banner-panel__close").on("click", function(e) {
        e.preventDefault();
         GOVUK.setCookie("iht_ur_panel", 1, 99999999999);
         $("#ur-panel").removeClass("banner-panel--show");
    });
  // end of on doc ready
});

/// =====================================================
/// Hash jump
/// Takes user to location of input which needs error fixing
/// Also looks for a parent label or fieldset and ensures the page is scrolled to leave that visible
/// =====================================================
function hashJump(hash){
    hash = hash.replace('.','\\\.');
    if (hash.length != 0) {
        if($('#' + hash).length > 0){
            var el = $('#' + hash);
            if(el[0].tagName == "INPUT"){
                // set fallback scroll of the input
                var scrollPos = el.offset().top;
                if (el[0].type == "radio" || el[0].type == "checkbox"){
                    // focus on the group legend if present, else on the label
                    if(el.parents('fieldset').length > 0){
                        scrollPos = el.parents('fieldset').first().find('legend').offset().top;
                    }
                } else {
                    if($('label[for="' + hash + '"]').length > 0){
                        scrollPos = $('label[for="' + hash + '"]').offset().top;
                        setFocus($('#' + hash));
                    }
                }
                setTimeout(function(){
                    $(window).scrollTop(scrollPos);
                }, 50);

            } else {
                 // scroll and set focus on inputs when anchor is on container
                 setInputFocus(hash);
            }
        }
    }
}

/// =====================================================
/// Set Focus
/// cross-device focus set
/// We set via a timeout for desktop browsers
/// We also set immediately as iOS will not support a timeout set due to security rules
/// Both are needed as support is not cross-device for a single implementation
/// =====================================================
function setFocus(el){
    el.focus();
    setTimeout(function(){
        el.focus();
    }, 10);
}

/// =====================================================
/// Set Input Focus
/// finds an input in a given element with id of elid and sets focus
/// Will focus on either:
/// 1. An input *outside* of a fieldset which is related to the given label id
/// This captures standard label + input[text] combinations
/// 2. An input of type[text||tel||number] which is the first visible input inside a fieldset
/// This captures patterns such as date-of-birth where it is desirable to have focus on the first input
/// 3. An already selected option in a group of checkboxes/radio-buttons
/// We can safely target this as it will playback the selected option
///
/// In all other instances focus will not be set
/// =====================================================
function setInputFocus(elid) {
    var labelTarget = $('label[id="' + elid + '"], div[id="' + elid + '"]');
    var fieldsetTarget = $('[id="' + elid + '"] fieldset, fieldset[id="' + elid + '"]').first();

    if(labelTarget.length > 0 && fieldsetTarget.length == 0){
       setFocus(labelTarget.find('input').first());
    }

    if(fieldsetTarget.length > 0){
        // focus on first text field if it is the first visible input in a fieldset
        var firstInput = fieldsetTarget.find('input:visible').first();
        if(firstInput.attr('type') == "text" || firstInput.attr('type') == "tel" || firstInput.attr('type') == "number"){
            // text input inside a fieldset
            setFocus(firstInput);
        } else {
            // non-focus-able inputs
            // but we can focus on a checked radio button or checkbox
            if(fieldsetTarget.find(':checked').length > 0){
                 setFocus(fieldsetTarget.find(':checked').first());
            } else {
                fieldsetTarget.first().click();
            }
        }
    }
}

// =====================================================
// Clear inputs
// Used to reset an input when a radio button is changed
// eg clear a value text input when a Yes is changed to a No
// =======================================================
function clearInput(el){
  var target = el.attr("data-clear-target");
  if($("#"+target+"").length){
      $('#'+target).val("");
  }
}

// =====================================================
// Takes an element and subtracts one input from another to show a combined message
// e.g. used on gifts details
// =====================================================
  function checkforCombinedValue(el){
    // input we are taking as a start value
    var addValue = $('#' + el.attr('data-combine-add'));
    var addValueVal = 0;
    if(isNumeric(addValue.val())){
      addValueVal = addValue.val();
    }
    // input we are going to subtract from the start value
    var subtractValue = $('#' + el.attr('data-combine-subtract'));
    var subtractValueVal = 0;
    if(isNumeric(subtractValue.val())){
      subtractValueVal = subtractValue.val();
    }
    // calc
    var total = addValueVal - subtractValueVal;

    // conditionally show the message if we have a valid number
    if(total >= 0){
     if( (addValueVal.toString().indexOf('.')==-1) && (subtractValueVal.toString().indexOf('.')==-1) ) {
      el.html(el.attr('data-combine-copy') + total);
      } else {
      el.html(el.attr('data-combine-copy') + total.toFixed(2));}
    } else {
      // maybe need to show a message here if the calc is invalid to update screen readers
      el.html(el.attr('data-combine-copy') + 0);
    }
  }

// =====================================================
// Fires checkforCombinedValue on page load and on input change
// =====================================================
  function combinedValue(){
    $('.js-combined-value').each(function(){
      var el = $(this);
      var timer;
      function trackKey(){
        // gives a small delay after key input before updating value
        timer = setInterval(function(){
          clearInterval(timer);
          checkforCombinedValue(el);
        }, 500);
      }

      // check on page load
      checkforCombinedValue(el);

      // check when either input changes
      $('[aria-controls="' + el.attr('id') + '"]').each(function(){
        $(this).on('keyup blur', function(){
          if(timer != undefined){
            clearInterval(timer);
          }
          trackKey();
        });
      });

    });
  }

// ======================================================
// checks if a value is numeric
// ======================================================
function isNumeric(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

function numberInputs() {
    // =====================================================
    // Set currency fields to number inputs on touch devices
    // this ensures on-screen keyboards display the correct style
    // don't do this for FF as it has issues with trailing zeroes
    // =====================================================
    if($('html.touchevents').length > 0 && window.navigator.userAgent.indexOf("Firefox") == -1){
        $('[data-type="currency"]').each(function(){
          $(this).attr('type', 'number');
          $(this).attr('step', 'any');

          $(this).attr('min', '0');
        });
    }

    // =====================================================
    // Disable mouse wheel and arrow keys (38,40) for number inputs to prevent mis-entry
    // also disable commas (188) as they will silently invalidate entry on Safari 10.0.3 and IE11
    // =====================================================
    $("form").on("focus", "input[type=number]", function(e) {
        $(this).on('wheel', function(e) {
            e.preventDefault();
        });
    });
    $("form").on("blur", "input[type=number]", function(e) {
        $(this).off('wheel');
    });
    $("form").on("keydown", "input[type=number]", function(e) {
        if ( e.which == 38 || e.which == 40 || e.which == 188 )
            e.preventDefault();
    });
}<commit_msg>IHT-5177 Amend UR banner cookie<commit_after>$(document).ready(function() {

// =====================================================
// Sets the lang attribute of html to the language selected
// =====================================================
if($('article').attr('lang')){
    $("html").attr('lang', $("article").attr('lang'));
}

// =====================================================
// Remove hidden attribute from js-visible elements
// =====================================================
$('.js-visible').removeAttr('hidden');

// =====================================================
// Initialise show-hide-content
// Toggles additional content based on radio/checkbox input state
// =====================================================
var showHideContent = new GOVUK.ShowHideContent()
showHideContent.init()

// =====================================================
// Combine two values to display a third
// e.g. used on Gifts details
// =====================================================
  combinedValue();

// =====================================================
// Check for hashed url and jump to input if needed
// The non-error-list focus will not focus on the input for iOS due to security restrictions
// The in-page link-click focus setting is not affected
// =====================================================
    var hash = window.location.hash.substring(1);
    if(hash > ""){
        hashJump(hash);
    }

    // error summary jump links
    $('.error-list a').on('click', function(e){
        var linkhash = $(this).attr("href").replace('#','');
        hashJump(linkhash);
    });

// =====================================================
// Declaration
// =====================================================
    function runDec(btn){
      if(btn[0].checked){
          $(".toDisableButton").prop( "disabled", false );
      }else{
          $(".toDisableButton").prop( "disabled", true );
      }
    }
    if($("#isDeclared").length > 0){
        runDec($("#isDeclared"));

        $(".toDisableButton").attr('id', 'accept-button');
        $("#isDeclared").attr('aria-controls', 'accept-button');
        $("#isDeclared").click(function(){
            runDec($(this));
        });
    }

// =====================================================
// reset an input when a radio button changes value
// =====================================================
  $("[data-clear-target]").click(function(){
      clearInput($(this));
  });

// =====================================================
// polyfill for inline name wrapping on IE < 10
// Fixes where IE doesn't wrap if the container is not display: block
// This finds the inline name and passes a class to the parent container
// Not used by default as causes ugly wrapping of non-name copy
// Wrapped in browser UA sniffing as feature detection would not work here
// =====================================================
if(typeof window.navigator != "undefined" && typeof window.navigator.userAgent != "undefined"){
    if(window.navigator.userAgent.indexOf("MSIE 9") > 0 || window.navigator.userAgent.indexOf("MSIE 8") > 0){
        $('span.copy--restricted').not("a > .copy--restricted").each(function(){
            var wrapItem = $(this);
            var parentContents = wrapItem.parent().html();
            wrapItem.parent().html('
' + parentContents + '
');

        });
    }
}

// =====================================================
// Country code autocomplete
// =====================================================
if($('[data-enhanced-select]').length > 0){
    var countryCode = new Autobox($('select'));
}

// =====================================================
// Submit trigger
// Used on Gifts section
// Should be replaced by links without the POST through
// =======================================================
  $(".js-submitButtonAction").click(function(){
      var submitValue = $(this).attr("data-submitValue");
      $("form").append("<input type='hidden' name='action' value='"+submitValue+"'>");
      $("form").trigger('submit');
  });

// =====================================================
// Handle number inputs
// =====================================================
  numberInputs();

// =====================================================
// Handle the IHT UR panel dismiss link functionality
// =====================================================
    var cookieData=GOVUK.getCookie("mdtpurr");
    if (cookieData==null) {
        $("#ur-panel").addClass("banner-panel--show");
    }

    $(".banner-panel__close").on("click", function(e) {
        e.preventDefault();
         GOVUK.setCookie("mdtpurr", "suppress_for_all_services=true", 99999999999);
         $("#ur-panel").removeClass("banner-panel--show");
    });
  // end of on doc ready
});

/// =====================================================
/// Hash jump
/// Takes user to location of input which needs error fixing
/// Also looks for a parent label or fieldset and ensures the page is scrolled to leave that visible
/// =====================================================
function hashJump(hash){
    hash = hash.replace('.','\\\.');
    if (hash.length != 0) {
        if($('#' + hash).length > 0){
            var el = $('#' + hash);
            if(el[0].tagName == "INPUT"){
                // set fallback scroll of the input
                var scrollPos = el.offset().top;
                if (el[0].type == "radio" || el[0].type == "checkbox"){
                    // focus on the group legend if present, else on the label
                    if(el.parents('fieldset').length > 0){
                        scrollPos = el.parents('fieldset').first().find('legend').offset().top;
                    }
                } else {
                    if($('label[for="' + hash + '"]').length > 0){
                        scrollPos = $('label[for="' + hash + '"]').offset().top;
                        setFocus($('#' + hash));
                    }
                }
                setTimeout(function(){
                    $(window).scrollTop(scrollPos);
                }, 50);

            } else {
                 // scroll and set focus on inputs when anchor is on container
                 setInputFocus(hash);
            }
        }
    }
}

/// =====================================================
/// Set Focus
/// cross-device focus set
/// We set via a timeout for desktop browsers
/// We also set immediately as iOS will not support a timeout set due to security rules
/// Both are needed as support is not cross-device for a single implementation
/// =====================================================
function setFocus(el){
    el.focus();
    setTimeout(function(){
        el.focus();
    }, 10);
}

/// =====================================================
/// Set Input Focus
/// finds an input in a given element with id of elid and sets focus
/// Will focus on either:
/// 1. An input *outside* of a fieldset which is related to the given label id
/// This captures standard label + input[text] combinations
/// 2. An input of type[text||tel||number] which is the first visible input inside a fieldset
/// This captures patterns such as date-of-birth where it is desirable to have focus on the first input
/// 3. An already selected option in a group of checkboxes/radio-buttons
/// We can safely target this as it will playback the selected option
///
/// In all other instances focus will not be set
/// =====================================================
function setInputFocus(elid) {
    var labelTarget = $('label[id="' + elid + '"], div[id="' + elid + '"]');
    var fieldsetTarget = $('[id="' + elid + '"] fieldset, fieldset[id="' + elid + '"]').first();

    if(labelTarget.length > 0 && fieldsetTarget.length == 0){
       setFocus(labelTarget.find('input').first());
    }

    if(fieldsetTarget.length > 0){
        // focus on first text field if it is the first visible input in a fieldset
        var firstInput = fieldsetTarget.find('input:visible').first();
        if(firstInput.attr('type') == "text" || firstInput.attr('type') == "tel" || firstInput.attr('type') == "number"){
            // text input inside a fieldset
            setFocus(firstInput);
        } else {
            // non-focus-able inputs
            // but we can focus on a checked radio button or checkbox
            if(fieldsetTarget.find(':checked').length > 0){
                 setFocus(fieldsetTarget.find(':checked').first());
            } else {
                fieldsetTarget.first().click();
            }
        }
    }
}

// =====================================================
// Clear inputs
// Used to reset an input when a radio button is changed
// eg clear a value text input when a Yes is changed to a No
// =======================================================
function clearInput(el){
  var target = el.attr("data-clear-target");
  if($("#"+target+"").length){
      $('#'+target).val("");
  }
}

// =====================================================
// Takes an element and subtracts one input from another to show a combined message
// e.g. used on gifts details
// =====================================================
  function checkforCombinedValue(el){
    // input we are taking as a start value
    var addValue = $('#' + el.attr('data-combine-add'));
    var addValueVal = 0;
    if(isNumeric(addValue.val())){
      addValueVal = addValue.val();
    }
    // input we are going to subtract from the start value
    var subtractValue = $('#' + el.attr('data-combine-subtract'));
    var subtractValueVal = 0;
    if(isNumeric(subtractValue.val())){
      subtractValueVal = subtractValue.val();
    }
    // calc
    var total = addValueVal - subtractValueVal;

    // conditionally show the message if we have a valid number
    if(total >= 0){
     if( (addValueVal.toString().indexOf('.')==-1) && (subtractValueVal.toString().indexOf('.')==-1) ) {
      el.html(el.attr('data-combine-copy') + total);
      } else {
      el.html(el.attr('data-combine-copy') + total.toFixed(2));}
    } else {
      // maybe need to show a message here if the calc is invalid to update screen readers
      el.html(el.attr('data-combine-copy') + 0);
    }
  }

// =====================================================
// Fires checkforCombinedValue on page load and on input change
// =====================================================
  function combinedValue(){
    $('.js-combined-value').each(function(){
      var el = $(this);
      var timer;
      function trackKey(){
        // gives a small delay after key input before updating value
        timer = setInterval(function(){
          clearInterval(timer);
          checkforCombinedValue(el);
        }, 500);
      }

      // check on page load
      checkforCombinedValue(el);

      // check when either input changes
      $('[aria-controls="' + el.attr('id') + '"]').each(function(){
        $(this).on('keyup blur', function(){
          if(timer != undefined){
            clearInterval(timer);
          }
          trackKey();
        });
      });

    });
  }

// ======================================================
// checks if a value is numeric
// ======================================================
function isNumeric(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

function numberInputs() {
    // =====================================================
    // Set currency fields to number inputs on touch devices
    // this ensures on-screen keyboards display the correct style
    // don't do this for FF as it has issues with trailing zeroes
    // =====================================================
    if($('html.touchevents').length > 0 && window.navigator.userAgent.indexOf("Firefox") == -1){
        $('[data-type="currency"]').each(function(){
          $(this).attr('type', 'number');
          $(this).attr('step', 'any');

          $(this).attr('min', '0');
        });
    }

    // =====================================================
    // Disable mouse wheel and arrow keys (38,40) for number inputs to prevent mis-entry
    // also disable commas (188) as they will silently invalidate entry on Safari 10.0.3 and IE11
    // =====================================================
    $("form").on("focus", "input[type=number]", function(e) {
        $(this).on('wheel', function(e) {
            e.preventDefault();
        });
    });
    $("form").on("blur", "input[type=number]", function(e) {
        $(this).off('wheel');
    });
    $("form").on("keydown", "input[type=number]", function(e) {
        if ( e.which == 38 || e.which == 40 || e.which == 188 )
            e.preventDefault();
    });
}<|endoftext|>