<commit_before>/*---------------------------------------------------------
 * OpenERP web_graph
 *---------------------------------------------------------*/

openerp.web_graph = function (openerp) {
var COLOR_PALETTE = [
    '#cc99ff', '#ccccff', '#48D1CC', '#CFD784', '#8B7B8B', '#75507b',
    '#b0008c', '#ff0000', '#ff8e00', '#9000ff', '#0078ff', '#00ff00',
    '#e6ff00', '#ffff00', '#905000', '#9b0000', '#840067', '#9abe00',
    '#ffc900', '#510090', '#0000c9', '#009b00', '#75507b', '#3465a4',
    '#73d216', '#c17d11', '#edd400', '#fcaf3e', '#ef2929', '#ff00c9',
    '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f', '#f57900',
    '#cc0000', '#d400a8'];

var QWeb = openerp.web.qweb;
openerp.web.views.add('graph', 'openerp.web_graph.GraphView');
openerp.web_graph.GraphView = openerp.web.View.extend({

    init: function(parent, dataset, view_id, options) {
        this._super(parent);
        this.set_default_options(options);
        this.dataset = dataset;
        this.view_id = view_id;

        this.first_field = null;
        this.abscissa = null;
        this.ordinate = null;
        this.columns = [];
        this.group_field = null;
        this.is_loaded = $.Deferred();

        this.renderer = null;
    },
    stop: function () {
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this._super();
    },
    do_show: function () {
        this.$element.show();
    },
    do_hide: function () {
        this.$element.hide();
    },
    start: function() {
        var self = this;
        this._super();
        var loaded;
        if (this.embedded_view) {
            loaded = $.when([self.embedded_view]);
        } else {
            loaded = this.rpc('/web/view/load', {
                    model: this.dataset.model,
                    view_id: this.view_id,
                    view_type: 'graph'
            });
        }
        return $.when(
            this.dataset.call('fields_get', []),
            loaded)
            .then(function (fields_result, view_result) {
                self.fields = fields_result[0];
                self.fields_view = view_result[0];
                self.on_loaded();
            });
    },
    /**
     * Returns all object fields involved in the graph view
     */
    list_fields: function () {
        var fs = [this.abscissa];
        fs.push.apply(fs, _(this.columns).pluck('name'));
        if (this.group_field) {
            fs.push(this.group_field);
        }
        return fs;
    },
    on_loaded: function() {
        this.chart = this.fields_view.arch.attrs.type || 'pie';
        this.orientation = this.fields_view.arch.attrs.orientation || 'vertical';

        _.each(this.fields_view.arch.children, function (field) {
            var attrs = field.attrs;
            if (attrs.group) {
                this.group_field = attrs.name;
            } else if(!this.abscissa) {
                this.first_field = this.abscissa = attrs.name;
            } else {
                this.columns.push({
                    name: attrs.name,
                    operator: attrs.operator || '+'
                });
            }
        }, this);
        this.ordinate = this.columns[0].name;
        this.is_loaded.resolve();
    },
    schedule_chart: function(results) {
        var self = this;
        this.$element.html(QWeb.render("GraphView", {
            "fields_view": this.fields_view,
            "chart": this.chart,
            'element_id': this.element_id
        }));

        var fields = _(this.columns).pluck('name').concat([this.abscissa]);
        if (this.group_field) { fields.push(this.group_field); }
        // transform search result into usable records (convert from OpenERP
        // value shapes to usable atomic types
        var records = _(results).map(function (result) {
            var point = {};
            _(result).each(function (value, field) {
                if (!_(fields).contains(field)) { return; }
                if (value === false) { point[field] = false; return; }
                switch (self.fields[field].type) {
                case 'selection':
                    point[field] = _(self.fields[field].selection).detect(function (choice) {
                        return choice[0] === value;
                    })[1];
                    break;
                case 'many2one':
                    point[field] = value[1];
                    break;
                case 'integer': case 'float': case 'char':
                case 'date': case 'datetime':
                    point[field] = value;
                    break;
                default:
                    throw new Error(
                        "Unknown field type " + self.fields[field].type
                        + "for field " + field + " (" + value + ")");
                }
            });
            return point;
        });
        // aggregate data, because dhtmlx is crap. Aggregate on abscissa field,
        // leave split on group field => max m*n records where m is the # of
        // values for the abscissa and n is the # of values for the group field
        var graph_data = [];
        _(records).each(function (record) {
            var abscissa = record[self.abscissa],
                group = record[self.group_field];
            var r = _(graph_data).detect(function (potential) {
                return potential[self.abscissa] === abscissa
                        && (!self.group_field
                            || potential[self.group_field] === group);
            });
            var datapoint = r || {};

            datapoint[self.abscissa] = abscissa;
            if (self.group_field) { datapoint[self.group_field] = group; }
            _(self.columns).each(function (column) {
                var val = record[column.name],
                    aggregate = datapoint[column.name];
                switch(column.operator) {
                case '+':
                    datapoint[column.name] = (aggregate || 0) + val;
                    return;
                case '*':
                    datapoint[column.name] = (aggregate || 1) * val;
                    return;
                case 'min':
                    datapoint[column.name] = (aggregate || Infinity) > val
                                           ? val
                                           : aggregate;
                    return;
                case 'max':
                    datapoint[column.name] = (aggregate || -Infinity) < val
                                           ? val
                                           : aggregate;
                }
            });

            if (!r) { graph_data.push(datapoint); }
        });
        graph_data = _(graph_data).sortBy(function (point) {
            return point[self.abscissa] + '[[--]]' + point[self.group_field];
        });

        if (this.chart == 'bar') {
            return this.schedule_bar(graph_data);
        } else if (this.chart == "pie") {
            return this.schedule_pie(graph_data);
        }
    },

    schedule_bar: function(results) {
        var self = this;

        var group_list, view_chart;
        if (!this.group_field) {
            view_chart = (this.orientation === 'horizontal') ? 'barH' : 'bar';
            group_list = _(this.columns).map(function (column, index) {
                return {
                    group: column.name,
                    text: self.fields[column.name].string,
                    color: COLOR_PALETTE[index % (COLOR_PALETTE.length)]
                }
            });
        } else {
            // dhtmlx handles clustered bar charts (> 1 column per abscissa
            // value) and stacked bar charts (basically the same but with the
            // columns on top of one another instead of side by side), but it
            // does not handle clustered stacked bar charts
            if (this.columns.length > 1) {
                this.$element.text(
                    'OpenERP Web does not support combining grouping and '
                  + 'multiple columns in graph at this time.');
                throw new Error(
                    'dhtmlx can not handle columns counts of that magnitude');
            }
            // transform series for clustered charts into series for stacked
            // charts
            view_chart = (this.orientation === 'horizontal')
                    ? 'stackedBarH' : 'stackedBar';
            group_list = _(results).chain()
                    .pluck(this.group_field)
                    .uniq()
                    .map(function (value, index) {
                        return {
                            group: self.ordinate + '_' +
                                    value.toLowerCase().replace(/\s/g, '_'),
                            text: value,
                            color: COLOR_PALETTE[index % COLOR_PALETTE.length]
                        };
                    }).value();

            results = _(results).chain()
                .groupBy(function (record) { return record[self.abscissa]; })
                .map(function (records) {
                    var r = {};
                    // second argument is coerced to a str, no good for boolean
                    r[self.abscissa] = records[0][self.abscissa];
                    _(records).each(function (record) {
                        var key = _.str.sprintf('%s_%s',
                            self.ordinate,
                            record[self.group_field].toLowerCase().replace(/\s/g, '_'));
                        r[key] = record[self.ordinate];
                    });
                    return r;
                })
                .value();
        }

        var abscissa_description = {
            title: "" + this.fields[this.abscissa].string + "",
            template: function (obj) {
                return obj[self.abscissa] || 'Undefined';
            }
        };

        var ordinate_description = {
            lines: true,
            title: "" + this.fields[this.ordinate].string + ""
        };

        var x_axis, y_axis;
        if (self.orientation == 'horizontal') {
            x_axis = ordinate_description;
            y_axis = abscissa_description;
        } else {
            x_axis = abscissa_description;
            y_axis = ordinate_description;
        }

        var renderer = function () {
            if (self.$element.is(':hidden')) {
                self.renderer = setTimeout(renderer, 100);
                return;
            }
            self.renderer = null;
            var bar_chart = new dhtmlXChart({
                view: view_chart,
                container: self.element_id+"-barchart",
                value:"#"+group_list[0].group+"#",
                gradient: "3d",
                border: false,
                width: 1024,
                tooltip:{
                    template: _.str.sprintf("#%s#, %s=#%s#",
                        self.abscissa, group_list[0].text, group_list[0].group)
                },
                radius: 0,
                color:group_list[0].color,
                origin:0,
                xAxis: x_axis,
                yAxis: y_axis,
                padding: {
                    left: 75
                },
                legend: {
                    values: group_list,
                    align:"left",
                    valign:"top",
                    layout: "x",
                    marker: {
                        type:"round",
                        width:12
                    }
                }
            });
            for (var m = 1; m<group_list.length;m++){
                var column = group_list[m];
                if (column.group === self.group_field) { continue; }
                bar_chart.addSeries({
                    value: "#"+column.group+"#",
                    tooltip:{
                        template: _.str.sprintf("#%s#, %s=#%s#",
                            self.abscissa, column.text, column.group)
                    },
                    color: column.color
                });
            }
            bar_chart.parse(results, "json");
            self.$element.find("#"+self.element_id+"-barchart").height(
                self.$element.find("#"+self.element_id+"-barchart").height()+50);
            bar_chart.attachEvent("onItemClick", function(id) {
                self.open_list_view(bar_chart.get(id));
            });
        };
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this.renderer = setTimeout(renderer, 0);
    },
    schedule_pie: function(result) {
        var self = this;
        var renderer = function () {
            if (self.$element.is(':hidden')) {
                self.renderer = setTimeout(renderer, 100);
                return;
            }
            self.renderer = null;
            var chart =  new dhtmlXChart({
                view:"pie3D",
                container:self.element_id+"-piechart",
                value:"#"+self.ordinate+"#",
                pieInnerText:function(obj) {
                    var sum = chart.sum("#"+self.ordinate+"#");
                    var val = obj[self.ordinate] / sum * 100 ;
                    return val.toFixed(1) + "%";
                },
                tooltip:{
                    template:"#"+self.abscissa+"#"+"="+"#"+self.ordinate+"#"
                },
                gradient:"3d",
                height: 20,
                radius: 200,
                legend: {
                    width: 300,
                    align:"left",
                    valign:"top",
                    layout: "x",
                    marker:{
                        type:"round",
                        width:12
                    },
                    template:function(obj){
                        return obj[self.abscissa] || 'Undefined';
                    }
                }
            });
            chart.parse(result,"json");
            chart.attachEvent("onItemClick", function(id) {
                self.open_list_view(chart.get(id));
            });
        };
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this.renderer = setTimeout(renderer, 0);
    },
    open_list_view : function (id){
        var self = this;
        // unconditionally nuke tooltips before switching view
        $(".dhx_tooltip").remove('div');
        id = id[this.abscissa];
        if (typeof id == 'object'){
            id = id[0];
        }

        var record_id = this.abscissa;
        var modes = ["list", "form", "graph"];
        var views = [];
        _.each(modes, function(mode) {
            var view = [false, mode];
            if (self.fields.views && self.fields.views[mode]) {
                view.push(self.fields.views[mode]);
            }
            views.push(view);
        });
        this.do_action({
            "res_model" : this.dataset.model,
            "domain" : [[record_id, '=', id], ['id','in',this.dataset.ids]],
            "views" : views,
            "type" : "ir.actions.act_window",
            "auto_search" : true,
            "view_type" : "list",
            "view_mode" : "list"
        });
    },

    do_search: function(domain, context, group_by) {
        var self = this;
        return $.when(this.is_loaded).pipe(function() {
            // TODO: handle non-empty group_by with read_group?
            if (!_(group_by).isEmpty()) {
                self.abscissa = group_by[0];
            } else {
                self.abscissa = self.first_field;
            }
            return self.dataset.read_slice(self.list_fields(), {}, $.proxy(self, 'schedule_chart'));
        });
    }
});
};
// vim:et fdc=0 fdl=0:
<commit_msg>[FIX]Fixed code for showing stackbar chart. dhtmlxchart not support forward or backward slash in key to get value from result.<commit_after>/*---------------------------------------------------------
 * OpenERP web_graph
 *---------------------------------------------------------*/

