<commit_before>/*---------------------------------------------------------
 * OpenERP base_graph
 *---------------------------------------------------------*/

openerp.base_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'];

QWeb.add_template('/base_graph/static/src/xml/base_graph.xml');
openerp.base.views.add('graph', 'openerp.base_graph.GraphView');
openerp.base_graph.GraphView = openerp.base.Controller.extend({

    init: function(view_manager, session, element_id, dataset, view_id) {
        this._super(session, element_id);
        this.view_manager = view_manager;
        this.dataset = dataset;
        this.model = this.dataset.model;
        this.view_id = view_id;
    },
    do_show: function () {
        // TODO: re-trigger search
        this.$element.show();
    },
    do_hide: function () {
        this.$element.hide();
    },
    start: function() {
        this.rpc("/base_graph/graphview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
    },
    on_loaded: function(data) {
        this.all_fields = data.all_fields;
        this.fields_view = data.fields_view;
        this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
        this.view_id = this.fields_view.view_id;
        this.chart = this.fields_view.arch.attrs.type || 'pie';
        this.fields = this.fields_view.fields;
        this.chart_info_fields = [];
        this.operator_field = '';
        this.operator_field_one = '';
        this.operator = [];
        this.group_field = '';
        this.orientation = this.fields_view.arch.attrs.orientation || '';

        _.each(this.fields_view.arch.children, function (field) {
            if (field.attrs.operator) {
                this.operator.push(field.attrs.name);
            }
            else if (field.attrs.group) {
                this.group_field = field.attrs.name;
            }
            else {
                this.chart_info_fields.push(field.attrs.name);
            }
        }, this);

        this.operator_field = this.operator[0];
        if(this.operator.length > 1){
            this.operator_field_one = this.operator[1];
        }
        if(this.operator == ''){
            this.operator_field = this.chart_info_fields[1];
        }
        this.chart_info = this.chart_info_fields[0];
        this.x_title = this.fields[this.chart_info_fields[0]]['string'];
        this.y_title = this.fields[this.operator_field]['string'];
        this.load_chart();
    },

    load_chart: function(data) {
        var self = this;
        if(data){
            this.x_title = this.all_fields[this.chart_info_fields]['string'];
            this.y_title = this.all_fields[this.operator_field]['string'];
            self.schedule_chart(data);
        }else{
            this.dataset.read_ids(
                this.dataset.ids,
                {},
                function(res) {
                    self.schedule_chart(res);
                }
            );
        }
    },

    schedule_chart: function(results) {
        this.$element.html(QWeb.render("GraphView", {"fields_view": this.fields_view, "chart": this.chart,'view_id': this.view_id}));
        if (results.length){
            _.each(results, function (result) {
                _.each(result, function (field_value, field_name) {
                    if (typeof field_value == 'object') {
                        result[field_name] = field_value[field_value.length - 1];
                    }
                    if (typeof field_value == 'string'){
                        var choices = this.all_fields[field_name]['selection'];
                        _.each(choices, function (choice) {
                            if(field_value == choice[0]){
                               result[field_name] = choice;
                            }
                        });
                    }
                }, this);
            }, this);

            var opration_fld = {};
            _.each(results, function (result) {
                var gen_key = result[this.chart_info_fields]+"_"+result[this.group_field];
                var map_val = {};
                if (opration_fld[gen_key] == undefined){
                    map_val[this.operator_field] = result[this.operator_field];
                    if (this.operator.length > 1){
                        map_val[this.operator_field_one] = result[this.operator_field_one];
                    }
                    map_val[this.chart_info_fields] = result[this.chart_info_fields];
                    if (this.group_field){
                        map_val[this.group_field] = (typeof result[this.group_field] == 'object')?result[this.group_field][1]:result[this.group_field];
                    }
                } else {
                    map_val = opration_fld[gen_key];
                    map_val[this.operator_field] += result[this.operator_field];
                    if (this.operator.length > 1){
                        map_val[this.operator_field_one] += result[this.operator_field_one];
                    }
                }
                opration_fld[gen_key] = map_val;
            }, this);

            var graph_data = [];
            _.each(opration_fld, function (column_data) {
                graph_data.push(column_data);
            });

            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 view_chart = '';
        var xystr = {};
        var group_list = [];
        var newkey = '', newkey_one;

        if(self.group_field && (this.operator.length <= 1)){
            view_chart = self.orientation == 'horizontal'? 'stackedBarH' : 'stackedBar';
        }else{
            view_chart = self.orientation == 'horizontal'? 'barH' : 'bar';
        }

        _.each(results, function (result) {
            if (self.group_field && (this.operator.length <= 1)){
                newkey = result[self.group_field].split(' ').join('_');
            } else {
                newkey = "val";
            }

            if (jQuery.inArray(newkey, group_list) == -1){
                group_list.push(newkey);
                if(this.operator.length > 1){
                    newkey_one = "val1";
                    group_list.push(newkey_one);
                }
            }
        }, this);

        _.each(results, function (result) {
            var xystring = result[self.chart_info_fields],
                xyname = {};
            if (self.group_field && (self.operator.length <= 1)){
                newkey = result[self.group_field].split(' ').join('_');
            }else{
                newkey = "val";
            }
            if (xystr[xystring] == undefined){
                xyname[self.chart_info_fields] = xystring;
                _.each(group_list, function (group) {
                    xyname[group] = 0.0001;
                });
            } else {
                xyname = xystr[xystring];
            }
            xyname[newkey] = result[self.operator_field];
            if (self.operator.length > 1){
                xyname[newkey_one] = result[self.operator_field_one];
            }
            xystr[xystring] = xyname;
        });

        //for legend color
        var grp_color = _.map(group_list, function (group_legend, index) {
            var legend = {color: COLOR_PALETTE[index]};

            if (group_legend == "val"){
                legend['text'] = self.fields[self.operator_field]['string']
            }else if(group_legend == "val1"){
                legend['text'] = self.fields[self.operator_field_one]['string']
            }else{
                legend['text'] = group_legend;
            }
            return legend;
        });

        //for axis's value and title
        var temp_ax = {
            template: self.chart_info_fields,
            title: ""+self.x_title+""
        };
        var oth_ax = {
            lines: true,
            title: ""+self.y_title+""
        };

        var x_ax, y_ax;
        if (self.orientation == 'horizontal'){
             x_ax = oth_ax;
             y_ax = temp_ax;
        }else{
             x_ax = temp_ax;
             y_ax = oth_ax;
        }

        var bar_chart = new dhtmlxchartChart({
            view: view_chart,
            container: self.view_id+"-barchart",
            value:"#"+group_list[0]+"#",
            gradient: "3d",
            border: false,
            width: 1024,
            radius: 0,
            color:grp_color[0]['color'],
            origin:0,
            xAxis:{
                template:function(obj){
                    if(x_ax['template']){
                        var val = obj[x_ax['template']];
                        val = (typeof val == 'object')?val[1]:(!val?'Undefined':val);
                        return val;
                    }else{
                        return obj;
                    }
                },
                title:x_ax['title'],
                lines:x_ax['lines']
            },
            yAxis:{
                template:function(obj){
                    if(y_ax['template']){
                        var vals = obj[y_ax['template']];
                        vals = (typeof vals == 'object')?vals[1]:(!vals?'Undefined':vals);
                        return vals;
                    }else{
                        return obj;
                    }
                },
                title:y_ax['title'],
                lines: y_ax['lines']
            },
            padding: {
                left: 75
            },
            legend: {
                values: grp_color,
                align:"left",
                valign:"top",
                layout: "x",
                marker:{
                    type:"round",
                    width:12
                }
            }
        });
        for (var m = 1; m<group_list.length;m++){
            bar_chart.addSeries({
                value: "#"+group_list[m]+"#",
                color: grp_color[m]['color']
            });
        }
        bar_chart.parse(_.values(xystr), "json");
        bar_chart.attachEvent("onItemClick", function(id) {
            self.open_list_view(bar_chart.get(id));
        });
    },
    schedule_pie: function(result) {
        var self = this;
        var chart =  new dhtmlxchartChart({
            view:"pie3D",
            container:self.view_id+"-piechart",
            value:"#"+self.operator_field+"#",
            pieInnerText:function(obj) {
                var sum = chart.sum("#"+self.operator_field+"#");
                var val = obj[self.operator_field] / sum * 100 ;
                return Math.round(val * 10)/10 + "%";
            },
            gradient:"3d",
            height: 20,
            radius: 200,
            legend: {
                width: 300,
                align:"left",
                valign:"top",
                layout: "x",
                marker:{
                    type:"round",
                    width:12
                },
                template:function(obj){
                    var val = obj[self.chart_info_fields];
                    val = (typeof val == 'object')?val[1]:val;
                    return val;
                }
            }
        });
        chart.parse(result,"json");
        chart.attachEvent("onItemClick", function(id) {
            self.open_list_view(chart.get(id));
        });
    },
    open_list_view : function (id){
        var self = this;
        id = id[self.chart_info_fields];
        if (typeof id == 'object'){
            id = id[0];
        }
        if(this.view_manager.action.context){
           this.view_manager.action.context = {};
        }
        if(!this.view_manager.action.domain) {
            this.view_manager.action.domain = [[self.chart_info_fields, '=', id],['id','in',self.dataset.ids]];
        } else {
            this.view_manager.action.domain.push([self.chart_info_fields, '=', id],['id','in',self.dataset.ids]);
        }
        var action_manager = new openerp.base.ActionManager(this.view_manager.session, this.view_manager.element_id);
        action_manager.start();
        action_manager.do_action(this.view_manager.action);
    },
    do_search: function(domains, contexts, groupbys) {
        var self = this;
        this.rpc('/base/session/eval_domain_and_context', {
            domains: domains,
            contexts: contexts,
            group_by_seq: groupbys
        }, function (results) {
            // TODO: handle non-empty results.group_by with read_group
            if(results.group_by  && results.group_by != ''){
                self.chart_info_fields = results.group_by[0];
            }else{
                self.chart_info_fields = self.chart_info;
            }
            self.dataset.context = self.context = results.context;
            self.dataset.domain = self.domain = results.domain;
            self.dataset.read_slice({}, 0, self.limit,function(response){
                self.load_chart(response);
            });
        });
    }

});

// here you may tweak globals object, if any, and play with on_* or do_* callbacks on them

};

// vim:et fdc=0 fdl=0:
<commit_msg>[IMP] invert tests and bail out early in order to reduce excessive nesting of functions<commit_after>/*---------------------------------------------------------
 * OpenERP base_graph
 *---------------------------------------------------------*/

openerp.base_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'];

QWeb.add_template('/base_graph/static/src/xml/base_graph.xml');
openerp.base.views.add('graph', 'openerp.base_graph.GraphView');
openerp.base_graph.GraphView = openerp.base.Controller.extend({

    init: function(view_manager, session, element_id, dataset, view_id) {
        this._super(session, element_id);
        this.view_manager = view_manager;
        this.dataset = dataset;
        this.model = this.dataset.model;
        this.view_id = view_id;
    },
    do_show: function () {
        // TODO: re-trigger search
        this.$element.show();
    },
    do_hide: function () {
        this.$element.hide();
    },
    start: function() {
        this.rpc("/base_graph/graphview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
    },
    on_loaded: function(data) {
        this.all_fields = data.all_fields;
        this.fields_view = data.fields_view;
        this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
        this.view_id = this.fields_view.view_id;
        this.chart = this.fields_view.arch.attrs.type || 'pie';
        this.fields = this.fields_view.fields;
        this.chart_info_fields = [];
        this.operator_field = '';
        this.operator_field_one = '';
        this.operator = [];
        this.group_field = '';
        this.orientation = this.fields_view.arch.attrs.orientation || '';

        _.each(this.fields_view.arch.children, function (field) {
            if (field.attrs.operator) {
                this.operator.push(field.attrs.name);
            }
            else if (field.attrs.group) {
                this.group_field = field.attrs.name;
            }
            else {
                this.chart_info_fields.push(field.attrs.name);
            }
        }, this);

        this.operator_field = this.operator[0];
        if(this.operator.length > 1){
            this.operator_field_one = this.operator[1];
        }
        if(this.operator == ''){
            this.operator_field = this.chart_info_fields[1];
        }
        this.chart_info = this.chart_info_fields[0];
        this.x_title = this.fields[this.chart_info_fields[0]]['string'];
        this.y_title = this.fields[this.operator_field]['string'];
        this.load_chart();
    },

    load_chart: function(data) {
        var self = this;
        if(data){
            this.x_title = this.all_fields[this.chart_info_fields]['string'];
            this.y_title = this.all_fields[this.operator_field]['string'];
            self.schedule_chart(data);
        }else{
            this.dataset.read_ids(
                this.dataset.ids,
                {},
                function(res) {
                    self.schedule_chart(res);
                }
            );
        }
    },

    schedule_chart: function(results) {
        this.$element.html(QWeb.render("GraphView", {"fields_view": this.fields_view, "chart": this.chart,'view_id': this.view_id}));
        if (!results.length) {
            return;
        }
        _.each(results, function (result) {
            _.each(result, function (field_value, field_name) {
                if (typeof field_value == 'object') {
                    result[field_name] = field_value[field_value.length - 1];
                }
                if (typeof field_value == 'string') {
                    var choices = this.all_fields[field_name]['selection'];
                    _.each(choices, function (choice) {
                        if (field_value == choice[0]) {
                            result[field_name] = choice;
                        }
                    });
                }
            }, this);
        }, this);

        var opration_fld = {};
        _.each(results, function (result) {
            var gen_key = result[this.chart_info_fields] + "_" + result[this.group_field];
            var map_val = {};
            if (opration_fld[gen_key] == undefined) {
                map_val[this.operator_field] = result[this.operator_field];
                if (this.operator.length > 1) {
                    map_val[this.operator_field_one] = result[this.operator_field_one];
                }
                map_val[this.chart_info_fields] = result[this.chart_info_fields];
                if (this.group_field) {
                    map_val[this.group_field] = (typeof result[this.group_field] == 'object') ? result[this.group_field][1] : result[this.group_field];
                }
            } else {
                map_val = opration_fld[gen_key];
                map_val[this.operator_field] += result[this.operator_field];
                if (this.operator.length > 1) {
                    map_val[this.operator_field_one] += result[this.operator_field_one];
                }
            }
            opration_fld[gen_key] = map_val;
        }, this);

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

    schedule_bar: function(results) {
        var self = this;
        var view_chart = '';
        var xystr = {};
        var group_list = [];
        var newkey = '', newkey_one;

        if(self.group_field && (this.operator.length <= 1)){
            view_chart = self.orientation == 'horizontal'? 'stackedBarH' : 'stackedBar';
        }else{
            view_chart = self.orientation == 'horizontal'? 'barH' : 'bar';
        }

        _.each(results, function (result) {
            if (self.group_field && (this.operator.length <= 1)) {
                newkey = result[self.group_field].split(' ').join('_');
            } else {
                newkey = "val";
            }

            if (_.contains(group_list, newkey)) {
                return;
            }
            group_list.push(newkey);
            if (this.operator.length > 1) {
                newkey_one = "val1";
                group_list.push(newkey_one);
            }
        }, this);

        _.each(results, function (result) {
            var xystring = result[self.chart_info_fields],
                xyname = {};
            if (self.group_field && (self.operator.length <= 1)){
                newkey = result[self.group_field].split(' ').join('_');
            }else{
                newkey = "val";
            }
            if (xystr[xystring] == undefined){
                xyname[self.chart_info_fields] = xystring;
                _.each(group_list, function (group) {
                    xyname[group] = 0.0001;
                });
            } else {
                xyname = xystr[xystring];
            }
            xyname[newkey] = result[self.operator_field];
            if (self.operator.length > 1){
                xyname[newkey_one] = result[self.operator_field_one];
            }
            xystr[xystring] = xyname;
        });

        //for legend color
        var grp_color = _.map(group_list, function (group_legend, index) {
            var legend = {color: COLOR_PALETTE[index]};

            if (group_legend == "val"){
                legend['text'] = self.fields[self.operator_field]['string']
            }else if(group_legend == "val1"){
                legend['text'] = self.fields[self.operator_field_one]['string']
            }else{
                legend['text'] = group_legend;
            }
            return legend;
        });

        //for axis's value and title
        var temp_ax = {
            template: self.chart_info_fields,
            title: ""+self.x_title+""
        };
        var oth_ax = {
            lines: true,
            title: ""+self.y_title+""
        };

        var x_ax, y_ax;
        if (self.orientation == 'horizontal'){
             x_ax = oth_ax;
             y_ax = temp_ax;
        }else{
             x_ax = temp_ax;
             y_ax = oth_ax;
        }

        var bar_chart = new dhtmlxchartChart({
            view: view_chart,
            container: self.view_id+"-barchart",
            value:"#"+group_list[0]+"#",
            gradient: "3d",
            border: false,
            width: 1024,
            radius: 0,
            color:grp_color[0]['color'],
            origin:0,
            xAxis:{
                template:function(obj){
                    if(x_ax['template']){
                        var val = obj[x_ax['template']];
                        val = (typeof val == 'object')?val[1]:(!val?'Undefined':val);
                        return val;
                    }else{
                        return obj;
                    }
                },
                title:x_ax['title'],
                lines:x_ax['lines']
            },
            yAxis:{
                template:function(obj){
                    if(y_ax['template']){
                        var vals = obj[y_ax['template']];
                        vals = (typeof vals == 'object')?vals[1]:(!vals?'Undefined':vals);
                        return vals;
                    }else{
                        return obj;
                    }
                },
                title:y_ax['title'],
                lines: y_ax['lines']
            },
            padding: {
                left: 75
            },
            legend: {
                values: grp_color,
                align:"left",
                valign:"top",
                layout: "x",
                marker:{
                    type:"round",
                    width:12
                }
            }
        });
        for (var m = 1; m<group_list.length;m++){
            bar_chart.addSeries({
                value: "#"+group_list[m]+"#",
                color: grp_color[m]['color']
            });
        }
        bar_chart.parse(_.values(xystr), "json");
        bar_chart.attachEvent("onItemClick", function(id) {
            self.open_list_view(bar_chart.get(id));
        });
    },
    schedule_pie: function(result) {
        var self = this;
        var chart =  new dhtmlxchartChart({
            view:"pie3D",
            container:self.view_id+"-piechart",
            value:"#"+self.operator_field+"#",
            pieInnerText:function(obj) {
                var sum = chart.sum("#"+self.operator_field+"#");
                var val = obj[self.operator_field] / sum * 100 ;
                return Math.round(val * 10)/10 + "%";
            },
            gradient:"3d",
            height: 20,
            radius: 200,
            legend: {
                width: 300,
                align:"left",
                valign:"top",
                layout: "x",
                marker:{
                    type:"round",
                    width:12
                },
                template:function(obj){
                    var val = obj[self.chart_info_fields];
                    val = (typeof val == 'object')?val[1]:val;
                    return val;
                }
            }
        });
        chart.parse(result,"json");
        chart.attachEvent("onItemClick", function(id) {
            self.open_list_view(chart.get(id));
        });
    },
    open_list_view : function (id){
        var self = this;
        id = id[self.chart_info_fields];
        if (typeof id == 'object'){
            id = id[0];
        }
        if(this.view_manager.action.context){
           this.view_manager.action.context = {};
        }
        if(!this.view_manager.action.domain) {
            this.view_manager.action.domain = [[self.chart_info_fields, '=', id],['id','in',self.dataset.ids]];
        } else {
            this.view_manager.action.domain.push([self.chart_info_fields, '=', id],['id','in',self.dataset.ids]);
        }
        var action_manager = new openerp.base.ActionManager(this.view_manager.session, this.view_manager.element_id);
        action_manager.start();
        action_manager.do_action(this.view_manager.action);
    },
    do_search: function(domains, contexts, groupbys) {
        var self = this;
        this.rpc('/base/session/eval_domain_and_context', {
            domains: domains,
            contexts: contexts,
            group_by_seq: groupbys
        }, function (results) {
            // TODO: handle non-empty results.group_by with read_group
            if(results.group_by  && results.group_by != ''){
                self.chart_info_fields = results.group_by[0];
            }else{
                self.chart_info_fields = self.chart_info;
            }
            self.dataset.context = self.context = results.context;
            self.dataset.domain = self.domain = results.domain;
            self.dataset.read_slice({}, 0, self.limit,function(response){
                self.load_chart(response);
            });
        });
    }

});

// here you may tweak globals object, if any, and play with on_* or do_* callbacks on them

};

// vim:et fdc=0 fdl=0:
<|endoftext|>


<commit_before>/*
Copyright (c) 2011, OpenERP S.A.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

nova = (function() {
    var lib = {};
    lib.internal = {};

    /*
     * (Almost) unmodified John Resig's inheritance
     */
    /*
     * Simple JavaScript Inheritance By John Resig http://ejohn.org/ MIT
     * Licensed.
     */
    // Inspired by base2 and Prototype
    (function() {
        var initializing = false, fnTest = /xyz/.test(function() {
            xyz;
        }) ? /\b_super\b/ : /.*/;
        // The base Class implementation (does nothing)
        this.Class = function() {
        };

        // Create a new Class that inherits from this class
        this.Class.extend = function(prop) {
        var _super = this.prototype;

        // Instantiate a web class (but only create the instance,
        // don't run the init constructor)
        initializing = true;
        var prototype = new this();
        initializing = false;

        // Copy the properties over onto the new prototype
        for (var name in prop) {
            // Check if we're overwriting an existing function
            prototype[name] = typeof prop[name] == "function" &&
                              typeof _super[name] == "function" &&
                              fnTest.test(prop[name]) ?
                    (function(name, fn) {
                        return function() {
                            var tmp = this._super;

                            // Add a new ._super() method that is the same
                            // method but on the super-class
                            this._super = _super[name];

                            // The method only need to be bound temporarily, so
                            // we remove it when we're done executing
                            var ret = fn.apply(this, arguments);
                            this._super = tmp;

                            return ret;
                        };
                    })(name, prop[name]) :
                    prop[name];
        }

        // The dummy class constructor
        function Class() {
            // All construction is actually done in the init method
            if (!initializing && this.init) {
                var ret = this.init.apply(this, arguments);
                if (ret) { return ret; }
            }
            return this;
        }
        Class.include = function (properties) {
            for (var name in properties) {
                if (typeof properties[name] !== 'function'
                        || !fnTest.test(properties[name])) {
                    prototype[name] = properties[name];
                } else if (typeof prototype[name] === 'function'
                           && prototype.hasOwnProperty(name)) {
                    prototype[name] = (function (name, fn, previous) {
                        return function () {
                            var tmp = this._super;
                            this._super = previous;
                            var ret = fn.apply(this, arguments);
                            this._super = tmp;
                            return ret;
                        }
                    })(name, properties[name], prototype[name]);
                } else if (typeof _super[name] === 'function') {
                    prototype[name] = (function (name, fn) {
                        return function () {
                            var tmp = this._super;
                            this._super = _super[name];
                            var ret = fn.apply(this, arguments);
                            this._super = tmp;
                            return ret;
                        }
                    })(name, properties[name]);
                }
            }
        };

        // Populate our constructed prototype object
        Class.prototype = prototype;

        // Enforce the constructor to be what we expect
        Class.constructor = Class;

        // And make this class extendable
        Class.extend = arguments.callee;

        return Class;
        };
    }).call(lib);
    // end of John Resig's code

    lib.DestroyableMixin = {
        init: function() {
            this.__destroyableDestroyed = false;
        },
        isDestroyed : function() {
            return this.__destroyableDestroyed;
        },
        destroy : function() {
            this.__destroyableDestroyed = true;
        }
    };

    lib.ParentedMixin = _.extend({}, lib.DestroyableMixin, {
        __parentedMixin : true,
        init: function() {
            lib.DestroyableMixin.init.call(this);
            this.__parentedChildren = [];
            this.__parentedParent = null;
        },
        setParent : function(parent) {
            if (this.getParent()) {
                if (this.getParent().__parentedMixin) {
                    this.getParent().__parentedChildren = _.without(this
                            .getParent().getChildren(), this);
                }
            }
            this.__parentedParent = parent;
            if (parent && parent.__parentedMixin) {
                parent.__parentedChildren.push(this);
            }
        },
        getParent : function() {
            return this.__parentedParent;
        },
        getChildren : function() {
            return _.clone(this.__parentedChildren);
        },
        destroy : function() {
            _.each(this.getChildren(), function(el) {
                el.destroy();
            });
            this.setParent(undefined);
            lib.DestroyableMixin.destroy.call(this);
        }
    });

    /*
     * Yes, we steal Backbone's events :)
     *
     * This class just handle the dispatching of events, it is not meant to be extended,
     * nor used directly. All integration with parenting and automatic unregistration of
     * events is done in EventDispatcherMixin.
     */
    // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
    // Backbone may be freely distributed under the MIT license.
    // For all details and documentation:
    // http://backbonejs.org
    lib.internal.Events = lib.Class.extend({

        // Bind an event, specified by a string name, `ev`, to a `callback`
        // function. Passing `"all"` will bind the callback to all events fired.
        on : function(events, callback, context) {
            var ev;
            events = events.split(/\s+/);
            var calls = this._callbacks || (this._callbacks = {});
            while (ev = events.shift()) {
                // Create an immutable callback list, allowing traversal during
                // modification. The tail is an empty object that will always be
                // used
                // as the next node.
                var list = calls[ev] || (calls[ev] = {});
                var tail = list.tail || (list.tail = list.next = {});
                tail.callback = callback;
                tail.context = context;
                list.tail = tail.next = {};
            }
            return this;
        },

        // Remove one or many callbacks. If `context` is null, removes all
        // callbacks
        // with that function. If `callback` is null, removes all callbacks for
        // the
        // event. If `ev` is null, removes all bound callbacks for all events.
        off : function(events, callback, context) {
            var ev, calls, node;
            if (!events) {
                delete this._callbacks;
            } else if (calls = this._callbacks) {
                events = events.split(/\s+/);
                while (ev = events.shift()) {
                    node = calls[ev];
                    delete calls[ev];
                    if (!callback || !node)
                        continue;
                    // Create a new list, omitting the indicated event/context
                    // pairs.
                    while ((node = node.next) && node.next) {
                        if (node.callback === callback
                                && (!context || node.context === context))
                            continue;
                        this.on(ev, node.callback, node.context);
                    }
                }
            }
            return this;
        },

        // Trigger an event, firing all bound callbacks. Callbacks are passed
        // the
        // same arguments as `trigger` is, apart from the event name.
        // Listening for `"all"` passes the true event name as the first
        // argument.
        trigger : function(events) {
            var event, node, calls, tail, args, all, rest;
            if (!(calls = this._callbacks))
                return this;
            all = calls['all'];
            (events = events.split(/\s+/)).push(null);
            // Save references to the current heads & tails.
            while (event = events.shift()) {
                if (all)
                    events.push({
                        next : all.next,
                        tail : all.tail,
                        event : event
                    });
                if (!(node = calls[event]))
                    continue;
                events.push({
                    next : node.next,
                    tail : node.tail
                });
            }
            // Traverse each list, stopping when the saved tail is reached.
            rest = Array.prototype.slice.call(arguments, 1);
            while (node = events.pop()) {
                tail = node.tail;
                args = node.event ? [ node.event ].concat(rest) : rest;
                while ((node = node.next) !== tail) {
                    node.callback.apply(node.context || this, args);
                }
            }
            return this;
        }
    });
    // end of Backbone's events class

    lib.EventDispatcherMixin = _.extend({}, lib.ParentedMixin, {
        __eventDispatcherMixin: true,
        init: function() {
            lib.ParentedMixin.init.call(this);
            this.__edispatcherEvents = new lib.internal.Events();
            this.__edispatcherRegisteredEvents = [];
        },
        bind: function(events, dest, func) {
            var self = this;
            events = events.split(/\s+/);
            _.each(events, function(eventName) {
                self.__edispatcherEvents.on(eventName, func, dest);
                if (dest && dest.__eventDispatcherMixin) {
                    dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self});
                }
            });
            return this;
        },
        unbind: function(events, dest, func) {
            var self = this;
            events = events.split(/\s+/);
            _.each(events, function(eventName) {
                self.__edispatcherEvents.off(eventName, func, dest);
                if (dest && dest.__eventDispatcherMixin) {
                    dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function(el) {
                        return !(el.name === eventName && el.func === func && el.source === self);
                    });
                }
            });
            return this;
        },
        trigger: function(events) {
            this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments);
            return this;
        },
        destroy: function() {
            var self = this;
            _.each(this.__edispatcherRegisteredEvents, function(event) {
                event.source.__edispatcherEvents.off(event.name, event.func, self);
            });
            this.__edispatcherRegisteredEvents = [];
            this.__edispatcherEvents.off();
            lib.ParentedMixin.destroy.call(this);
        }
    });

    lib.GetterSetterMixin = _.extend({}, lib.EventDispatcherMixin, {
        init: function() {
            lib.EventDispatcherMixin.init.call(this);
            this.__getterSetterInternalMap = {};
        },
        set: function(map) {
            var self = this;
            _.each(map, function(val, key) {
                var tmp = self.__getterSetterInternalMap[key];
                self.__getterSetterInternalMap[key] = val;
                self.trigger("changed:" + key, {
                    oldValue: tmp,
                    newValue: val,
                    source: self
                });
            });
        },
        get: function(key) {
            return this.__getterSetterInternalMap[key];
        }
    });

    lib.Widget = lib.Class.extend(_.extend({}, lib.GetterSetterMixin, {
        /**
         * Tag name when creating a default $element.
         * @type string
         */
        tag_name: 'div',
        /**
         * Constructs the widget and sets its parent if a parent is given.
         *
         * @constructs openerp.web.Widget
         * @extends openerp.web.CallbackEnabled
         *
         * @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance.
         * When that widget is destroyed by calling destroy(), the current instance will be
         * destroyed too. Can be null.
         * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
         * to bind the current Widget to an already existing part of the DOM, which is not compatible
         * with the DOM insertion methods provided by the current implementation of Widget. So
         * for new components this argument should not be provided any more.
         */
        init: function(parent) {
            lib.GetterSetterMixin.init.call(this);
            this.$element = $(document.createElement(this.tag_name));

            this.setParent(parent);
        },
        /**
         * Destroys the current widget, also destroys all its children before destroying itself.
         */
        destroy: function() {
            _.each(this.getChildren(), function(el) {
                el.destroy();
            });
            if(this.$element != null) {
                this.$element.remove();
            }
            lib.GetterSetterMixin.destroy.call(this);
        },
        /**
         * Renders the current widget and appends it to the given jQuery object or Widget.
         *
         * @param target A jQuery object or a Widget instance.
         */
        appendTo: function(target) {
            var self = this;
            return this.__renderAndInsert(function(t) {
                self.$element.appendTo(t);
            }, target);
        },
        /**
         * Renders the current widget and prepends it to the given jQuery object or Widget.
         *
         * @param target A jQuery object or a Widget instance.
         */
        prependTo: function(target) {
            var self = this;
            return this.__renderAndInsert(function(t) {
                self.$element.prependTo(t);
            }, target);
        },
        /**
         * Renders the current widget and inserts it after to the given jQuery object or Widget.
         *
         * @param target A jQuery object or a Widget instance.
         */
        insertAfter: function(target) {
            var self = this;
            return this.__renderAndInsert(function(t) {
                self.$element.insertAfter(t);
            }, target);
        },
        /**
         * Renders the current widget and inserts it before to the given jQuery object or Widget.
         *
         * @param target A jQuery object or a Widget instance.
         */
        insertBefore: function(target) {
            var self = this;
            return this.__renderAndInsert(function(t) {
                self.$element.insertBefore(t);
            }, target);
        },
        /**
         * Renders the current widget and replaces the given jQuery object.
         *
         * @param target A jQuery object or a Widget instance.
         */
        replace: function(target) {
            return this.__renderAndInsert(_.bind(function(t) {
                this.$element.replaceAll(t);
            }, this), target);
        },
        __renderAndInsert: function(insertion, target) {
            this.render_element();
            insertion(target);
            return this.start();
        },
        /**
         * This is the method to implement to render the Widget.
         */
        render_element: function() {},
        /**
         * Method called after rendering. Mostly used to bind actions, perform asynchronous
         * calls, etc...
         *
         * By convention, the method should return a promise to inform the caller when
         * this widget has been initialized.
         *
         * @returns {jQuery.Deferred}
         */
        start: function() {}
    }));

    return lib;
})();
<commit_msg>[imp] cosmetic change<commit_after>/*
Copyright (c) 2011, OpenERP S.A.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

nova = (function() {
    var lib = {};
    lib.internal = {};

    /*
     * (Almost) unmodified John Resig's inheritance
     */
    /*
     * Simple JavaScript Inheritance By John Resig http://ejohn.org/ MIT
     * Licensed.
     */
    // Inspired by base2 and Prototype
    (function() {
        var initializing = false, fnTest = /xyz/.test(function() {
            xyz;
        }) ? /\b_super\b/ : /.*/;
        // The base Class implementation (does nothing)
        this.Class = function() {
        };

        // Create a new Class that inherits from this class
        this.Class.extend = function(prop) {
        var _super = this.prototype;

        // Instantiate a web class (but only create the instance,
        // don't run the init constructor)
        initializing = true;
        var prototype = new this();
        initializing = false;

        // Copy the properties over onto the new prototype
        for (var name in prop) {
            // Check if we're overwriting an existing function
            prototype[name] = typeof prop[name] == "function" &&
                              typeof _super[name] == "function" &&
                              fnTest.test(prop[name]) ?
                    (function(name, fn) {
                        return function() {
                            var tmp = this._super;

                            // Add a new ._super() method that is the same
                            // method but on the super-class
                            this._super = _super[name];

                            // The method only need to be bound temporarily, so
                            // we remove it when we're done executing
                            var ret = fn.apply(this, arguments);
                            this._super = tmp;

                            return ret;
                        };
                    })(name, prop[name]) :
                    prop[name];
        }

        // The dummy class constructor
        function Class() {
            // All construction is actually done in the init method
            if (!initializing && this.init) {
                var ret = this.init.apply(this, arguments);
                if (ret) { return ret; }
            }
            return this;
        }
        Class.include = function (properties) {
            for (var name in properties) {
                if (typeof properties[name] !== 'function'
                        || !fnTest.test(properties[name])) {
                    prototype[name] = properties[name];
                } else if (typeof prototype[name] === 'function'
                           && prototype.hasOwnProperty(name)) {
                    prototype[name] = (function (name, fn, previous) {
                        return function () {
                            var tmp = this._super;
                            this._super = previous;
                            var ret = fn.apply(this, arguments);
                            this._super = tmp;
                            return ret;
                        }
                    })(name, properties[name], prototype[name]);
                } else if (typeof _super[name] === 'function') {
                    prototype[name] = (function (name, fn) {
                        return function () {
                            var tmp = this._super;
                            this._super = _super[name];
                            var ret = fn.apply(this, arguments);
                            this._super = tmp;
                            return ret;
                        }
                    })(name, properties[name]);
                }
            }
        };

        // Populate our constructed prototype object
        Class.prototype = prototype;

        // Enforce the constructor to be what we expect
        Class.constructor = Class;

        // And make this class extendable
        Class.extend = arguments.callee;

        return Class;
        };
    }).call(lib);
    // end of John Resig's code

    lib.DestroyableMixin = {
        init: function() {
            this.__destroyableDestroyed = false;
        },
        isDestroyed : function() {
            return this.__destroyableDestroyed;
        },
        destroy : function() {
            this.__destroyableDestroyed = true;
        }
    };

    lib.ParentedMixin = _.extend({}, lib.DestroyableMixin, {
        __parentedMixin : true,
        init: function() {
            lib.DestroyableMixin.init.call(this);
            this.__parentedChildren = [];
            this.__parentedParent = null;
        },
        setParent : function(parent) {
            if (this.getParent()) {
                if (this.getParent().__parentedMixin) {
                    this.getParent().__parentedChildren = _.without(this
                            .getParent().getChildren(), this);
                }
            }
            this.__parentedParent = parent;
            if (parent && parent.__parentedMixin) {
                parent.__parentedChildren.push(this);
            }
        },
        getParent : function() {
            return this.__parentedParent;
        },
        getChildren : function() {
            return _.clone(this.__parentedChildren);
        },
        destroy : function() {
            _.each(this.getChildren(), function(el) {
                el.destroy();
            });
            this.setParent(undefined);
            lib.DestroyableMixin.destroy.call(this);
        }
    });

    /*
     * Yes, we steal Backbone's events :)
     *
     * This class just handle the dispatching of events, it is not meant to be extended,
     * nor used directly. All integration with parenting and automatic unregistration of
     * events is done in EventDispatcherMixin.
     */
    // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
    // Backbone may be freely distributed under the MIT license.
    // For all details and documentation:
    // http://backbonejs.org
    lib.internal.Events = lib.Class.extend({

        // Bind an event, specified by a string name, `ev`, to a `callback`
        // function. Passing `"all"` will bind the callback to all events fired.
        on : function(events, callback, context) {
            var ev;
            events = events.split(/\s+/);
            var calls = this._callbacks || (this._callbacks = {});
            while (ev = events.shift()) {
                // Create an immutable callback list, allowing traversal during
                // modification. The tail is an empty object that will always be
                // used
                // as the next node.
                var list = calls[ev] || (calls[ev] = {});
                var tail = list.tail || (list.tail = list.next = {});
                tail.callback = callback;
                tail.context = context;
                list.tail = tail.next = {};
            }
            return this;
        },

        // Remove one or many callbacks. If `context` is null, removes all
        // callbacks
        // with that function. If `callback` is null, removes all callbacks for
        // the
        // event. If `ev` is null, removes all bound callbacks for all events.
        off : function(events, callback, context) {
            var ev, calls, node;
            if (!events) {
                delete this._callbacks;
            } else if (calls = this._callbacks) {
                events = events.split(/\s+/);
                while (ev = events.shift()) {
                    node = calls[ev];
                    delete calls[ev];
                    if (!callback || !node)
                        continue;
                    // Create a new list, omitting the indicated event/context
                    // pairs.
                    while ((node = node.next) && node.next) {
                        if (node.callback === callback
                                && (!context || node.context === context))
                            continue;
                        this.on(ev, node.callback, node.context);
                    }
                }
            }
            return this;
        },

        // Trigger an event, firing all bound callbacks. Callbacks are passed
        // the
        // same arguments as `trigger` is, apart from the event name.
        // Listening for `"all"` passes the true event name as the first
        // argument.
        trigger : function(events) {
            var event, node, calls, tail, args, all, rest;
            if (!(calls = this._callbacks))
                return this;
            all = calls['all'];
            (events = events.split(/\s+/)).push(null);
            // Save references to the current heads & tails.
            while (event = events.shift()) {
                if (all)
                    events.push({
                        next : all.next,
                        tail : all.tail,
                        event : event
                    });
                if (!(node = calls[event]))
                    continue;
                events.push({
                    next : node.next,
                    tail : node.tail
                });
            }
            // Traverse each list, stopping when the saved tail is reached.
            rest = Array.prototype.slice.call(arguments, 1);
            while (node = events.pop()) {
                tail = node.tail;
                args = node.event ? [ node.event ].concat(rest) : rest;
                while ((node = node.next) !== tail) {
                    node.callback.apply(node.context || this, args);
                }
            }
            return this;
        }
    });
    // end of Backbone's events class

    lib.EventDispatcherMixin = _.extend({}, lib.ParentedMixin, {
        __eventDispatcherMixin: true,
        init: function() {
            lib.ParentedMixin.init.call(this);
            this.__edispatcherEvents = new lib.internal.Events();
            this.__edispatcherRegisteredEvents = [];
        },
        bind: function(events, dest, func) {
            var self = this;
            events = events.split(/\s+/);
            _.each(events, function(eventName) {
                self.__edispatcherEvents.on(eventName, func, dest);
                if (dest && dest.__eventDispatcherMixin) {
                    dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self});
                }
            });
            return this;
        },
        unbind: function(events, dest, func) {
            var self = this;
            events = events.split(/\s+/);
            _.each(events, function(eventName) {
                self.__edispatcherEvents.off(eventName, func, dest);
                if (dest && dest.__eventDispatcherMixin) {
                    dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function(el) {
                        return !(el.name === eventName && el.func === func && el.source === self);
                    });
                }
            });
            return this;
        },
        trigger: function(events) {
            this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments);
            return this;
        },
        destroy: function() {
            var self = this;
            _.each(this.__edispatcherRegisteredEvents, function(event) {
                event.source.__edispatcherEvents.off(event.name, event.func, self);
            });
            this.__edispatcherRegisteredEvents = [];
            this.__edispatcherEvents.off();
            lib.ParentedMixin.destroy.call(this);
        }
    });

    lib.GetterSetterMixin = _.extend({}, lib.EventDispatcherMixin, {
        init: function() {
            lib.EventDispatcherMixin.init.call(this);
            this.__getterSetterInternalMap = {};
        },
        set: function(map) {
            var self = this;
            _.each(map, function(val, key) {
                var tmp = self.__getterSetterInternalMap[key];
                self.__getterSetterInternalMap[key] = val;
                self.trigger("changed:" + key, {
                    oldValue: tmp,
                    newValue: val,
                    source: self
                });
            });
        },
        get: function(key) {
            return this.__getterSetterInternalMap[key];
        }
    });

    lib.Widget = lib.Class.extend(_.extend({}, lib.GetterSetterMixin, {
        /**
         * Tag name when creating a default $element.
         * @type string
         */
        tag_name: 'div',
        /**
         * Constructs the widget and sets its parent if a parent is given.
         *
         * @constructs openerp.web.Widget
         * @extends openerp.web.CallbackEnabled
         *
         * @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance.
         * When that widget is destroyed by calling destroy(), the current instance will be
         * destroyed too. Can be null.
         * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
         * to bind the current Widget to an already existing part of the DOM, which is not compatible
         * with the DOM insertion methods provided by the current implementation of Widget. So
         * for new components this argument should not be provided any more.
         */
        init: function(parent) {
            lib.GetterSetterMixin.init.call(this);
            this.$element = $(document.createElement(this.tag_name));

            this.setParent(parent);
        },
        /**
         * Destroys the current widget, also destroys all its children before destroying itself.
         */
        destroy: function() {
            _.each(this.getChildren(), function(el) {
                el.destroy();
            });
            if(this.$element != null) {
                this.$element.remove();
            }
            lib.GetterSetterMixin.destroy.call(this);
        },
        /**
         * Renders the current widget and appends it to the given jQuery object or Widget.
         *
         * @param target A jQuery object or a Widget instance.
         */
        appendTo: function(target) {
            var self = this;
            return this.__widgetRenderAndInsert(function(t) {
                self.$element.appendTo(t);
            }, target);
        },
        /**
         * Renders the current widget and prepends it to the given jQuery object or Widget.
         *
         * @param target A jQuery object or a Widget instance.
         */
        prependTo: function(target) {
            var self = this;
            return this.__widgetRenderAndInsert(function(t) {
                self.$element.prependTo(t);
            }, target);
        },
        /**
         * Renders the current widget and inserts it after to the given jQuery object or Widget.
         *
         * @param target A jQuery object or a Widget instance.
         */
        insertAfter: function(target) {
            var self = this;
            return this.__widgetRenderAndInsert(function(t) {
                self.$element.insertAfter(t);
            }, target);
        },
        /**
         * Renders the current widget and inserts it before to the given jQuery object or Widget.
         *
         * @param target A jQuery object or a Widget instance.
         */
        insertBefore: function(target) {
            var self = this;
            return this.__widgetRenderAndInsert(function(t) {
                self.$element.insertBefore(t);
            }, target);
        },
        /**
         * Renders the current widget and replaces the given jQuery object.
         *
         * @param target A jQuery object or a Widget instance.
         */
        replace: function(target) {
            return this.__widgetRenderAndInsert(_.bind(function(t) {
                this.$element.replaceAll(t);
            }, this), target);
        },
        __widgetRenderAndInsert: function(insertion, target) {
            this.render_element();
            insertion(target);
            return this.start();
        },
        /**
         * This is the method to implement to render the Widget.
         */
        render_element: function() {},
        /**
         * Method called after rendering. Mostly used to bind actions, perform asynchronous
         * calls, etc...
         *
         * By convention, the method should return a promise to inform the caller when
         * this widget has been initialized.
         *
         * @returns {jQuery.Deferred}
         */
        start: function() {}
    }));

    return lib;
})();
<|endoftext|>


<commit_before>import React from 'react';
import classnames from 'classnames';

import Columns from '../columns';
import CurrentListStore from '../stores/CurrentListStore';
import ListControl from './ListControl';

import { DropTarget, DragSource } from 'react-dnd';

const ItemsRow = React.createClass({
	propTypes: {
		columns: React.PropTypes.array,
		items: React.PropTypes.object,
		list: React.PropTypes.object,
		index: React.PropTypes.number,
		id: React.PropTypes.any,
		// Injected by React DnD:
		isDragging: React.PropTypes.bool,
		connectDragSource: React.PropTypes.func,
		connectDropTarget: React.PropTypes.func,
		connectDragPreview: React.PropTypes.func
	},
	renderRow (item) {
		let itemId = item.id;
		let rowClassname = classnames({
			'ItemList__row--dragging': this.props.isDragging,
			'ItemList__row--selected': this.props.checkedItems[itemId],
			'ItemList__row--manage': this.props.manageMode,
			'ItemList__row--success': this.props.rowAlert.success === itemId,
			'ItemList__row--failure': this.props.rowAlert.fail === itemId,
		});
		// item fields
		var cells = this.props.columns.map((col, i) => {
			var ColumnType = Columns[col.type] || Columns.__unrecognised__;
			var linkTo = !i ? `/keystone/${this.props.list.path}/${itemId}` : undefined;
			return <ColumnType key={col.path} list={this.props.list} col={col} data={item} linkTo={linkTo} />;
		});

		// add sortable icon when applicable
		if (this.props.list.sortable) {
			cells.unshift(<ListControl key="_sort" type="sortable" dragSource={this.props.connectDragSource} />);
		}

		// add delete/check icon when applicable
		if (!this.props.list.nodelete) {
			cells.unshift(this.props.manageMode ? (
				<ListControl key="_check" type="check" active={this.props.checkedItems[itemId]} />
			) : (
				<ListControl key="_delete" onClick={(e) => this.props.deleteTableItem(item, e)} type="delete" />
			));
		}

		var addRow = (
 this.props.checkTableItem(item, e) : null} className={rowClassname}>{cells}
);

		if(this.props.list.sortable) {
			return (
				// we could add a preview container/image
				// this.props.connectDragPreview(this.props.connectDropTarget(addRow))
				this.props.connectDropTarget(addRow)
			);
		} else {
			return (addRow);
		}
	},
	render () {
		return this.renderRow(this.props.item);
	}
});