openerp.web_graph = function (openerp) {
var COLOR_PALETTE = [
    '#cc99ff', '#ccccff', '#48D1CC', '#CFD784', '#8B7B8B', '#75507b',
    '#b0008c', '#ff0000', '#ff8e00', '#9000ff', '#0078ff', '#00ff00',
    '#e6ff00', '#ffff00', '#905000', '#9b0000', '#840067', '#9abe00',
    '#ffc900', '#510090', '#0000c9', '#009b00', '#75507b', '#3465a4',
    '#73d216', '#c17d11', '#edd400', '#fcaf3e', '#ef2929', '#ff00c9',
    '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f', '#f57900',
    '#cc0000', '#d400a8'];

var QWeb = openerp.web.qweb;
openerp.web.views.add('graph', 'openerp.web_graph.GraphView');
openerp.web_graph.GraphView = openerp.web.View.extend({

    init: function(parent, dataset, view_id, options) {
        this._super(parent);
        this.set_default_options(options);
        this.dataset = dataset;
        this.view_id = view_id;

        this.first_field = null;
        this.abscissa = null;
        this.ordinate = null;
        this.columns = [];
        this.group_field = null;
        this.is_loaded = $.Deferred();

        this.renderer = null;
    },
    stop: function () {
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this._super();
    },
    do_show: function () {
        this.$element.show();
    },
    do_hide: function () {
        this.$element.hide();
    },
    start: function() {
        var self = this;
        this._super();
        var loaded;
        if (this.embedded_view) {
            loaded = $.when([self.embedded_view]);
        } else {
            loaded = this.rpc('/web/view/load', {
                    model: this.dataset.model,
                    view_id: this.view_id,
                    view_type: 'graph'
            });
        }
        return $.when(
            this.dataset.call('fields_get', []),
            loaded)
            .then(function (fields_result, view_result) {
                self.fields = fields_result[0];
                self.fields_view = view_result[0];
                self.on_loaded();
            });
    },
    /**
     * Returns all object fields involved in the graph view
     */
    list_fields: function () {
        var fs = [this.abscissa];
        fs.push.apply(fs, _(this.columns).pluck('name'));
        if (this.group_field) {
            fs.push(this.group_field);
        }
        return fs;
    },
    on_loaded: function() {
        this.chart = this.fields_view.arch.attrs.type || 'pie';
        this.orientation = this.fields_view.arch.attrs.orientation || 'vertical';

        _.each(this.fields_view.arch.children, function (field) {
            var attrs = field.attrs;
            if (attrs.group) {
                this.group_field = attrs.name;
            } else if(!this.abscissa) {
                this.first_field = this.abscissa = attrs.name;
            } else {
                this.columns.push({
                    name: attrs.name,
                    operator: attrs.operator || '+'
                });
            }
        }, this);
        this.ordinate = this.columns[0].name;
        this.is_loaded.resolve();
    },
    schedule_chart: function(results) {
        var self = this;
        this.$element.html(QWeb.render("GraphView", {
            "fields_view": this.fields_view,
            "chart": this.chart,
            'element_id': this.element_id
        }));

        var fields = _(this.columns).pluck('name').concat([this.abscissa]);
        if (this.group_field) { fields.push(this.group_field); }
        // transform search result into usable records (convert from OpenERP
        // value shapes to usable atomic types
        var records = _(results).map(function (result) {
            var point = {};
            _(result).each(function (value, field) {
                if (!_(fields).contains(field)) { return; }
                if (value === false) { point[field] = false; return; }
                switch (self.fields[field].type) {
                case 'selection':
                    point[field] = _(self.fields[field].selection).detect(function (choice) {
                        return choice[0] === value;
                    })[1];
                    break;
                case 'many2one':
                    point[field] = value[1];
                    break;
                case 'integer': case 'float': case 'char':
                case 'date': case 'datetime':
                    point[field] = value;
                    break;
                default:
                    throw new Error(
                        "Unknown field type " + self.fields[field].type
                        + "for field " + field + " (" + value + ")");
                }
            });
            return point;
        });
        // aggregate data, because dhtmlx is crap. Aggregate on abscissa field,
        // leave split on group field => max m*n records where m is the # of
        // values for the abscissa and n is the # of values for the group field
        var graph_data = [];
        _(records).each(function (record) {
            var abscissa = record[self.abscissa],
                group = record[self.group_field];
            var r = _(graph_data).detect(function (potential) {
                return potential[self.abscissa] === abscissa
                        && (!self.group_field
                            || potential[self.group_field] === group);
            });
            var datapoint = r || {};

            datapoint[self.abscissa] = abscissa;
            if (self.group_field) { datapoint[self.group_field] = group; }
            _(self.columns).each(function (column) {
                var val = record[column.name],
                    aggregate = datapoint[column.name];
                switch(column.operator) {
                case '+':
                    datapoint[column.name] = (aggregate || 0) + val;
                    return;
                case '*':
                    datapoint[column.name] = (aggregate || 1) * val;
                    return;
                case 'min':
                    datapoint[column.name] = (aggregate || Infinity) > val
                                           ? val
                                           : aggregate;
                    return;
                case 'max':
                    datapoint[column.name] = (aggregate || -Infinity) < val
                                           ? val
                                           : aggregate;
                }
            });

            if (!r) { graph_data.push(datapoint); }
        });
        graph_data = _(graph_data).sortBy(function (point) {
            return point[self.abscissa] + '[[--]]' + point[self.group_field];
        });

        if (this.chart == 'bar') {
            return this.schedule_bar(graph_data);
        } else if (this.chart == "pie") {
            return this.schedule_pie(graph_data);
        }
    },

    schedule_bar: function(results) {
        var self = this;
        var group_list, view_chart;
        if (!this.group_field) {
            view_chart = (this.orientation === 'horizontal') ? 'barH' : 'bar';
            group_list = _(this.columns).map(function (column, index) {
                return {
                    group: column.name,
                    text: self.fields[column.name].string,
                    color: COLOR_PALETTE[index % (COLOR_PALETTE.length)]
                }
            });
        } else {
            // dhtmlx handles clustered bar charts (> 1 column per abscissa
            // value) and stacked bar charts (basically the same but with the
            // columns on top of one another instead of side by side), but it
            // does not handle clustered stacked bar charts
            if (this.columns.length > 1) {
                this.$element.text(
                    'OpenERP Web does not support combining grouping and '
                  + 'multiple columns in graph at this time.');
                throw new Error(
                    'dhtmlx can not handle columns counts of that magnitude');
            }
            // transform series for clustered charts into series for stacked
            // charts
            view_chart = (this.orientation === 'horizontal')
                    ? 'stackedBarH' : 'stackedBar';
            group_list = _(results).chain()
                    .pluck(this.group_field)
                    .uniq()
                    .map(function (value, index) {
                        return {
                            group: self.ordinate + '_' +
                                    value.toLowerCase().replace(/[\s\/]+/g,'_'),
                            text: value,
                            color: COLOR_PALETTE[index % COLOR_PALETTE.length]
                        };
                    }).value();

            results = _(results).chain()
                .groupBy(function (record) { return record[self.abscissa]; })
                .map(function (records) {
                    var r = {};
                    // second argument is coerced to a str, no good for boolean
                    r[self.abscissa] = records[0][self.abscissa];
                    _(records).each(function (record) {
                        var key = _.str.sprintf('%s_%s',
                            self.ordinate,
                            record[self.group_field].toLowerCase().replace(/[\s\/]+/g,'_'));
                        r[key] = record[self.ordinate];
                    });
                    return r;
                })
                .value();
        }
        var abscissa_description = {
            title: "" + this.fields[this.abscissa].string + "",
            template: function (obj) {
                return obj[self.abscissa] || 'Undefined';
            }
        };
        var ordinate_description = {
            lines: true,
            title: "" + this.fields[this.ordinate].string + ""
        };

        var x_axis, y_axis;
        if (self.orientation == 'horizontal') {
            x_axis = ordinate_description;
            y_axis = abscissa_description;
        } else {
            x_axis = abscissa_description;
            y_axis = ordinate_description;
        }
        var renderer = function () {
            if (self.$element.is(':hidden')) {
                self.renderer = setTimeout(renderer, 100);
                return;
            }
            self.renderer = null;
            var bar_chart = new dhtmlXChart({
                view: view_chart,
                container: self.element_id+"-barchart",
                value:"#"+group_list[0].group+"#",
                gradient: "3d",
                border: false,
                width: 1024,
                tooltip:{
                    template: _.str.sprintf("#%s#, %s=#%s#",
                        self.abscissa, group_list[0].text, group_list[0].group)
                },
                radius: 0,
                color:group_list[0].color,
                origin:0,
                xAxis: x_axis,
                yAxis: y_axis,
                padding: {
                    left: 75
                },
                legend: {
                    values: group_list,
                    align:"left",
                    valign:"top",
                    layout: "x",
                    marker: {
                        type:"round",
                        width:12
                    }
                }
            });
            for (var m = 1; m<group_list.length;m++){
                var column = group_list[m];
                if (column.group === self.group_field) { continue; }
                bar_chart.addSeries({
                    value: "#"+column.group+"#",
                    tooltip:{
                        template: _.str.sprintf("#%s#, %s=#%s#",
                            self.abscissa, column.text, column.group)
                    },
                    color: column.color
                });
            }
            bar_chart.parse(results, "json");
            self.$element.find("#"+self.element_id+"-barchart").height(
                self.$element.find("#"+self.element_id+"-barchart").height()+50);
            bar_chart.attachEvent("onItemClick", function(id) {
                self.open_list_view(bar_chart.get(id));
            });
        };
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this.renderer = setTimeout(renderer, 0);
    },
    schedule_pie: function(result) {
        var self = this;
        var renderer = function () {
            if (self.$element.is(':hidden')) {
                self.renderer = setTimeout(renderer, 100);
                return;
            }
            self.renderer = null;
            var chart =  new dhtmlXChart({
                view:"pie3D",
                container:self.element_id+"-piechart",
                value:"#"+self.ordinate+"#",
                pieInnerText:function(obj) {
                    var sum = chart.sum("#"+self.ordinate+"#");
                    var val = obj[self.ordinate] / sum * 100 ;
                    return val.toFixed(1) + "%";
                },
                tooltip:{
                    template:"#"+self.abscissa+"#"+"="+"#"+self.ordinate+"#"
                },
                gradient:"3d",
                height: 20,
                radius: 200,
                legend: {
                    width: 300,
                    align:"left",
                    valign:"top",
                    layout: "x",
                    marker:{
                        type:"round",
                        width:12
                    },
                    template:function(obj){
                        return obj[self.abscissa] || 'Undefined';
                    }
                }
            });
            chart.parse(result,"json");
            chart.attachEvent("onItemClick", function(id) {
                self.open_list_view(chart.get(id));
            });
        };
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this.renderer = setTimeout(renderer, 0);
    },
    open_list_view : function (id){
        var self = this;
        // unconditionally nuke tooltips before switching view
        $(".dhx_tooltip").remove('div');
        id = id[this.abscissa];
        if (typeof id == 'object'){
            id = id[0];
        }

        var record_id = this.abscissa;
        var modes = ["list", "form", "graph"];
        var views = [];
        _.each(modes, function(mode) {
            var view = [false, mode];
            if (self.fields.views && self.fields.views[mode]) {
                view.push(self.fields.views[mode]);
            }
            views.push(view);
        });
        this.do_action({
            "res_model" : this.dataset.model,
            "domain" : [[record_id, '=', id], ['id','in',this.dataset.ids]],
            "views" : views,
            "type" : "ir.actions.act_window",
            "auto_search" : true,
            "view_type" : "list",
            "view_mode" : "list"
        });
    },

    do_search: function(domain, context, group_by) {
        var self = this;
        return $.when(this.is_loaded).pipe(function() {
            // TODO: handle non-empty group_by with read_group?
            if (!_(group_by).isEmpty()) {
                self.abscissa = group_by[0];
            } else {
                self.abscissa = self.first_field;
            }
            return self.dataset.read_slice(self.list_fields(), {}, $.proxy(self, 'schedule_chart'));
        });
    }
});
};
// vim:et fdc=0 fdl=0:
<|endoftext|>