module.exports = exports = ItemsRow;

// Expose Sortable

/**
 * Implements drag source.
 */
const dragItem = {
	beginDrag (props) {
		let send = { ...props };
		CurrentListStore.setDragBase(props.item, props.index);
		return { ...send };
	},
	endDrag (props, monitor, component) {

		if (!monitor.didDrop()) {
			return CurrentListStore.resetItems(CurrentListStore.findClonedItemById(props.id).index);
		}

		const base = CurrentListStore.getDragBase();
		const page = CurrentListStore.getCurrentPage();
		const droppedOn = monitor.getDropResult();
		// some drops provide the data for us in prevSortOrder
		const prevSortOrder = droppedOn.prevSortOrder ? droppedOn.prevSortOrder : props.sortOrder;
		// use a given newSortOrder prop or retrieve from the cloned items list
		let newSortOrder = droppedOn.newSortOrder ? droppedOn.newSortOrder : CurrentListStore.findClonedItemByIndex(droppedOn.index).sortOrder;

		// self
		if (prevSortOrder === newSortOrder) {
			if(base.page !== page) {
				// we were dropped on ourself, but not on our original page
				if(droppedOn.index === 0) {
					// item is first in the list
					// save to the sortOrder of the 2nd item - 1
					newSortOrder = CurrentListStore.findClonedItemByIndex(1).sortOrder - 1;
					droppedOn.goToPage = Number(page) - 1;
				} else {
					// item is last in the list
					// save to the sortOrder of the 2nd to last item - 1
					newSortOrder = CurrentListStore.findClonedItemByIndex(droppedOn.index - 1).sortOrder + 1;
					droppedOn.goToPage = Number(page) + 1;
				}
				if (!newSortOrder || !droppedOn.goToPage) {
					// something is wrong so reset
					return CurrentListStore.resetItems(CurrentListStore.findClonedItemById(props.id).index);
				}
			} else {
				return CurrentListStore.resetItems(CurrentListStore.findClonedItemById(props.id).index);
			}
		}
		// dropped on a target
		// droppedOn.goToPage is an optional page override for dropping items on a new page target
		CurrentListStore.reorderItems(props.item, prevSortOrder, newSortOrder, Number(droppedOn.goToPage));
	}
};
/**
 * Implements drag target.
 */