<commit_before>import external from './../../externalModules.js';

const state = {
  drawColorId: 0,
  radius: 10,
  minRadius: 1,
  maxRadius: 50,
  alpha: 0.4,
  renderBrushIfHiddenButActive: true,
  hiddenButActiveAlpha: 0.2,
  colorMapId: 'BrushColorMap',
  visibleSegmentations: {},
  imageBitmapCache: {},
  segmentationMetadata: {},
};

const setters = {
  /**
   * Sets the brush radius, account for global min/max radius
   *
   * @param {number} radius
   * @returns {void}
   */
  radius: radius => {
    state.radius = Math.min(Math.max(radius, state.minRadius), state.maxRadius);
  },

  /**
   * TODO: Should this be a init config property?
   * Sets the brush color map to something other than the default
   *
   * @param  {Array} colors An array of 4D [red, green, blue, alpha] arrays.
   * @returns {void}
   */
  brushColorMap: colors => {
    const colormap = external.cornerstone.colors.getColormap(state.colorMapId);

    colormap.setNumberOfColors(colors.length);

    for (let i = 0; i < colors.length; i++) {
      colormap.setColor(i, colors[i]);
    }
  },
  elementVisible: enabledElement => {
    if (!external.cornerstone) {
      return;
    }

    const cornerstoneEnabledElement = external.cornerstone.getEnabledElement(
      enabledElement
    );

    const enabledElementUID = cornerstoneEnabledElement.uuid;
    const colormap = external.cornerstone.colors.getColormap(state.colorMapId);
    const numberOfColors = colormap.getNumberOfColors();

    state.visibleSegmentations[enabledElementUID] = [];

    for (let i = 0; i < numberOfColors; i++) {
      state.visibleSegmentations[enabledElementUID].push(true);
    }
  },
  brushVisibilityForElement: (enabledElementUID, segIndex, visible = true) => {
    if (!state.visibleSegmentations[enabledElementUID]) {
      state.imageBitmapCache[enabledElementUID] = [];
    }

    state.visibleSegmentations[enabledElementUID][segIndex] = visible;
  },
  imageBitmapCacheForElement: (enabledElementUID, segIndex, imageBitmap) => {
    if (!state.imageBitmapCache[enabledElementUID]) {
      state.imageBitmapCache[enabledElementUID] = [];
    }

    state.imageBitmapCache[enabledElementUID][segIndex] = imageBitmap;
  },
  clearImageBitmapCacheForElement: enabledElementUID => {
    state.imageBitmapCache[enabledElementUID] = [];
  },
  metadata: (seriesInstanceUid, segIndex, metadata) => {
    if (!state.segmentationMetadata[seriesInstanceUid]) {
      state.segmentationMetadata[seriesInstanceUid] = [];
    }

    state.segmentationMetadata[seriesInstanceUid][segIndex] = metadata;
  },
};

const getters = {
  imageBitmapCacheForElement: enabledElementUID => {
    if (!state.imageBitmapCache[enabledElementUID]) {
      return null;
    }

    return state.imageBitmapCache[enabledElementUID];
  },
  visibleSegmentationsForElement: enabledElementUID => {
    if (!state.visibleSegmentations[enabledElementUID]) {
      return null;
    }

    return state.visibleSegmentations[enabledElementUID];
  },

  /**
   * Retrieves series-specific brush segmentation metadata.
   * @public
   * @function metadata
   * @param {string} seriesInstanceUid - The seriesInstanceUid of the scan.
   * @param {number} [segIndex] - The segmentation index.
   *
   * @returns {Object[]|Object} An array of segmentation metadata, or specifc
   *                            segmentation data if segIndex is defined.
   */
  metadata: (seriesInstanceUid, segIndex) => {
    if (!state.segmentationMetadata[seriesInstanceUid]) {
      return;
    }

    if (segIndex !== undefined) {
      return state.segmentationMetadata[seriesInstanceUid][segIndex];
    }

    return state.segmentationMetadata[seriesInstanceUid];
  },
};

/**
 * EnabledElementCallback - Element specific initilisation.
 * @public
 * @param  {Object} enabledElement - The element on which the module is
 *                                  being initialised.
 * @returns {void}
 */
function enabledElementCallback(enabledElement) {
  setters.elementVisible(enabledElement);
}

/**
 * RemoveEnabledElementCallback - Element specific memory cleanup.
 * @public
 * @param  {Object} enabledElement  The element being removed.
 * @returns {void}
 */
// TODO -> Test this before adding it to the module.
function removeEnabledElementCallback(enabledElement) {
  if (!external.cornerstone) {
    return;
  }

  const cornerstoneEnabledElement = external.cornerstone.getEnabledElement(
    enabledElement
  );

  const enabledElementUID = cornerstoneEnabledElement.uuid;
  const colormap = external.cornerstone.colors.getColormap(state.colorMapId);
  const numberOfColors = colormap.getNumberOfColors();

  // Remove enabledElement specific data.
  delete state.visibleSegmentations[enabledElementUID];
  delete state.imageBitmapCache[enabledElementUID];
}

/**
 * OnRegisterCallback - Initialise the module when a new element is added.
 * @public
 * @returns {void}
 */
function onRegisterCallback() {
  _initDefaultColorMap();
}

export default {
  state,
  onRegisterCallback,
  enabledElementCallback,
  getters,
  setters,
};

const distinctColors = [
  [230, 25, 75, 255],
  [60, 180, 175, 255],
  [255, 225, 25, 255],
  [0, 130, 200, 255],
  [245, 130, 48, 255],
  [145, 30, 180, 255],
  [70, 240, 240, 255],
  [240, 50, 230, 255],
  [210, 245, 60, 255],
  [250, 190, 190, 255],
  [0, 128, 128, 255],
  [230, 190, 255, 255],
  [170, 110, 40, 255],
  [255, 250, 200, 255],
  [128, 0, 0, 255],
  [170, 255, 195, 255],
  [128, 128, 0, 255],
  [255, 215, 180, 255],
  [0, 0, 128, 255],
];

let colorPairIndex = 0;

function _initDefaultColorMap() {
  const defaultSegmentationCount = 19;
  const colormap = external.cornerstone.colors.getColormap(state.colorMapId);

  colormap.setNumberOfColors(defaultSegmentationCount);

  /*
    19 Colors selected to be as distinct from each other as possible,
    and ordered such that between each index you make large jumps around the
    color wheel. If defaultSegmentationCount is greater than 19, generate a
    random linearly interperlated color between 2 colors.
  */
  for (let i = 0; i < defaultSegmentationCount; i++) {
    if (i < distinctColors.length) {
      colormap.setColor(i, distinctColors[i]);
    } else {
      colormap.setColor(i, _generateInterpolatedColor());
    }
  }
}

/**
 * _generateInterpolatedColor -  generates a color interpolated between two
 *                              colors. Humans can only distinguish between
 *                              ~15-20 colors, so this is the best we can do.
 * @private
 *
 * @returns {type}  description
 */
function _generateInterpolatedColor() {
  const randIndicies = _getNextColorPair();
  const fraction = Math.random();
  const interpolatedColor = [];

  for (let i = 0; i < 4; i++) {
    interpolatedColor.push(
      Math.floor(
        fraction * distinctColors[randIndicies[0]][i] +
          (1.0 - fraction) * distinctColors[randIndicies[1]][i]
      )
    );
  }

  return interpolatedColor;
}

/**
 * _getNextColorPair - returns the next pair of indicies to interpolate between.
 *
 * @private
 *
 * @returns {Array} An array containing the two indicies.
 */
function _getNextColorPair() {
  const indexPair = [colorPairIndex];

  if (colorPairIndex < distinctColors.length - 1) {
    colorPairIndex++;
    indexPair.push(colorPairIndex);
  } else {
    colorPairIndex = 0;
    indexPair.push(colorPairIndex);
  }

  return indexPair;
}
<commit_msg>feat(brushModule): Method to add an element by UID to bust bitmap cache<commit_after>import external from './../../externalModules.js';

const state = {
  drawColorId: 0,
  radius: 10,
  minRadius: 1,
  maxRadius: 50,
  alpha: 0.4,
  renderBrushIfHiddenButActive: true,
  hiddenButActiveAlpha: 0.2,
  colorMapId: 'BrushColorMap',
  visibleSegmentations: {},
  imageBitmapCache: {},
  segmentationMetadata: {},
  //
  invalidatedEnabledElements: [],
};

const setters = {
  /**
   * Sets the brush radius, account for global min/max radius
   *
   * @param {number} radius
   * @returns {void}
   */
  radius: radius => {
    state.radius = Math.min(Math.max(radius, state.minRadius), state.maxRadius);
  },

  forceUpdateImageForElement(enabledElementUID) {
    if (!state.invalidatedEnabledElements.includes(enabledElementUID)) {
      state.invalidatedEnabledElements.push(enabledElementUID);
    }
  },

  /**
   * TODO: Should this be a init config property?
   * Sets the brush color map to something other than the default
   *
   * @param  {Array} colors An array of 4D [red, green, blue, alpha] arrays.
   * @returns {void}
   */
  brushColorMap: colors => {
    const colormap = external.cornerstone.colors.getColormap(state.colorMapId);

    colormap.setNumberOfColors(colors.length);

    for (let i = 0; i < colors.length; i++) {
      colormap.setColor(i, colors[i]);
    }
  },
  elementVisible: enabledElement => {
    if (!external.cornerstone) {
      return;
    }

    const cornerstoneEnabledElement = external.cornerstone.getEnabledElement(
      enabledElement
    );

    const enabledElementUID = cornerstoneEnabledElement.uuid;
    const colormap = external.cornerstone.colors.getColormap(state.colorMapId);
    const numberOfColors = colormap.getNumberOfColors();

    state.visibleSegmentations[enabledElementUID] = [];

    for (let i = 0; i < numberOfColors; i++) {
      state.visibleSegmentations[enabledElementUID].push(true);
    }
  },
  brushVisibilityForElement: (enabledElementUID, segIndex, visible = true) => {
    if (!state.visibleSegmentations[enabledElementUID]) {
      state.imageBitmapCache[enabledElementUID] = [];
    }

    state.visibleSegmentations[enabledElementUID][segIndex] = visible;
  },
  imageBitmapCacheForElement: (enabledElementUID, segIndex, imageBitmap) => {
    if (!state.imageBitmapCache[enabledElementUID]) {
      state.imageBitmapCache[enabledElementUID] = [];
    }

    state.imageBitmapCache[enabledElementUID][segIndex] = imageBitmap;
  },
  clearImageBitmapCacheForElement: enabledElementUID => {
    state.imageBitmapCache[enabledElementUID] = [];
  },
  metadata: (seriesInstanceUid, segIndex, metadata) => {
    if (!state.segmentationMetadata[seriesInstanceUid]) {
      state.segmentationMetadata[seriesInstanceUid] = [];
    }

    state.segmentationMetadata[seriesInstanceUid][segIndex] = metadata;
  },
};

const getters = {
  imageBitmapCacheForElement: enabledElementUID => {
    if (!state.imageBitmapCache[enabledElementUID]) {
      return null;
    }

    return state.imageBitmapCache[enabledElementUID];
  },
  visibleSegmentationsForElement: enabledElementUID => {
    if (!state.visibleSegmentations[enabledElementUID]) {
      return null;
    }

    return state.visibleSegmentations[enabledElementUID];
  },

  /**
   * Retrieves series-specific brush segmentation metadata.
   * @public
   * @function metadata
   * @param {string} seriesInstanceUid - The seriesInstanceUid of the scan.
   * @param {number} [segIndex] - The segmentation index.
   *
   * @returns {Object[]|Object} An array of segmentation metadata, or specifc
   *                            segmentation data if segIndex is defined.
   */
  metadata: (seriesInstanceUid, segIndex) => {
    if (!state.segmentationMetadata[seriesInstanceUid]) {
      return;
    }

    if (segIndex !== undefined) {
      return state.segmentationMetadata[seriesInstanceUid][segIndex];
    }

    return state.segmentationMetadata[seriesInstanceUid];
  },
};

/**
 * EnabledElementCallback - Element specific initilisation.
 * @public
 * @param  {Object} enabledElement - The element on which the module is
 *                                  being initialised.
 * @returns {void}
 */
function enabledElementCallback(enabledElement) {
  setters.elementVisible(enabledElement);
}

/**
 * RemoveEnabledElementCallback - Element specific memory cleanup.
 * @public
 * @param  {Object} enabledElement  The element being removed.
 * @returns {void}
 */
// TODO -> Test this before adding it to the module.
function removeEnabledElementCallback(enabledElement) {
  if (!external.cornerstone) {
    return;
  }

  const cornerstoneEnabledElement = external.cornerstone.getEnabledElement(
    enabledElement
  );

  const enabledElementUID = cornerstoneEnabledElement.uuid;
  const colormap = external.cornerstone.colors.getColormap(state.colorMapId);
  const numberOfColors = colormap.getNumberOfColors();

  // Remove enabledElement specific data.
  delete state.visibleSegmentations[enabledElementUID];
  delete state.imageBitmapCache[enabledElementUID];
}

/**
 * OnRegisterCallback - Initialise the module when a new element is added.
 * @public
 * @returns {void}
 */
function onRegisterCallback() {
  _initDefaultColorMap();
}

export default {
  state,
  onRegisterCallback,
  enabledElementCallback,
  getters,
  setters,
};

const distinctColors = [
  [230, 25, 75, 255],
  [60, 180, 175, 255],
  [255, 225, 25, 255],
  [0, 130, 200, 255],
  [245, 130, 48, 255],
  [145, 30, 180, 255],
  [70, 240, 240, 255],
  [240, 50, 230, 255],
  [210, 245, 60, 255],
  [250, 190, 190, 255],
  [0, 128, 128, 255],
  [230, 190, 255, 255],
  [170, 110, 40, 255],
  [255, 250, 200, 255],
  [128, 0, 0, 255],
  [170, 255, 195, 255],
  [128, 128, 0, 255],
  [255, 215, 180, 255],
  [0, 0, 128, 255],
];

let colorPairIndex = 0;

function _initDefaultColorMap() {
  const defaultSegmentationCount = 19;
  const colormap = external.cornerstone.colors.getColormap(state.colorMapId);

  colormap.setNumberOfColors(defaultSegmentationCount);

  /*
    19 Colors selected to be as distinct from each other as possible,
    and ordered such that between each index you make large jumps around the
    color wheel. If defaultSegmentationCount is greater than 19, generate a
    random linearly interperlated color between 2 colors.
  */
  for (let i = 0; i < defaultSegmentationCount; i++) {
    if (i < distinctColors.length) {
      colormap.setColor(i, distinctColors[i]);
    } else {
      colormap.setColor(i, _generateInterpolatedColor());
    }
  }
}

/**
 * _generateInterpolatedColor -  generates a color interpolated between two
 *                              colors. Humans can only distinguish between
 *                              ~15-20 colors, so this is the best we can do.
 * @private
 *
 * @returns {type}  description
 */
function _generateInterpolatedColor() {
  const randIndicies = _getNextColorPair();
  const fraction = Math.random();
  const interpolatedColor = [];

  for (let i = 0; i < 4; i++) {
    interpolatedColor.push(
      Math.floor(
        fraction * distinctColors[randIndicies[0]][i] +
          (1.0 - fraction) * distinctColors[randIndicies[1]][i]
      )
    );
  }

  return interpolatedColor;
}

/**
 * _getNextColorPair - returns the next pair of indicies to interpolate between.
 *
 * @private
 *
 * @returns {Array} An array containing the two indicies.
 */
function _getNextColorPair() {
  const indexPair = [colorPairIndex];

  if (colorPairIndex < distinctColors.length - 1) {
    colorPairIndex++;
    indexPair.push(colorPairIndex);
  } else {
    colorPairIndex = 0;
    indexPair.push(colorPairIndex);
  }

  return indexPair;
}
<|endoftext|>


<commit_before>import {
  moduleFor,
  test
} from 'ember-qunit';

moduleFor('service:icon', {
  // Specify the other units that are required for this test.
  // needs: ['service:foo']
});

// Replace this with your real tests.
test('it exists', function(assert) {
  var service = this.subject();
  assert.ok(service);
});

test('should get a single svg icon', function(assert) {
	assert.expect(0);
});<commit_msg>Add basic unit test for icon service<commit_after>import {
	moduleFor,
	test
}
from 'ember-qunit';

import Ember from 'ember';

moduleFor('service:icon', {
	// Specify the other units that are required for this test.
	// needs: ['service:foo']
});

function updateDefaults(svg) {
	svg = Ember.$(svg);

	console.log('before: ', svg[0].outerHTML);

	svg[0].setAttribute('xmlns', 'http://www.w3.org/2000/svg');

	svg[0].setAttribute('fit', '');
	svg[0].setAttribute('height', '100%');
	svg[0].setAttribute('width', '100%');
	svg[0].setAttribute('preserveAspectRatio', 'xMidYMid meet');

	svg[0].setAttribute('viewBox', ('0 0 24 24'));

	svg.css({
		'pointer-events': 'none',
		'display': 'block'
	});

	console.log('after: ', svg[0].outerHTML);

	return svg[0].outerHTML;
}

// Replace this with your real tests.
test('it exists', function(assert) {
	var service = this.subject();
	assert.ok(service);
});

test('should get a single svg icon', function(assert) {

	var icon,
		expected = updateDefaults('<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g><path d="M12 2c-5.53 0-10 4.47-10 10s4.47 10 10 10 10-4.47 10-10-4.47-10-10-10zm5 13.59l-1.41 1.41-3.59-3.59-3.59 3.59-1.41-1.41 3.59-3.59-3.59-3.59 1.41-1.41 3.59 3.59 3.59-3.59 1.41 1.41-3.59 3.59 3.59 3.59z"/></g></svg>'),
		service = this.subject();

	Ember.run(() => {
		service.getIcon('cancel').then((result) => {
			icon = result;
		});
	});

	console.log('expected:', expected);
	console.log('actual:', icon[0].outerHTML);

	assert.equal(icon[0].outerHTML, expected);
});<|endoftext|>


<commit_before>import productionsApi from '../api/productions'
import { sortProductions, sortByName } from '../../lib/sorting'
import {
  LOAD_PRODUCTIONS_START,
  LOAD_PRODUCTIONS_ERROR,
  LOAD_PRODUCTIONS_END,

  LOAD_OPEN_PRODUCTIONS_START,
  LOAD_OPEN_PRODUCTIONS_ERROR,
  LOAD_OPEN_PRODUCTIONS_END,

  LOAD_PRODUCTION_STATUS_START,
  LOAD_PRODUCTION_STATUS_ERROR,
  LOAD_PRODUCTION_STATUS_END,

  EDIT_PRODUCTION_START,
  EDIT_PRODUCTION_ERROR,
  EDIT_PRODUCTION_END,

  DELETE_PRODUCTION_START,
  DELETE_PRODUCTION_ERROR,
  DELETE_PRODUCTION_END,

  SET_CURRENT_PRODUCTION,
  PRODUCTION_PICTURE_FILE_SELECTED,
  PRODUCTION_AVATAR_UPLOADED,

  TEAM_ADD_PERSON,
  TEAM_REMOVE_PERSON,

  RESET_ALL
} from '../mutation-types'