const dropItem = {

	drop (props, monitor, component) {
		return { ...props };
	},
	hover (props, monitor, component) {

		// reset row alerts
		if(props.rowAlert.success || props.rowAlert.fail) {
			CurrentListStore.rowAlert('reset');
		}

		const dragged = monitor.getItem().index;
		const over = props.index;

		// self
		if (dragged === over) {
		  return;
		}

		CurrentListStore.moveItem(dragged, over, props);
		monitor.getItem().index = over;
	}
};

/**
 * Specifies the props to inject into your component.
 */
function dragProps (connect, monitor) {
  return {
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging(),
    connectDragPreview: connect.dragPreview(),
  };
}

function dropProps (connect) {
	 return {
		 connectDropTarget: connect.dropTarget()
	};
};

exports.Sortable = DragSource('item', dragItem, dragProps)(DropTarget('item', dropItem, dropProps)(ItemsRow));

<commit_msg>fix regression with admin path<commit_after>import React from 'react';
import classnames from 'classnames';

import Columns from '../columns';
import CurrentListStore from '../stores/CurrentListStore';
import ListControl from './ListControl';

import { DropTarget, DragSource } from 'react-dnd';

const ItemsRow = React.createClass({
	propTypes: {
		columns: React.PropTypes.array,
		items: React.PropTypes.object,
		list: React.PropTypes.object,
		index: React.PropTypes.number,
		id: React.PropTypes.any,
		// Injected by React DnD:
		isDragging: React.PropTypes.bool,
		connectDragSource: React.PropTypes.func,
		connectDropTarget: React.PropTypes.func,
		connectDragPreview: React.PropTypes.func
	},
	renderRow (item) {
		let itemId = item.id;
		let rowClassname = classnames({
			'ItemList__row--dragging': this.props.isDragging,
			'ItemList__row--selected': this.props.checkedItems[itemId],
			'ItemList__row--manage': this.props.manageMode,
			'ItemList__row--success': this.props.rowAlert.success === itemId,
			'ItemList__row--failure': this.props.rowAlert.fail === itemId,
		});
		// item fields
		var cells = this.props.columns.map((col, i) => {
			var ColumnType = Columns[col.type] || Columns.__unrecognised__;
			var linkTo = !i ? `${Keystone.adminPath}/${this.props.list.path}/${itemId}` : undefined;
			return <ColumnType key={col.path} list={this.props.list} col={col} data={item} linkTo={linkTo} />;
		});

		// add sortable icon when applicable
		if (this.props.list.sortable) {
			cells.unshift(<ListControl key="_sort" type="sortable" dragSource={this.props.connectDragSource} />);
		}

		// add delete/check icon when applicable
		if (!this.props.list.nodelete) {
			cells.unshift(this.props.manageMode ? (
				<ListControl key="_check" type="check" active={this.props.checkedItems[itemId]} />
			) : (
				<ListControl key="_delete" onClick={(e) => this.props.deleteTableItem(item, e)} type="delete" />
			));
		}

		var addRow = (
 this.props.checkTableItem(item, e) : null} className={rowClassname}>{cells}
);

		if(this.props.list.sortable) {
			return (
				// we could add a preview container/image
				// this.props.connectDragPreview(this.props.connectDropTarget(addRow))
				this.props.connectDropTarget(addRow)
			);
		} else {
			return (addRow);
		}
	},
	render () {
		return this.renderRow(this.props.item);
	}
});