const initialState = {
  productions: [],
  productionMap: {},
  openProductions: [],
  productionStatus: [],
  currentProduction: null,
  productionAvatarFormData: null,

  isProductionsLoading: false,
  isProductionsLoadingError: false,
  isOpenProductionsLoading: false,

  editProduction: {
    isLoading: false,
    isError: false
  },

  deleteProduction: {
    isLoading: false,
    isError: false
  },

  assetsPath: {name: 'open-productions'},
  assetTypesPath: {name: 'open-productions'},
  shotsPath: {name: 'open-productions'},
  sequencesPath: {name: 'open-productions'},
  episodesPath: {name: 'open-productions'},
  breakdownPath: {name: 'open-productions'},
  playlistsPath: {name: 'open-productions'},
  teamPath: {name: 'open-productions'}
}

let state = {...initialState}

const helpers = {
  getProductionComponentPath (routeName, productionId) {
    return {
      name: routeName,
      params: {
        production_id: productionId
      }
    }
  }
}

const getters = {
  productions: state => state.productions,
  productionMap: state => state.productionMap,
  openProductions: state => state.openProductions,
  productionStatus: state => state.productionStatus,

  productionAvatarFormData: state => state.productionAvatarFormData,

  isProductionsLoading: state => state.isProductionsLoading,
  isProductionsLoadingError: state => state.isProductionsLoadingError,
  isOpenProductionsLoading: state => state.isOpenProductionsLoading,

  editProduction: state => state.editProduction,
  deleteProduction: state => state.deleteProduction,

  assetsPath: state => state.assetsPath,
  assetTypesPath: state => state.assetTypesPath,
  shotsPath: state => state.shotsPath,
  sequencesPath: state => state.sequencesPath,
  episodesPath: state => state.episodesPath,
  breakdownPath: state => state.breakdownPath,
  playlistsPath: state => state.playlistsPath,
  teamPath: state => state.teamPath,

  currentProduction: (state) => {
    if (state.currentProduction) {
      return state.currentProduction
    } else if (state.openProductions.length > 0) {
      return state.openProductions[0]
    } else {
      return null
    }
  },
  getProduction: (state, getters) => (id) => {
    return state.productions.find(
      (production) => production.id === id
    )
  },
  getProductionStatus: (state, getters) => (id) => {
    return state.productionStatus.find(
      (productionStatus) => productionStatus.id === id
    )
  },
  getProductionStatusOptions: state => state.productionStatus.map(
    (status) => { return { label: status.name, value: status.id } }
  ),
  getOpenProductionOptions: state => state.openProductions.map(
    (production) => { return { label: production.name, value: production.id } }
  )
}

const actions = {

  loadProductionStatus ({ commit, state }, callback) {
    commit(LOAD_PRODUCTION_STATUS_START)
    productionsApi.getProductionStatus((err, productionStatus) => {
      if (err) commit(LOAD_PRODUCTION_STATUS_ERROR)
      else commit(LOAD_PRODUCTION_STATUS_END, productionStatus)
      if (callback) callback(err)
    })
  },

  loadOpenProductions ({ commit, state }, callback) {
    commit(LOAD_OPEN_PRODUCTIONS_START)
    productionsApi.getOpenProductions((err, productions) => {
      if (err) commit(LOAD_OPEN_PRODUCTIONS_ERROR)
      else {
        commit(LOAD_OPEN_PRODUCTIONS_END, productions)
        if (!state.currentProduction && productions.length > 0) {
          commit(SET_CURRENT_PRODUCTION, productions[0].id)
        }
      }
      if (callback) callback(err)
    })
  },

  loadProductions ({ commit, state }, callback) {
    commit(LOAD_PRODUCTIONS_START)
    productionsApi.getProductions((err, productions) => {
      if (err) commit(LOAD_PRODUCTIONS_ERROR)
      else commit(LOAD_PRODUCTIONS_END, productions)
      if (callback) callback(err)
    })
  },

  newProduction ({ commit, state }, { data, callback }) {
    commit(EDIT_PRODUCTION_START, data)
    productionsApi.newProduction(data, (err, production) => {
      if (err) {
        commit(EDIT_PRODUCTION_ERROR)
      } else {
        commit(EDIT_PRODUCTION_END, production)
      }
      if (callback) callback(err, production)
    })
  },

  editProduction ({ commit, state }, payload) {
    commit(EDIT_PRODUCTION_START)
    productionsApi.updateProduction(payload.data, (err, production) => {
      if (err) {
        commit(EDIT_PRODUCTION_ERROR)
      } else {
        commit(EDIT_PRODUCTION_END, production)
      }
      if (payload.callback) payload.callback(err)
    })
  },

  deleteProduction ({ commit, state }, payload) {
    commit(DELETE_PRODUCTION_START)
    const production = payload.production
    productionsApi.deleteProduction(production, (err) => {
      if (err) {
        commit(DELETE_PRODUCTION_ERROR)
      } else {
        commit(DELETE_PRODUCTION_END, production)
      }
      if (payload.callback) payload.callback(err)
    })
  },

  setProduction ({commit}, productionId) {
    commit(SET_CURRENT_PRODUCTION, productionId)
  },

  storeProductionPicture ({ commit }, formData) {
    commit(PRODUCTION_PICTURE_FILE_SELECTED, formData)
  },

  uploadProductionAvatar ({ commit, state }, productionId) {
    return new Promise((resolve, reject) => {
      productionsApi.postAvatar(
        productionId,
        state.productionAvatarFormData,
        (err) => {
          commit(PRODUCTION_AVATAR_UPLOADED, productionId)
          if (err) reject(err)
          else resolve()
        }
      )
    })
  },

  addPersonToTeam ({ commit, state }, person) {
    return new Promise((resolve, reject) => {
      commit(TEAM_ADD_PERSON, person.id)
      return productionsApi.addPersonToTeam(
        state.currentProduction.id,
        person.id
      )
        .then(resolve)
        .catch(reject)
    })
  },

  removePersonFromTeam ({ commit, state }, person) {
    return new Promise((resolve, reject) => {
      commit(TEAM_REMOVE_PERSON, person.id)
      return productionsApi.removePersonFromTeam(
        state.currentProduction.id,
        person.id
      )
        .then(resolve)
        .catch(reject)
    })
  }
}

const mutations = {
  [LOAD_PRODUCTIONS_START] (state) {
    state.productions = []
    state.isProductionsLoading = true
    state.isProductionsLoadingError = false
  },

  [LOAD_PRODUCTIONS_ERROR] (state) {
    state.productions = []
    state.isProductionsLoading = false
    state.isProductionsLoadingError = true
  },

  [LOAD_PRODUCTIONS_END] (state, productions) {
    state.isProductionsLoading = false
    state.isProductionsLoadingError = false
    state.productions = sortProductions(productions)

    const productionMap = {}
    state.productions.forEach((production) => {
      productionMap[production.id] = production
    })
    state.productionMap = productionMap
  },

  [LOAD_OPEN_PRODUCTIONS_START] (state) {
    state.isOpenProductionsLoading = true
    state.openProductions = []
  },
  [LOAD_OPEN_PRODUCTIONS_ERROR] (state) {
    state.isOpenProductionsLoading = false
  },
  [LOAD_OPEN_PRODUCTIONS_END] (state, productions) {
    state.isOpenProductionsLoading = false
    state.openProductions = sortByName(productions)

    const productionMap = {}
    productions.forEach((production) => {
      productionMap[production.id] = production
    })
    state.productionMap = productionMap
  },

  [LOAD_PRODUCTION_STATUS_START] (state) {
    state.productionStatus = []
  },
  [LOAD_PRODUCTION_STATUS_ERROR] (state) {
  },
  [LOAD_PRODUCTION_STATUS_END] (state, productionStatus) {
    state.productionStatus = productionStatus
  },

  [EDIT_PRODUCTION_START] (state, data) {
    state.editProduction.isLoading = true
    state.editProduction.isError = false
  },
  [EDIT_PRODUCTION_ERROR] (state) {
    state.editProduction.isLoading = false
    state.editProduction.isError = true
  },

  [EDIT_PRODUCTION_END] (state, newProduction) {
    const production = getters.getProduction(state)(newProduction.id)
    const productionStatus = getters.getProductionStatus(state)(
      newProduction.project_status_id
    )
    newProduction.project_status_name = productionStatus.name

    if (production) {
      const openProductionIndex = state.openProductions.findIndex(
        (openProduction) => openProduction.id === newProduction.id
      )
      if (newProduction.project_status_id) {
        if (
          openProductionIndex >= 0 &&
          production.project_status_id !== newProduction.project_status_id
        ) {
          state.openProductions.splice(openProductionIndex, 1)
        } else {
          state.openProductions.push(production)
          state.openProductions = sortByName(state.openProductions)
        }
      }

      Object.assign(production, newProduction)
    } else {
      state.productions.push(newProduction)
      state.productionMap[newProduction.id] = newProduction
      state.openProductions.push(newProduction)
      state.productions = sortProductions(state.productions)
      state.openProductions = sortByName(state.openProductions)
    }
    state.editProduction = {
      isLoading: false,
      isError: false
    }
  },

  [DELETE_PRODUCTION_START] (state) {
    state.deleteProduction = {
      isLoading: true,
      isError: false
    }
  },
  [DELETE_PRODUCTION_ERROR] (state) {
    state.deleteProduction = {
      isLoading: false,
      isError: true
    }
  },
  [DELETE_PRODUCTION_END] (state, productionToDelete) {
    const productionToDeleteIndex = state.productions.findIndex(
      (production) => production.id === productionToDelete.id
    )
    state.productions.splice(productionToDeleteIndex, 1)

    state.deleteProduction = {
      isLoading: false,
      isError: false
    }
  },

  [PRODUCTION_PICTURE_FILE_SELECTED] (state, formData) {
    state.productionAvatarFormData = formData
  },

  [PRODUCTION_AVATAR_UPLOADED] (state, productionId) {
    const production = state.productionMap[productionId]
    if (production) production.has_avatar = true
  },

  [SET_CURRENT_PRODUCTION] (state, currentProductionId) {
    const production = state.openProductions.find(
      (production) => production.id === currentProductionId
    )
    state.currentProduction = production
    state.assetsPath = helpers.getProductionComponentPath(
      'assets', currentProductionId)
    state.assetTypesPath = helpers.getProductionComponentPath(
      'production-asset-types', currentProductionId)
    state.shotsPath = helpers.getProductionComponentPath(
      'shots', currentProductionId)
    state.sequencesPath = helpers.getProductionComponentPath(
      'sequences', currentProductionId)
    state.episodesPath = helpers.getProductionComponentPath(
      'episodes', currentProductionId)
    state.breakdownPath = helpers.getProductionComponentPath(
      'breakdown', currentProductionId)
    state.playlistsPath = helpers.getProductionComponentPath(
      'playlists', currentProductionId)
    state.teamPath = helpers.getProductionComponentPath(
      'team', currentProductionId)
  },

  [TEAM_ADD_PERSON] (state, personId) {
    if (!state.currentProduction.team.find((pId) => pId === personId)) {
      state.currentProduction.team.push(personId)
    }
  },

  [TEAM_REMOVE_PERSON] (state, personId) {
    const personIndex = state.currentProduction.team.find(
      (pId) => pId === personId
    )
    if (personIndex) {
      state.currentProduction.team.splice(personIndex, 1)
    }
  },

  [RESET_ALL] (state) {
    Object.assign(state, {...initialState})
  }
}

export default {
  state,
  getters,
  actions,
  mutations
}
<commit_msg>Fix team member removal<commit_after>import productionsApi from '../api/productions'
import { sortProductions, sortByName } from '../../lib/sorting'
import {
  LOAD_PRODUCTIONS_START,
  LOAD_PRODUCTIONS_ERROR,
  LOAD_PRODUCTIONS_END,

  LOAD_OPEN_PRODUCTIONS_START,
  LOAD_OPEN_PRODUCTIONS_ERROR,
  LOAD_OPEN_PRODUCTIONS_END,

  LOAD_PRODUCTION_STATUS_START,
  LOAD_PRODUCTION_STATUS_ERROR,
  LOAD_PRODUCTION_STATUS_END,

  EDIT_PRODUCTION_START,
  EDIT_PRODUCTION_ERROR,
  EDIT_PRODUCTION_END,

  DELETE_PRODUCTION_START,
  DELETE_PRODUCTION_ERROR,
  DELETE_PRODUCTION_END,

  SET_CURRENT_PRODUCTION,
  PRODUCTION_PICTURE_FILE_SELECTED,
  PRODUCTION_AVATAR_UPLOADED,

  TEAM_ADD_PERSON,
  TEAM_REMOVE_PERSON,

  RESET_ALL
} from '../mutation-types'

const initialState = {
  productions: [],
  productionMap: {},
  openProductions: [],
  productionStatus: [],
  currentProduction: null,
  productionAvatarFormData: null,

  isProductionsLoading: false,
  isProductionsLoadingError: false,
  isOpenProductionsLoading: false,

  editProduction: {
    isLoading: false,
    isError: false
  },

  deleteProduction: {
    isLoading: false,
    isError: false
  },

  assetsPath: {name: 'open-productions'},
  assetTypesPath: {name: 'open-productions'},
  shotsPath: {name: 'open-productions'},
  sequencesPath: {name: 'open-productions'},
  episodesPath: {name: 'open-productions'},
  breakdownPath: {name: 'open-productions'},
  playlistsPath: {name: 'open-productions'},
  teamPath: {name: 'open-productions'}
}

let state = {...initialState}

const helpers = {
  getProductionComponentPath (routeName, productionId) {
    return {
      name: routeName,
      params: {
        production_id: productionId
      }
    }
  }
}

const getters = {
  productions: state => state.productions,
  productionMap: state => state.productionMap,
  openProductions: state => state.openProductions,
  productionStatus: state => state.productionStatus,

  productionAvatarFormData: state => state.productionAvatarFormData,

  isProductionsLoading: state => state.isProductionsLoading,
  isProductionsLoadingError: state => state.isProductionsLoadingError,
  isOpenProductionsLoading: state => state.isOpenProductionsLoading,

  editProduction: state => state.editProduction,
  deleteProduction: state => state.deleteProduction,

  assetsPath: state => state.assetsPath,
  assetTypesPath: state => state.assetTypesPath,
  shotsPath: state => state.shotsPath,
  sequencesPath: state => state.sequencesPath,
  episodesPath: state => state.episodesPath,
  breakdownPath: state => state.breakdownPath,
  playlistsPath: state => state.playlistsPath,
  teamPath: state => state.teamPath,

  currentProduction: (state) => {
    if (state.currentProduction) {
      return state.currentProduction
    } else if (state.openProductions.length > 0) {
      return state.openProductions[0]
    } else {
      return null
    }
  },
  getProduction: (state, getters) => (id) => {
    return state.productions.find(
      (production) => production.id === id
    )
  },
  getProductionStatus: (state, getters) => (id) => {
    return state.productionStatus.find(
      (productionStatus) => productionStatus.id === id
    )
  },
  getProductionStatusOptions: state => state.productionStatus.map(
    (status) => { return { label: status.name, value: status.id } }
  ),
  getOpenProductionOptions: state => state.openProductions.map(
    (production) => { return { label: production.name, value: production.id } }
  )
}

const actions = {

  loadProductionStatus ({ commit, state }, callback) {
    commit(LOAD_PRODUCTION_STATUS_START)
    productionsApi.getProductionStatus((err, productionStatus) => {
      if (err) commit(LOAD_PRODUCTION_STATUS_ERROR)
      else commit(LOAD_PRODUCTION_STATUS_END, productionStatus)
      if (callback) callback(err)
    })
  },

  loadOpenProductions ({ commit, state }, callback) {
    commit(LOAD_OPEN_PRODUCTIONS_START)
    productionsApi.getOpenProductions((err, productions) => {
      if (err) commit(LOAD_OPEN_PRODUCTIONS_ERROR)
      else {
        commit(LOAD_OPEN_PRODUCTIONS_END, productions)
        if (!state.currentProduction && productions.length > 0) {
          commit(SET_CURRENT_PRODUCTION, productions[0].id)
        }
      }
      if (callback) callback(err)
    })
  },

  loadProductions ({ commit, state }, callback) {
    commit(LOAD_PRODUCTIONS_START)
    productionsApi.getProductions((err, productions) => {
      if (err) commit(LOAD_PRODUCTIONS_ERROR)
      else commit(LOAD_PRODUCTIONS_END, productions)
      if (callback) callback(err)
    })
  },

  newProduction ({ commit, state }, { data, callback }) {
    commit(EDIT_PRODUCTION_START, data)
    productionsApi.newProduction(data, (err, production) => {
      if (err) {
        commit(EDIT_PRODUCTION_ERROR)
      } else {
        commit(EDIT_PRODUCTION_END, production)
      }
      if (callback) callback(err, production)
    })
  },

  editProduction ({ commit, state }, payload) {
    commit(EDIT_PRODUCTION_START)
    productionsApi.updateProduction(payload.data, (err, production) => {
      if (err) {
        commit(EDIT_PRODUCTION_ERROR)
      } else {
        commit(EDIT_PRODUCTION_END, production)
      }
      if (payload.callback) payload.callback(err)
    })
  },

  deleteProduction ({ commit, state }, payload) {
    commit(DELETE_PRODUCTION_START)
    const production = payload.production
    productionsApi.deleteProduction(production, (err) => {
      if (err) {
        commit(DELETE_PRODUCTION_ERROR)
      } else {
        commit(DELETE_PRODUCTION_END, production)
      }
      if (payload.callback) payload.callback(err)
    })
  },

  setProduction ({commit}, productionId) {
    commit(SET_CURRENT_PRODUCTION, productionId)
  },

  storeProductionPicture ({ commit }, formData) {
    commit(PRODUCTION_PICTURE_FILE_SELECTED, formData)
  },

  uploadProductionAvatar ({ commit, state }, productionId) {
    return new Promise((resolve, reject) => {
      productionsApi.postAvatar(
        productionId,
        state.productionAvatarFormData,
        (err) => {
          commit(PRODUCTION_AVATAR_UPLOADED, productionId)
          if (err) reject(err)
          else resolve()
        }
      )
    })
  },

  addPersonToTeam ({ commit, state }, person) {
    return new Promise((resolve, reject) => {
      commit(TEAM_ADD_PERSON, person.id)
      return productionsApi.addPersonToTeam(
        state.currentProduction.id,
        person.id
      )
        .then(resolve)
        .catch(reject)
    })
  },

  removePersonFromTeam ({ commit, state }, person) {
    return new Promise((resolve, reject) => {
      commit(TEAM_REMOVE_PERSON, person.id)
      return productionsApi.removePersonFromTeam(
        state.currentProduction.id,
        person.id
      )
        .then(resolve)
        .catch(reject)
    })
  }
}