module.exports = exports = ItemsRow;

// Expose Sortable

/**
 * Implements drag source.
 */
const dragItem = {
	beginDrag (props) {
		let send = { ...props };
		CurrentListStore.setDragBase(props.item, props.index);
		return { ...send };
	},
	endDrag (props, monitor, component) {

		if (!monitor.didDrop()) {
			return CurrentListStore.resetItems(CurrentListStore.findClonedItemById(props.id).index);
		}

		const base = CurrentListStore.getDragBase();
		const page = CurrentListStore.getCurrentPage();
		const droppedOn = monitor.getDropResult();
		// some drops provide the data for us in prevSortOrder
		const prevSortOrder = droppedOn.prevSortOrder ? droppedOn.prevSortOrder : props.sortOrder;
		// use a given newSortOrder prop or retrieve from the cloned items list
		let newSortOrder = droppedOn.newSortOrder ? droppedOn.newSortOrder : CurrentListStore.findClonedItemByIndex(droppedOn.index).sortOrder;

		// self
		if (prevSortOrder === newSortOrder) {
			if(base.page !== page) {
				// we were dropped on ourself, but not on our original page
				if(droppedOn.index === 0) {
					// item is first in the list
					// save to the sortOrder of the 2nd item - 1
					newSortOrder = CurrentListStore.findClonedItemByIndex(1).sortOrder - 1;
					droppedOn.goToPage = Number(page) - 1;
				} else {
					// item is last in the list
					// save to the sortOrder of the 2nd to last item - 1
					newSortOrder = CurrentListStore.findClonedItemByIndex(droppedOn.index - 1).sortOrder + 1;
					droppedOn.goToPage = Number(page) + 1;
				}
				if (!newSortOrder || !droppedOn.goToPage) {
					// something is wrong so reset
					return CurrentListStore.resetItems(CurrentListStore.findClonedItemById(props.id).index);
				}
			} else {
				return CurrentListStore.resetItems(CurrentListStore.findClonedItemById(props.id).index);
			}
		}
		// dropped on a target
		// droppedOn.goToPage is an optional page override for dropping items on a new page target
		CurrentListStore.reorderItems(props.item, prevSortOrder, newSortOrder, Number(droppedOn.goToPage));
	}
};
/**
 * Implements drag target.
 */
const dropItem = {

	drop (props, monitor, component) {
		return { ...props };
	},
	hover (props, monitor, component) {

		// reset row alerts
		if(props.rowAlert.success || props.rowAlert.fail) {
			CurrentListStore.rowAlert('reset');
		}

		const dragged = monitor.getItem().index;
		const over = props.index;

		// self
		if (dragged === over) {
		  return;
		}

		CurrentListStore.moveItem(dragged, over, props);
		monitor.getItem().index = over;
	}
};

/**
 * Specifies the props to inject into your component.
 */
function dragProps (connect, monitor) {
  return {
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging(),
    connectDragPreview: connect.dragPreview(),
  };
}

function dropProps (connect) {
	 return {
		 connectDropTarget: connect.dropTarget()
	};
};

exports.Sortable = DragSource('item', dragItem, dragProps)(DropTarget('item', dropItem, dropProps)(ItemsRow));

<|endoftext|>


<commit_before>// eslint-disable-next-line
'use strict';
const capitalize = require('capitalize');

function getFormElement(formElement) {
  const formElementTypes = {
    input: formElement,
    submit: Object.assign({}, {
      passProps: {
        color: 'isSuccess',
        buttonStyle: 'isOutlined',
      },
    },
    formElement),
  };
  const element = formElementTypes[ formElement.type ] || formElement;

  if (formElement.label === true) {
    element.label = capitalize(element.name);
  }
  if (formElement.submit === true) {
    element.submitOnEnter = true;
  }
  if (formElement.horizontal === true) {
    element.layoutProps = Object.assign({},
      element.layoutProps,
      {

        horizontalform: true,
        formItemStyle: {
        },
      });
  }
  if (formElement.columnSize) {
    element.layoutProps = Object.assign({}, element.layoutProps, { size: formElement.columnSize, });
  }
  return element;
}