const mutations = {
  [LOAD_PRODUCTIONS_START] (state) {
    state.productions = []
    state.isProductionsLoading = true
    state.isProductionsLoadingError = false
  },

  [LOAD_PRODUCTIONS_ERROR] (state) {
    state.productions = []
    state.isProductionsLoading = false
    state.isProductionsLoadingError = true
  },

  [LOAD_PRODUCTIONS_END] (state, productions) {
    state.isProductionsLoading = false
    state.isProductionsLoadingError = false
    state.productions = sortProductions(productions)

    const productionMap = {}
    state.productions.forEach((production) => {
      productionMap[production.id] = production
    })
    state.productionMap = productionMap
  },

  [LOAD_OPEN_PRODUCTIONS_START] (state) {
    state.isOpenProductionsLoading = true
    state.openProductions = []
  },
  [LOAD_OPEN_PRODUCTIONS_ERROR] (state) {
    state.isOpenProductionsLoading = false
  },
  [LOAD_OPEN_PRODUCTIONS_END] (state, productions) {
    state.isOpenProductionsLoading = false
    state.openProductions = sortByName(productions)

    const productionMap = {}
    productions.forEach((production) => {
      productionMap[production.id] = production
    })
    state.productionMap = productionMap
  },

  [LOAD_PRODUCTION_STATUS_START] (state) {
    state.productionStatus = []
  },
  [LOAD_PRODUCTION_STATUS_ERROR] (state) {
  },
  [LOAD_PRODUCTION_STATUS_END] (state, productionStatus) {
    state.productionStatus = productionStatus
  },

  [EDIT_PRODUCTION_START] (state, data) {
    state.editProduction.isLoading = true
    state.editProduction.isError = false
  },
  [EDIT_PRODUCTION_ERROR] (state) {
    state.editProduction.isLoading = false
    state.editProduction.isError = true
  },

  [EDIT_PRODUCTION_END] (state, newProduction) {
    const production = getters.getProduction(state)(newProduction.id)
    const productionStatus = getters.getProductionStatus(state)(
      newProduction.project_status_id
    )
    newProduction.project_status_name = productionStatus.name

    if (production) {
      const openProductionIndex = state.openProductions.findIndex(
        (openProduction) => openProduction.id === newProduction.id
      )
      if (newProduction.project_status_id) {
        if (
          openProductionIndex >= 0 &&
          production.project_status_id !== newProduction.project_status_id
        ) {
          state.openProductions.splice(openProductionIndex, 1)
        } else {
          state.openProductions.push(production)
          state.openProductions = sortByName(state.openProductions)
        }
      }

      Object.assign(production, newProduction)
    } else {
      state.productions.push(newProduction)
      state.productionMap[newProduction.id] = newProduction
      state.openProductions.push(newProduction)
      state.productions = sortProductions(state.productions)
      state.openProductions = sortByName(state.openProductions)
    }
    state.editProduction = {
      isLoading: false,
      isError: false
    }
  },

  [DELETE_PRODUCTION_START] (state) {
    state.deleteProduction = {
      isLoading: true,
      isError: false
    }
  },
  [DELETE_PRODUCTION_ERROR] (state) {
    state.deleteProduction = {
      isLoading: false,
      isError: true
    }
  },
  [DELETE_PRODUCTION_END] (state, productionToDelete) {
    const productionToDeleteIndex = state.productions.findIndex(
      (production) => production.id === productionToDelete.id
    )
    state.productions.splice(productionToDeleteIndex, 1)

    state.deleteProduction = {
      isLoading: false,
      isError: false
    }
  },

  [PRODUCTION_PICTURE_FILE_SELECTED] (state, formData) {
    state.productionAvatarFormData = formData
  },

  [PRODUCTION_AVATAR_UPLOADED] (state, productionId) {
    const production = state.productionMap[productionId]
    if (production) production.has_avatar = true
  },

  [SET_CURRENT_PRODUCTION] (state, currentProductionId) {
    const production = state.openProductions.find(
      (production) => production.id === currentProductionId
    )
    state.currentProduction = production
    state.assetsPath = helpers.getProductionComponentPath(
      'assets', currentProductionId)
    state.assetTypesPath = helpers.getProductionComponentPath(
      'production-asset-types', currentProductionId)
    state.shotsPath = helpers.getProductionComponentPath(
      'shots', currentProductionId)
    state.sequencesPath = helpers.getProductionComponentPath(
      'sequences', currentProductionId)
    state.episodesPath = helpers.getProductionComponentPath(
      'episodes', currentProductionId)
    state.breakdownPath = helpers.getProductionComponentPath(
      'breakdown', currentProductionId)
    state.playlistsPath = helpers.getProductionComponentPath(
      'playlists', currentProductionId)
    state.teamPath = helpers.getProductionComponentPath(
      'team', currentProductionId)
  },

  [TEAM_ADD_PERSON] (state, personId) {
    if (!state.currentProduction.team.find((pId) => pId === personId)) {
      state.currentProduction.team.push(personId)
    }
  },

  [TEAM_REMOVE_PERSON] (state, personId) {
    const personIndex = state.currentProduction.team.findIndex(
      (pId) => pId === personId
    )
    if (personIndex) {
      state.currentProduction.team.splice(personIndex, 1)
    }
  },

  [RESET_ALL] (state) {
    Object.assign(state, {...initialState})
  }
}

export default {
  state,
  getters,
  actions,
  mutations
}
<|endoftext|>


<commit_before>JCS = {
    // quicksearch timeout
    qto: null,

    init: function() {
        $(".flashbag div").css('marginLeft', function(index) {
            return -1 *( $(this).outerWidth() / 2);
        }).delay(4000).fadeOut(3000);
        $(".ajaxbag div").hide();

        // add the inquiry ajax actions
        $(".inquiry a").click(function() {
            if (!$(this).hasClass('ajax-loading2') && $(this).attr('href')) {
                $(this).addClass('ajax-loading2');
                var that = this;
                $.post($(this).attr('href'), function(data) {
                    $(that).removeClass('ajax-loading2');
                    JCS.showNotice(data);
                });
            }
            return false;
        });

        // quick search
        var orig_results_text = $("#search-results").html();

        var nf = new NiceField($("#quicksearch #q"), {
            clearHook: function() {
                JCS.qSubmit();
            },
            onChange: function() {
                clearTimeout(JCS.qto);
                JCS.qto = setTimeout('JCS.qSubmit()', 300);
            },
            select: true,
            focus: true
        });

        // search results height
        JCS.setSrHeight();
        $(window).resize(function(){
            JCS.setSrHeight();
        })

        $("#quicksearch").submit(function(){
            nf.start();
            $.post($(this).attr('action'), $(this).serialize(), function(data) {
                nf.stop();
                if ($("#quicksearch #q").attr('value') == '') {
                    $("#search-results").html(orig_results_text);
                }
                else {
                    $("#search-results").html(data);
                }
            });
            return false;
        });

    },

    qSubmit: function() {
        $("#quicksearch").submit();
    },
    setSrHeight: function() {
        $('#search-results').height($(window).innerHeight() - 170);
    },
    showAjaxLoader: function() {
        $(".ajaxbag .ajax-loader")
            .css('marginLeft', -1 * ($(".ajaxbag .ajax-loader").outerWidth() / 2))
            .show();
    },
    hideAjaxLoader: function() {
        $(".ajaxbag .ajax-loader").hide();
    },
    showNotice: function(notice) {
        JCS.hideAjaxLoader();
        $(".ajaxbag .ajax-notice")
            .stop()
            .clearQueue()
            .html(notice)
            .css({
                'marginLeft': -1 * ($(".ajaxbag .ajax-notice").outerWidth() / 2),
                'opacity': 1
            })
            .show()
            .delay(4000)
            .fadeOut(3000);
    },
    hideNotice: function() {
        $(".ajaxbag .ajax-notice")
            .stop()
            .clearQueue()
            .hide();
    }
}

NiceField = function(o, opt) {
    if (typeof(opt) == 'undefined') {
        opt = {};
    }
    this.o = $(o);
    this.opt = opt;
    this.container = '

';
    this.indibutt = '

';

    $(o).wrap(this.container).after(this.indibutt);
    $(o).parent().css({
        'height': $(o).outerHeight(),
        'width': $(o).outerWidth()
    });
    this.indi = $(o).next();
    $(this.indi).css({
        'height': $(o).outerHeight()
    });
    this.clear = $(o).next().next();
    $(this.clear).css({
        'height': $(o).outerHeight(),
        'width': $(o).outerHeight()
    }).click(function(){
        $(o).attr('value', '');
        if ($.isFunction(opt.clearHook)) {
            opt.clearHook();
        }
    });
    if ($.isFunction(opt.onChange)) {
        $(o).on('input', function() {
            opt.onChange();
        });
    }

    this.start = function() {
        $(this.indi).show();
        $(this.clear).hide();
    }
    this.stop = function() {
        $(this.indi).hide();
        $(this.clear).show();
    }
    return this;
}

// document ready
$(function() {
    JCS.init();

    //JCS.qSubmit();
});

<commit_msg>quick search and results<commit_after>JCS = {
    // quicksearch timeout
    qto: null,

    init: function() {
        $(".flashbag div").css('marginLeft', function(index) {
            return -1 *( $(this).outerWidth() / 2);
        }).delay(4000).fadeOut(3000);
        $(".ajaxbag div").hide();

        // add the inquiry ajax actions
        $(".inquiry a").click(function() {
            if (!$(this).hasClass('ajax-loading2') && $(this).attr('href')) {
                $(this).addClass('ajax-loading2');
                var that = this;
                $.post($(this).attr('href'), function(data) {
                    $(that).removeClass('ajax-loading2');
                    JCS.showNotice(data);
                });
            }
            return false;
        });

        // quick search
        var orig_results_text = $("#search-results").html();

        var nf = new NiceField($("#quicksearch #q"), {
            clearHook: function() {
                JCS.qSubmit();
            },
            onChange: function() {
                clearTimeout(JCS.qto);
                JCS.qto = setTimeout('JCS.qSubmit()', 300);
            },
            select: true,
            focus: true
        });

        // search results height
        JCS.setSrHeight();
        $(window).resize(function(){
            JCS.setSrHeight();
        })

        $("#quicksearch").submit(function(){
            nf.start();
            $.post($(this).attr('action'), $(this).serialize(), function(data) {
                nf.stop();
                if ($("#quicksearch #q").attr('value') == '') {
                    $("#search-results").html(orig_results_text);
                }
                else {
                    $("#search-results").html(data);
                }
            });
            return false;
        });

    },

    qSubmit: function() {
        $("#quicksearch").submit();
    },
    setSrHeight: function() {
        $('#search-results').height($(window).innerHeight() - 180);
    },
    showAjaxLoader: function() {
        $(".ajaxbag .ajax-loader")
            .css('marginLeft', -1 * ($(".ajaxbag .ajax-loader").outerWidth() / 2))
            .show();
    },
    hideAjaxLoader: function() {
        $(".ajaxbag .ajax-loader").hide();
    },
    showNotice: function(notice) {
        JCS.hideAjaxLoader();
        $(".ajaxbag .ajax-notice")
            .stop()
            .clearQueue()
            .html(notice)
            .css({
                'marginLeft': -1 * ($(".ajaxbag .ajax-notice").outerWidth() / 2),
                'opacity': 1
            })
            .show()
            .delay(4000)
            .fadeOut(3000);
    },
    hideNotice: function() {
        $(".ajaxbag .ajax-notice")
            .stop()
            .clearQueue()
            .hide();
    }
}

NiceField = function(o, opt) {
    if (typeof(opt) == 'undefined') {
        opt = {};
    }
    this.o = $(o);
    this.opt = opt;
    this.container = '

';
    this.indibutt = '

';

    $(o).wrap(this.container).after(this.indibutt);
    $(o).parent().css({
        'height': $(o).outerHeight(),
        'width': $(o).outerWidth()
    });
    this.indi = $(o).next();
    $(this.indi).css({
        'height': $(o).outerHeight()
    });
    this.clear = $(o).next().next();
    $(this.clear).css({
        'height': $(o).outerHeight(),
        'width': $(o).outerHeight()
    }).click(function(){
        $(o).attr('value', '');
        if ($.isFunction(opt.clearHook)) {
            opt.clearHook();
        }
    });
    if ($.isFunction(opt.onChange)) {
        $(o).on('input', function() {
            opt.onChange();
        });
    }

    this.start = function() {
        $(this.indi).show();
        $(this.clear).hide();
    }
    this.stop = function() {
        $(this.indi).hide();
        $(this.clear).show();
    }
    return this;
}

// document ready
$(function() {
    JCS.init();

    //JCS.qSubmit();
});

<|endoftext|>


<commit_before>var TransferComponentFormView = Backbone.View.extend({
  initialize: function(options) {
    this.form_layout_template = _.template(options.form_layout_template);
    this.modal_template = options.modal_template;
    this.sourceDirectories = options.sourceDirectories;
  },

  showSelector: function(sourceDir) {

   // display action selector in modal window
    $(this.modal_template).modal({show: true});

    // make it destroy rather than hide modal
    $('#transfer-component-select-close, #transfer-component-select-cancel')
      .click(function() {
        $('#transfer-component-select-modal').remove();
      });

    // add directory selector
    createDirectoryPicker(
      sourceDir,
      'transfer-component-select-modal',
      'path_container'
    );
  },

  addedPaths: function() {
    var paths = [];
    $('.transfer_path').each(function() {
      paths.push($(this).text());
    });
    return paths;
  },

  startTransfer: function(transfer) {
    var path
      , copied = 0;

    $('.transfer-component-activity-indicator').show();
    // get path to temp directory in which to copy individual transfer
    // components
    $.ajax({
      url: '/filesystem/get_temp_directory/',
      type: 'GET',
      cache: false,
      success: function(results) {

        var tempDir = results.tempDir;

        // copy each transfer component to the temp directory
        for (var index in transfer.sourcePaths) {
          path = transfer.sourcePaths[index];

          $.ajax({
            url: '/filesystem/copy_transfer_component/',
            type: 'POST',
            async: false,
            cache: false,
            data: {
              name:        transfer.name,
              path:        path,
              destination: tempDir
            },
            success: function(results) {
              copied++;
            }
          });
        }
        // move from temp directory to appropriate watchdir
        var url = '/filesystem/ransfer/'
          , isZipFile = path.toLowerCase().indexOf('.zip') != -1
          , filepath;

        // if transfer is a ZIP file, then extract basename add to temporary directory
        if (isZipFile) {
          filepath = tempDir + '/' + path.replace(/\\/g,'/').replace( /.*\//, '' );
        } else {
          filepath = tempDir + '/' + transfer.name;
        }

        $.ajax({
          url: url,
          type: 'POST',
          async: false,
          cache: false,
          data: {
            filepath: filepath,
            type:     transfer.type
          },
          success: function(results) {
            $('#transfer-name').val('');
            $('#path_container').html('');
            $('.transfer-component-activity-indicator').hide();
          }
        });
        // report progress
      }
    });
  },

  render: function() {
    var $pathAreaEl = $('

')
       , $pathContainerEl = $('

');

    this.pathContainerEl = $pathContainerEl;

    // add button to add paths via a pop-up selector
    var $buttonContainer = $('

')
      , $addButton = $('Browse')
      , $sourceDirSelect = $('<select id="path_source_select"></select>')
      , $startTransferButton = $('Start transfer')
      , self = this;

    $buttonContainer
      .append($sourceDirSelect)
      .append($addButton)
      .append($startTransferButton);

    $pathAreaEl.append($buttonContainer);

    // add path container to parent container
    $pathAreaEl.append($pathContainerEl);

    // populate select with source directory values
    $.each(this.sourceDirectories, function(id, path) {
      $sourceDirSelect
        .append($("<option></option>")
        .attr("value", id)
        .text(path));
    });

    // populate view's DOM element with template output
    var context = {
      transfer_paths: $pathAreaEl.html()
    };
    $(this.el).html(this.form_layout_template(context));

    // make add button clickable
    $('#path_add_button').click(function() {
      // add modal containing directory selector
      // selecting makes modal disappear, adds directory, and re-renders
      self.showSelector($('#path_source_select').children(':selected').text());
    });

    // add logic to determine whether or not transfer name needs to be
    // visible if transfer type changed
    $('#transfer-type').change(function() {
      if ($(this).val() == 'zipped bag') {
        $('#transfer-name-container').hide('slide', {direction: 'left'}, 250);
      } else {
        $('#transfer-name-container').show('slide', {direction: 'left'}, 250);
      }
    });

    // make start transfer button clickable
    $('#start_transfer_button').click(function() {
      var transferName = $('#transfer-name').val();

      if (!transferName)
      {
        alert('Please enter a transfer name');
      } else {
        if (!self.addedPaths().length) {
          alert('Please click "Browse" to add one or more paths from the source directory.');
        } else {
          var transferData = {
            'name':            transferName,
            'type':            $('#transfer-type').val(),
            'accessionNumber': $('#transfer-accession-number').val(),
            'sourcePaths':     self.addedPaths()
          };
          self.startTransfer(transferData);
        }
      }
    });
  }
});
<commit_msg>Added workaround for issue with starting zipped bag transfers.<commit_after>var TransferComponentFormView = Backbone.View.extend({
  initialize: function(options) {
    this.form_layout_template = _.template(options.form_layout_template);
    this.modal_template = options.modal_template;
    this.sourceDirectories = options.sourceDirectories;
  },

  showSelector: function(sourceDir) {

   // display action selector in modal window
    $(this.modal_template).modal({show: true});

    // make it destroy rather than hide modal
    $('#transfer-component-select-close, #transfer-component-select-cancel')
      .click(function() {
        $('#transfer-component-select-modal').remove();
      });

    // add directory selector
    createDirectoryPicker(
      sourceDir,
      'transfer-component-select-modal',
      'path_container'
    );
  },

  addedPaths: function() {
    var paths = [];
    $('.transfer_path').each(function() {
      paths.push($(this).text());
    });
    return paths;
  },

  startTransfer: function(transfer) {
    var path
      , copied = 0;

    $('.transfer-component-activity-indicator').show();
    // get path to temp directory in which to copy individual transfer
    // components
    $.ajax({
      url: '/filesystem/get_temp_directory/',
      type: 'GET',
      cache: false,
      success: function(results) {

        var tempDir = results.tempDir;

        // copy each transfer component to the temp directory
        for (var index in transfer.sourcePaths) {
          path = transfer.sourcePaths[index];

          $.ajax({
            url: '/filesystem/copy_transfer_component/',
            type: 'POST',
            async: false,
            cache: false,
            data: {
              name:        transfer.name,
              path:        path,
              destination: tempDir
            },
            success: function(results) {
              copied++;
            }
          });
        }
        // move from temp directory to appropriate watchdir
        var url = '/filesystem/ransfer/'
          , isZipFile = path.toLowerCase().indexOf('.zip') != -1
          , filepath;

        // if transfer is a ZIP file, then extract basename add to temporary directory
        if (isZipFile) {
          filepath = tempDir + '/' + path.replace(/\\/g,'/').replace( /.*\//, '' );
        } else {
          filepath = tempDir + '/' + transfer.name;
        }

        $.ajax({
          url: url,
          type: 'POST',
          async: false,
          cache: false,
          data: {
            filepath: filepath,
            type:     transfer.type
          },
          success: function(results) {
            $('#transfer-name').val('');
            $('#transfer-name-container').show();
            $('#transfer-type').val('standard');
            $('#path_container').html('');
            $('.transfer-component-activity-indicator').hide();
          }
        });
        // report progress
      }
    });
  },

  render: function() {
    var $pathAreaEl = $('

')
       , $pathContainerEl = $('

');

    this.pathContainerEl = $pathContainerEl;

    // add button to add paths via a pop-up selector
    var $buttonContainer = $('

')
      , $addButton = $('Browse')
      , $sourceDirSelect = $('<select id="path_source_select"></select>')
      , $startTransferButton = $('Start transfer')
      , self = this;

    $buttonContainer
      .append($sourceDirSelect)
      .append($addButton)
      .append($startTransferButton);

    $pathAreaEl.append($buttonContainer);

    // add path container to parent container
    $pathAreaEl.append($pathContainerEl);

    // populate select with source directory values
    $.each(this.sourceDirectories, function(id, path) {
      $sourceDirSelect
        .append($("<option></option>")
        .attr("value", id)
        .text(path));
    });

    // populate view's DOM element with template output
    var context = {
      transfer_paths: $pathAreaEl.html()
    };
    $(this.el).html(this.form_layout_template(context));

    // make add button clickable
    $('#path_add_button').click(function() {
      // add modal containing directory selector
      // selecting makes modal disappear, adds directory, and re-renders
      self.showSelector($('#path_source_select').children(':selected').text());
    });

    // add logic to determine whether or not transfer name needs to be
    // visible if transfer type changed
    $('#transfer-type').change(function() {
      if ($(this).val() == 'zipped bag') {
        $('#transfer-name-container').hide('slide', {direction: 'left'}, 250);
      } else {
        $('#transfer-name-container').show('slide', {direction: 'left'}, 250);
      }
    });

    // make start transfer button clickable
    $('#start_transfer_button').click(function() {
      var transferName = $('#transfer-name').val();

      // if transfering a zipped bag, give it a dummy name
      if ($('#transfer-type').val() == 'zipped bag') {
        transferName = 'ZippedBag';
      }

      if (!transferName)
      {
        alert('Please enter a transfer name');
      } else {
        if (!self.addedPaths().length) {
          alert('Please click "Browse" to add one or more paths from the source directory.');
        } else {
          var transferData = {
            'name':            transferName,
            'type':            $('#transfer-type').val(),
            'accessionNumber': $('#transfer-accession-number').val(),
            'sourcePaths':     self.addedPaths()
          };
          self.startTransfer(transferData);
        }
      }
    });
  }
});
<|endoftext|>


<commit_before>Ext.define('CustomApp', {
    extend: 'Rally.app.App',
    componentCls: 'app',
    items:{ html:'App SDK 2.0rc2 Docs'},
    launch: function(){
	console.log("Hello World");
	this._loadData();
    },
    //get data from Rally
    _loadData: function(){
	var myStore = Ext.create('Rally.data.wsapi.Store', {
	model: 'User Story',
	autoLoad: true,
	//events that will fire
	listeners: {
	    load: function(myStore, myData, success) {
	    //add grid logic after store loaded...
	    this. _loadGrid(myStore);

	    },
	    scope: this
	},
	fetch: ['FormattedID', 'Name', 'ScheduleState']
       });
    },
    //create and show a grid of given stories
    _loadGrid: function(myStore){
	var myGrid= Ext.create('Rally.ui.grid.Grid', {
	    store: myStore,
	    columnCfgs: [
		 'FormattedID', 'Name', 'ScheduleState'
	     ]
	});
	this.add(myGrid);
	console.log("What is this?", this);

    }
});

<commit_msg>first grid working<commit_after>Ext.define('CustomApp', {
    extend: 'Rally.app.App',
    componentCls: 'app',
    launch: function(){
	console.log("Hello World");
	this._loadData();
    },
    //get data from Rally
    _loadData: function(){
	var myStore = Ext.create('Rally.data.wsapi.Store', {
	model: 'User Story',
	autoLoad: true,
	//events that will fire
	listeners: {
	    load: function(myStore, myData, success) {
	    //add grid logic after store loaded...
	    this. _loadGrid(myStore);

	    },
	    scope: this
	},
	fetch: ['FormattedID', 'Name', 'ScheduleState']
       });
    },
    //create and show a grid of given stories
    _loadGrid: function(myStore){
	var myGrid= Ext.create('Rally.ui.grid.Grid', {
	    store: myStore,
	    columnCfgs: [
		 'FormattedID', 'Name', 'ScheduleState'
	     ]
	});
	this.add(myGrid);
	console.log("What is this?", this);

    }
});

<|endoftext|>