function getFormgroups(options) {
  const randomKey = Math.random;
  return options.rows.map(row => (Object.assign({}, row, {
    gridProps: {
      key: randomKey(),
      // style: {
      //   marginTop: 30,
      //   padding: 10,
      // },
      subColumnProps: row.containGrid ? {
        style: {
          // overflow:'hidden',
          overflow:'auto',
        },
      }:undefined,
    },
    formElements: (row.formElements && row.formElements.length)?row.formElements.map(getFormElement):[],
  })));
}

function getFormOnSubmitCallbacks(type, options) {
  const callbackMethod = {
    redirect: {
      callback: 'func:this.props.reduxRouter.push',
      props: options.redirect,
    },
    refresh: {
      callback: 'func:this.props.refresh',
      // props: options.redirect,
    },
    closeModal: {
      callback: 'func:this.props.hideModal',
      props: 'last',
    },
  };
  const submitAction = callbackMethod[ options.onSubmit ] || {};
  const completeAction = callbackMethod[ options.onComplete ] || {};
  return {
    successCallback: submitAction.callback,
    successProps: submitAction.props,
    responseCallback: completeAction.callback,
    responseProps: completeAction.props,
  };
}

function getFormNotification(options) {
  if (options.successNotification) {
    return { notification: options.successNotification, };
  } else if (options.successModal) {
    return { modal:options.successModal, };
  } else {
    return true;
  }
}

function getFormValidations(options) {
  const { validations = [], } = options;
  return validations.reduce((validationArray, key) => {
    const returnValidationObject = {};
    returnValidationObject.name = key.field;
    returnValidationObject.constraints = {
      [ key.field ]: key.constraints,
    };
    validationArray.push(returnValidationObject);
    return validationArray;
  }, []);
}

function createForm(options) {
  const formSubmission = Object.assign({
    success:getFormNotification(options),
    url: options.action, //'/tranforms',
    options: {
      method: (options.method) ? options.method.toUpperCase() : 'POST',
      'Content-Type': 'application/json',
    },
    'params': options.actionParams, /** [{key:':id',val:'_id'}] */

  },
  (options.onSubmit)
    ? getFormOnSubmitCallbacks('onSubmit', options)
    : {},
  (options.onComplete)
    ? getFormOnSubmitCallbacks('onComplete', options)
    : {},
  options.formSubmissionProps
  );
  const validations = getFormValidations(options);
  const formProps = {
    flattenFormData: true,
    footergroups: false,
    useLoadingButtons:true,
    blockPageUI: options.loadingScreen,
    blockPageUILayout: options.loadingScreenLayout,
    hiddenFields: [
      {
        form_val: '$loki',
        form_name: '$loki',
      },
      {
        form_val: 'meta',
        form_name: 'meta',
      },
    ].concat(options.hiddenFields||[]),
    onSubmit: formSubmission,
    validations,
    formgroups: getFormgroups(options),
    style: Object.assign({
      paddingBottom:'1rem',
    }, options.style),
  };
  return {
    component: 'ResponsiveForm',
    props: Object.assign({},
      formProps,
      options.formProps),
    asyncprops: Object.assign({}, options.asyncprops),
  };
}

module.exports = {
  getFormElement,
  getFormgroups,
  getFormOnSubmitCallbacks,
  getFormNotification,
  createForm,
};<commit_msg>passable gridprops<commit_after>// eslint-disable-next-line
'use strict';
const capitalize = require('capitalize');

function getFormElement(formElement) {
  const formElementTypes = {
    input: formElement,
    submit: Object.assign({}, {
      passProps: {
        color: 'isSuccess',
        buttonStyle: 'isOutlined',
      },
    },
    formElement),
  };
  const element = formElementTypes[ formElement.type ] || formElement;

  if (formElement.label === true) {
    element.label = capitalize(element.name);
  }
  if (formElement.submit === true) {
    element.submitOnEnter = true;
  }
  if (formElement.horizontal === true) {
    element.layoutProps = Object.assign({},
      element.layoutProps,
      {

        horizontalform: true,
        formItemStyle: {
        },
      });
  }
  if (formElement.columnSize) {
    element.layoutProps = Object.assign({}, element.layoutProps, { size: formElement.columnSize, });
  }
  return element;
}

function getFormgroups(options) {
  const randomKey = Math.random;
  return options.rows.map(row => (Object.assign({}, row, {
    gridProps: Object.assign({
      key: randomKey(),
      // style: {
      //   marginTop: 30,
      //   padding: 10,
      // },
      subColumnProps: row.containGrid ? {
        style: {
          // overflow:'hidden',
          overflow:'auto',
        },
      }:undefined,
    },row.gridProps),
    formElements: (row.formElements && row.formElements.length)?row.formElements.map(getFormElement):[],
  })));
}

function getFormOnSubmitCallbacks(type, options) {
  const callbackMethod = {
    redirect: {
      callback: 'func:this.props.reduxRouter.push',
      props: options.redirect,
    },
    refresh: {
      callback: 'func:this.props.refresh',
      // props: options.redirect,
    },
    closeModal: {
      callback: 'func:this.props.hideModal',
      props: 'last',
    },
  };
  const submitAction = callbackMethod[ options.onSubmit ] || {};
  const completeAction = callbackMethod[ options.onComplete ] || {};
  return {
    successCallback: submitAction.callback,
    successProps: submitAction.props,
    responseCallback: completeAction.callback,
    responseProps: completeAction.props,
  };
}

function getFormNotification(options) {
  if (options.successNotification) {
    return { notification: options.successNotification, };
  } else if (options.successModal) {
    return { modal:options.successModal, };
  } else {
    return true;
  }
}

function getFormValidations(options) {
  const { validations = [], } = options;
  return validations.reduce((validationArray, key) => {
    const returnValidationObject = {};
    returnValidationObject.name = key.field;
    returnValidationObject.constraints = {
      [ key.field ]: key.constraints,
    };
    validationArray.push(returnValidationObject);
    return validationArray;
  }, []);
}

function createForm(options) {
  const formSubmission = Object.assign({
    success:getFormNotification(options),
    url: options.action, //'/tranforms',
    options: {
      method: (options.method) ? options.method.toUpperCase() : 'POST',
      'Content-Type': 'application/json'
    },
    'params': options.actionParams, /** [{key:':id',val:'_id'}] */

  },
    (options.onSubmit) ? getFormOnSubmitCallbacks('onSubmit', options) : {},
    (options.onComplete) ? getFormOnSubmitCallbacks('onComplete', options) : {}
  );
  const validations = getFormValidations(options);
  const formProps = {
    flattenFormData: true,
    footergroups: false,
    useLoadingButtons:true,
    blockPageUI: options.loadingScreen,
    blockPageUILayout: options.loadingScreenLayout,
    hiddenFields: [
      {
        form_val: '$loki',
        form_name: '$loki',
      },
      {
        form_val: 'meta',
        form_name: 'meta',
      },
    ].concat(options.hiddenFields||[]),
    onSubmit: formSubmission,
    validations,
    formgroups: getFormgroups(options),
    style: Object.assign({
      paddingBottom:'1rem',
    }, options.style),
  };
  return {
    component: 'ResponsiveForm',
    props: Object.assign({},
      formProps,
      options.formProps),
    asyncprops: Object.assign({}, options.asyncprops),
  };
}

module.exports = {
  getFormElement,
  getFormgroups,
  getFormOnSubmitCallbacks,
  getFormNotification,
  createForm,
};<|endoftext|>