<commit_before>if (typeof cloudCare === 'undefined') {
    var cloudCare = {};
}

cloudCare.EMPTY = '---';

var _caseListLoadError = function (model, response) {
    hideLoadingCallback();
    var message = response.responseText || translatedStrings.caseListError;
    showError(message, $("#cloudcare-notifications"));
};

cloudCare.CASE_PROPERTY_MAP = {
    // IMPORTANT: if you edit this you probably want to also edit
    // the corresponding map in the app_manager
    // (corehq/apps/app_manager/models.py)
    'external-id': 'external_id',
    'date-opened': 'date_opened',
    'status': '@status', // must map to corresponding function on the Case model
    'name': 'case_name',
    'owner_id': '@owner_id'
};

cloudCare.Case = Backbone.Model.extend({

    initialize: function () {
        _.bindAll(this, 'getProperty', 'status');
    },
    idAttribute: "case_id",

    getProperty: function (property) {
        if (cloudCare.CASE_PROPERTY_MAP[property] !== undefined) {
            property = cloudCare.CASE_PROPERTY_MAP[property];
        }
        if (property.indexOf("@") == 0) {
            // we use @ signs to denote function references, since the phone
            // does some magic with them that we need to reproduce here.
            var f = this[property.substring(1)];
            if (f !== undefined) {
                return f();
            }
        }
        var root = this.get(property);
        return root ? root : this.get("properties")[property];
    },

    status: function () {
        return this.get("closed") ? "closed" : "open";
    },

    caseProperties: function(language) {
        // Returns case-details as key-value pairs
        // there should be a better way to access language var, than passing here
        var raw_columns = this.get("module") ?
                          this.get("module").get("case_details").long.columns :
                          this.get("appConfig").module.get("case_details").long.columns ; // If Parent-child selection is on
        return _.map(raw_columns, function(col){
            return {
                key: localize(col.header, language),
                value: this.getProperty(col.field) ? this.getProperty(col.field) : cloudCare.EMPTY
            };
        }, this);
    },

    caseDetailsLabel: function(language) {
        var caseLabel = (this.get("module") ?
                         this.get("module").get("case_label")[language] :
                         this.get("appConfig").module.get("case_label")[language]) +
                        ": ";
        if (caseLabel === "Cases: "){
            caseLabel = "";
        }
        var caseName = this.get("properties").case_name;
        return caseLabel + caseName;

    },

    childCaseUrl: function() {
        var parentConfig = this.get("appConfig");
        if (!parentConfig) {
            throw "not a parent case";
        }
        var root = window.location.href.replace(Backbone.history.getFragment(), '');
        return getChildSelectUrl(
            root,
            parentConfig.app_id,
            parentConfig.module_index,
            parentConfig.form_index,
            this.id
        );
    },
});

cloudCare.Details = Backbone.Model.extend({
    // nothing here yet
});

cloudCare.caseViewMixin = {
    lookupField: function (field) {
        var self = this;
        if (self.options.delegation) {
            // casedb maps case_ids to unwrapped case json
            var parent = self.options.casedb[self.model.get('indices').parent.case_id];
            return parent[field];
        } else {
            return self.model.getProperty(field);
        }
    },
    delegationFormName: function () {
        var self = this;
        if (self.options.delegation) {
            var formId = self.model.getProperty('form_id');
            var module = self.options.appConfig.module;
            var form = module.getFormByUniqueId(formId);
            return localize(form.get('name'), self.options.language);
        } else {
            throw "not in delegation mode"
        }
    },
    makeDelegationFormTd: function () {
        var self = this;
        var name;

        try {
            name = self.delegationFormName();
        } catch (e) {
            if (e.type == 'FormLookupError') {
                name = '?';
            } else {
                throw e;
            }
        }
        return $('').text(name);
    },
    makeTd: function (col) {
        var self = this,
            text = self.lookupField(col.field),
            td = $("");
        if (text) {
            return td.text(text);
        } else {
            return td.append(
                $('').text(cloudCare.EMPTY)
            );
        }
    }
};

// Though this is called the CaseView, it actually displays the case
// summary as a line in the CaseListView. Not to be confused with the
// CaseDetailsView
cloudCare.CaseView = Selectable.extend(cloudCare.caseViewMixin).extend({
    tagName: 'tr', // name of (orphan) root tag in this.el
    initialize: function () {
        var self = this;
        _.bindAll(self, 'render', 'select', 'deselect', 'toggle');
        self.selected = false;
    },
    render: function () {
        var self = this;
        if (self.options.delegation) {
            self.makeDelegationFormTd().appendTo(self.el);
        }
        _(self.options.columns).each(function (col) {
            self.makeTd(col).appendTo(self.el);
        });
        return self;
    }
});

cloudCare.CaseList = Backbone.Collection.extend({
    initialize: function () {
        var self = this;
        _.bindAll(self, 'url', 'setUrl');
        self.casedb = {};
    },
    model: cloudCare.Case,
    url: function () {
        return this.caseUrl;
    },
    setUrl: function (url) {
        this.caseUrl = url;
    },
    parse: function (resp) {
        var self = this;
        if (resp.cases) {
            // object: {cases: [...], parents: [...]}
            for (var key in resp) {
                if (resp.hasOwnProperty(key)) {
                    var cases = resp[key];
                    for (var i = 0; i < cases.length; i++) {
                        self.casedb[cases[i]._id] = cases[i];
                    }
                }
            }
            return resp.cases;
        } else {
            // just the array: [...]
            return resp;
        }

    }
});

cloudCare.CaseListView = Backbone.View.extend({

    initialize: function () {
        _.bindAll(this, 'render', 'appendItem', 'appendAll');

        this.caseMap = {};

        this.detailsShort = new cloudCare.Details();
        this.detailsShort.set(this.options.details);

        this.caseList = new cloudCare.CaseList();
        this.caseList.bind('add', this.appendItem);
        this.caseList.bind('reset', this.appendAll);
        if (this.options.cases) {
            this.caseList.reset(this.options.cases);
        } else if (this.options.caseUrl) {
            this.caseList.setUrl(this.options.caseUrl);
            showLoading();
            this.caseList.fetch({
                success: hideLoadingCallback,
                error: _caseListLoadError
            });
        }
    },
    render: function () {
	    var self = this;
	    self.el = $('
').attr("id", "case-list").addClass("span7");
        var table = $("
").addClass("table table-striped datatable").css('clear', 'both').appendTo($(self.el));
        var thead = $("
").appendTo(table);
        var theadrow = $("
").appendTo(thead);
        if (self.options.delegation) {
            $('').appendTo(theadrow);
        }
        _(self.detailsShort.get("columns")).each(function (col) {
            $("").append(' ').append(localize(col.header, self.options.language)).appendTo(theadrow);
        });
        var tbody = $("
").appendTo(table);
        _(self.caseList.models).each(function(item){
            self.appendItem(item);
        });

        return self;
    },
    appendItem: function (item) {
        var self = this;
        var caseView = new cloudCare.CaseView({
            model: item,
            columns: self.detailsShort.get("columns"),
            delegation: self.options.delegation,
            casedb: self.caseList.casedb,
            appConfig: self.options.appConfig,
            language: self.options.language
        });
        // set the app config on the case if it's there
        // so that other events can access it later
        item.set("appConfig", self.options.appConfig);
        caseView.on("selected", function () {
            if (self.selectedCaseView) {
                self.selectedCaseView.deselect();
            }
            if (self.selectedCaseView !== this) {
                self.selectedCaseView = this;
                cloudCare.dispatch.trigger("case:selected", this.model);
            }
        });
        caseView.on("deselected", function () {
            self.selectedCaseView = null;
            cloudCare.dispatch.trigger("case:deselected", this.model);
        });

        $('table tbody', self.el).append(caseView.render().el);
        self.caseMap[item.id] = caseView;

    },
    appendAll: function () {
        this.caseList.each(this.appendItem);
        $('table', this.el).dataTable({
            bFilter: true,
            bPaginate: false,
            bSort: true,
            oLanguage: {
                "sSearch": "Filter cases:"
            },
            sScrollX: $('#case-list').css('width'),
            bScrollCollapse: true
        });
        var $dataTablesFilter = $(".dataTables_filter");
        $dataTablesFilter.css('float', 'none').css('padding', '3px').addClass('span12');
        $dataTablesFilter.addClass("form-search");
        var $inputField = $dataTablesFilter.find("input"),
            $inputLabel = $dataTablesFilter.find("label");

        $dataTablesFilter.append($inputField);
        $inputField.attr("id", "dataTables-filter-box");
        $inputField.addClass("search-query").addClass("input-large");
        $inputField.attr("placeholder", "Filter...");

        $inputLabel.attr("for", "dataTables-filter-box");
        $inputLabel.text('Filter cases:');
        this.el.parent().before($('
').append($dataTablesFilter));
    }
});

cloudCare.CaseDetailsView = Backbone.View.extend(cloudCare.caseViewMixin).extend({
    initialize: function () {
        _.bindAll(this, 'render');
        this.details = new cloudCare.Details();
        this.details.set(this.options.details);
        this.render();
    },

    render: function () {
        var self = this;
        if (!self._everRendered) {
            self.el = $('
').attr("id", "case-details").addClass("span5");
            self._everRendered = true;
        }
        $(self.el).html(""); // clear
        if (self.model) {
            var table = $("
").addClass("table table-striped datatable").appendTo($(self.el));
            var thead = $("
").appendTo(table);
            var theadrow = $("
").appendTo(thead);
	        $("").attr("colspan", "2").text("Case Details for " + self.model.getProperty("name")).appendTo(theadrow);
	        var tbody = $("
").appendTo(table);

            _(self.details.get("columns")).each(function (col) {
                var row = $("
").appendTo(table);
                $("").text(localize(col.header, self.options.language)).appendTo(row);
                self.makeTd(col).appendTo(row);
            });
        }
        return self;
    }
});

cloudCare.CaseMainView = Backbone.View.extend({
    initialize: function () {
        var self = this;
        _.bindAll(this, 'render', 'selectCase', 'fetchCaseList');
        // adding an internal section so that the filter button displays correctly
        self.el = self.options.el;
        self.section = $('
');
        self.section.appendTo(self.el);
        // this is copy-pasted
        self.delegation = self.options.appConfig.form_index === 'task-list';
        self.listView = new cloudCare.CaseListView({
            details: self.options.listDetails,
            cases: self.options.cases,
            case_type: self.options.case_type,
            language: self.options.language,
            caseUrl: self.options.caseUrl,
            appConfig: self.options.appConfig,
            delegation: self.delegation
        });
        $(self.listView.render().el).appendTo($(self.section));
        self.detailsView = new cloudCare.CaseDetailsView({
            details: self.options.summaryDetails,
            language: self.options.language,
            appConfig: self.options.appConfig,
            casedb: self.listView.caseList.casedb,
            delegation: self.delegation
        });
        $(self.detailsView.render().el).appendTo($(self.section));
        $("
").addClass("clear").appendTo($(self.section));
    },

    selectCase: function (caseModel) {
        if (caseModel === null) {
            this.detailsView.model = null;
        } else {
            this.detailsView.model = caseModel;
        }
        this.detailsView.render();
    },

    fetchCaseList: function () {
        showLoading();
        this.listView.caseList.fetch({
            success: hideLoadingCallback,
            error: _caseListLoadError
        });
    },

    render: function () {
        return this;
    }
});

cloudCare.CaseSelectionModel = Backbone.Model.extend({
});

cloudCare.CaseSelectionView = Backbone.View.extend({
    el: $("#case-crumbs"),
    template: _.template($("#template-crumbs").html()),

    initialize: function (){
        var self = this;
        self.model = new cloudCare.CaseSelectionModel();
        self.language = this.options.language;
        self.model.on("change", self.render, this);
    },
    render: function (){
        var self = this;
        var parentCase = self.model.get("parentCase");
        var childCase = self.model.get("childCase");
        var data = {parentCase: null, childCase: null};

        if (parentCase){
            data.parentCase = {};
            data.parentCase.text = parentCase.caseDetailsLabel(self.language);
            data.parentCase.href = parentCase.childCaseUrl();
            data.parentCase.properties = parentCase.caseProperties(self.language);
        }
        if (childCase){
            data.childCase = {};
            data.childCase.text = childCase.caseDetailsLabel(self.language);
            data.childCase.properties = childCase.caseProperties(self.language);
        }
        self.$el.html(self.template(data));
        return self;
    }
});

$.extend( $.fn.dataTableExt.oStdClasses, {
    "sSortAsc": "header headerSortDown",
    "sSortDesc": "header headerSortUp",
    "sSortable": "header"
} );
<commit_msg>Log the actual error message to the console<commit_after>if (typeof cloudCare === 'undefined') {
    var cloudCare = {};
}

cloudCare.EMPTY = '---';

var _caseListLoadError = function (model, response) {
    hideLoadingCallback();
    console.error(response.responseText);
    showError(translatedStrings.caseListError, $("#cloudcare-notifications"));
};

cloudCare.CASE_PROPERTY_MAP = {
    // IMPORTANT: if you edit this you probably want to also edit
    // the corresponding map in the app_manager
    // (corehq/apps/app_manager/models.py)
    'external-id': 'external_id',
    'date-opened': 'date_opened',
    'status': '@status', // must map to corresponding function on the Case model
    'name': 'case_name',
    'owner_id': '@owner_id'
};

cloudCare.Case = Backbone.Model.extend({

    initialize: function () {
        _.bindAll(this, 'getProperty', 'status');
    },
    idAttribute: "case_id",

    getProperty: function (property) {
        if (cloudCare.CASE_PROPERTY_MAP[property] !== undefined) {
            property = cloudCare.CASE_PROPERTY_MAP[property];
        }
        if (property.indexOf("@") == 0) {
            // we use @ signs to denote function references, since the phone
            // does some magic with them that we need to reproduce here.
            var f = this[property.substring(1)];
            if (f !== undefined) {
                return f();
            }
        }
        var root = this.get(property);
        return root ? root : this.get("properties")[property];
    },

    status: function () {
        return this.get("closed") ? "closed" : "open";
    },

    caseProperties: function(language) {
        // Returns case-details as key-value pairs
        // there should be a better way to access language var, than passing here
        var raw_columns = this.get("module") ?
                          this.get("module").get("case_details").long.columns :
                          this.get("appConfig").module.get("case_details").long.columns ; // If Parent-child selection is on
        return _.map(raw_columns, function(col){
            return {
                key: localize(col.header, language),
                value: this.getProperty(col.field) ? this.getProperty(col.field) : cloudCare.EMPTY
            };
        }, this);
    },

    caseDetailsLabel: function(language) {
        var caseLabel = (this.get("module") ?
                         this.get("module").get("case_label")[language] :
                         this.get("appConfig").module.get("case_label")[language]) +
                        ": ";
        if (caseLabel === "Cases: "){
            caseLabel = "";
        }
        var caseName = this.get("properties").case_name;
        return caseLabel + caseName;

    },

    childCaseUrl: function() {
        var parentConfig = this.get("appConfig");
        if (!parentConfig) {
            throw "not a parent case";
        }
        var root = window.location.href.replace(Backbone.history.getFragment(), '');
        return getChildSelectUrl(
            root,
            parentConfig.app_id,
            parentConfig.module_index,
            parentConfig.form_index,
            this.id
        );
    },
});

cloudCare.Details = Backbone.Model.extend({
    // nothing here yet
});

cloudCare.caseViewMixin = {
    lookupField: function (field) {
        var self = this;
        if (self.options.delegation) {
            // casedb maps case_ids to unwrapped case json
            var parent = self.options.casedb[self.model.get('indices').parent.case_id];
            return parent[field];
        } else {
            return self.model.getProperty(field);
        }
    },
    delegationFormName: function () {
        var self = this;
        if (self.options.delegation) {
            var formId = self.model.getProperty('form_id');
            var module = self.options.appConfig.module;
            var form = module.getFormByUniqueId(formId);
            return localize(form.get('name'), self.options.language);
        } else {
            throw "not in delegation mode"
        }
    },
    makeDelegationFormTd: function () {
        var self = this;
        var name;

        try {
            name = self.delegationFormName();
        } catch (e) {
            if (e.type == 'FormLookupError') {
                name = '?';
            } else {
                throw e;
            }
        }
        return $('').text(name);
    },
    makeTd: function (col) {
        var self = this,
            text = self.lookupField(col.field),
            td = $("");
        if (text) {
            return td.text(text);
        } else {
            return td.append(
                $('').text(cloudCare.EMPTY)
            );
        }
    }
};

// Though this is called the CaseView, it actually displays the case
// summary as a line in the CaseListView. Not to be confused with the
// CaseDetailsView
cloudCare.CaseView = Selectable.extend(cloudCare.caseViewMixin).extend({
    tagName: 'tr', // name of (orphan) root tag in this.el
    initialize: function () {
        var self = this;
        _.bindAll(self, 'render', 'select', 'deselect', 'toggle');
        self.selected = false;
    },
    render: function () {
        var self = this;
        if (self.options.delegation) {
            self.makeDelegationFormTd().appendTo(self.el);
        }
        _(self.options.columns).each(function (col) {
            self.makeTd(col).appendTo(self.el);
        });
        return self;
    }
});

cloudCare.CaseList = Backbone.Collection.extend({
    initialize: function () {
        var self = this;
        _.bindAll(self, 'url', 'setUrl');
        self.casedb = {};
    },
    model: cloudCare.Case,
    url: function () {
        return this.caseUrl;
    },
    setUrl: function (url) {
        this.caseUrl = url;
    },
    parse: function (resp) {
        var self = this;
        if (resp.cases) {
            // object: {cases: [...], parents: [...]}
            for (var key in resp) {
                if (resp.hasOwnProperty(key)) {
                    var cases = resp[key];
                    for (var i = 0; i < cases.length; i++) {
                        self.casedb[cases[i]._id] = cases[i];
                    }
                }
            }
            return resp.cases;
        } else {
            // just the array: [...]
            return resp;
        }

    }
});

cloudCare.CaseListView = Backbone.View.extend({

    initialize: function () {
        _.bindAll(this, 'render', 'appendItem', 'appendAll');

        this.caseMap = {};

        this.detailsShort = new cloudCare.Details();
        this.detailsShort.set(this.options.details);

        this.caseList = new cloudCare.CaseList();
        this.caseList.bind('add', this.appendItem);
        this.caseList.bind('reset', this.appendAll);
        if (this.options.cases) {
            this.caseList.reset(this.options.cases);
        } else if (this.options.caseUrl) {
            this.caseList.setUrl(this.options.caseUrl);
            showLoading();
            this.caseList.fetch({
                success: hideLoadingCallback,
                error: _caseListLoadError
            });
        }
    },
    render: function () {
	    var self = this;
	    self.el = $('
').attr("id", "case-list").addClass("span7");
        var table = $("
").addClass("table table-striped datatable").css('clear', 'both').appendTo($(self.el));
        var thead = $("
").appendTo(table);
        var theadrow = $("
").appendTo(thead);
        if (self.options.delegation) {
            $('').appendTo(theadrow);
        }
        _(self.detailsShort.get("columns")).each(function (col) {
            $("").append(' ').append(localize(col.header, self.options.language)).appendTo(theadrow);
        });
        var tbody = $("
").appendTo(table);
        _(self.caseList.models).each(function(item){
            self.appendItem(item);
        });

        return self;
    },
    appendItem: function (item) {
        var self = this;
        var caseView = new cloudCare.CaseView({
            model: item,
            columns: self.detailsShort.get("columns"),
            delegation: self.options.delegation,
            casedb: self.caseList.casedb,
            appConfig: self.options.appConfig,
            language: self.options.language
        });
        // set the app config on the case if it's there
        // so that other events can access it later
        item.set("appConfig", self.options.appConfig);
        caseView.on("selected", function () {
            if (self.selectedCaseView) {
                self.selectedCaseView.deselect();
            }
            if (self.selectedCaseView !== this) {
                self.selectedCaseView = this;
                cloudCare.dispatch.trigger("case:selected", this.model);
            }
        });
        caseView.on("deselected", function () {
            self.selectedCaseView = null;
            cloudCare.dispatch.trigger("case:deselected", this.model);
        });

        $('table tbody', self.el).append(caseView.render().el);
        self.caseMap[item.id] = caseView;

    },
    appendAll: function () {
        this.caseList.each(this.appendItem);
        $('table', this.el).dataTable({
            bFilter: true,
            bPaginate: false,
            bSort: true,
            oLanguage: {
                "sSearch": "Filter cases:"
            },
            sScrollX: $('#case-list').css('width'),
            bScrollCollapse: true
        });
        var $dataTablesFilter = $(".dataTables_filter");
        $dataTablesFilter.css('float', 'none').css('padding', '3px').addClass('span12');
        $dataTablesFilter.addClass("form-search");
        var $inputField = $dataTablesFilter.find("input"),
            $inputLabel = $dataTablesFilter.find("label");

        $dataTablesFilter.append($inputField);
        $inputField.attr("id", "dataTables-filter-box");
        $inputField.addClass("search-query").addClass("input-large");
        $inputField.attr("placeholder", "Filter...");

        $inputLabel.attr("for", "dataTables-filter-box");
        $inputLabel.text('Filter cases:');
        this.el.parent().before($('
').append($dataTablesFilter));
    }
});

cloudCare.CaseDetailsView = Backbone.View.extend(cloudCare.caseViewMixin).extend({
    initialize: function () {
        _.bindAll(this, 'render');
        this.details = new cloudCare.Details();
        this.details.set(this.options.details);
        this.render();
    },

    render: function () {
        var self = this;
        if (!self._everRendered) {
            self.el = $('
').attr("id", "case-details").addClass("span5");
            self._everRendered = true;
        }
        $(self.el).html(""); // clear
        if (self.model) {
            var table = $("
").addClass("table table-striped datatable").appendTo($(self.el));
            var thead = $("
").appendTo(table);
            var theadrow = $("
").appendTo(thead);
	        $("").attr("colspan", "2").text("Case Details for " + self.model.getProperty("name")).appendTo(theadrow);
	        var tbody = $("
").appendTo(table);

            _(self.details.get("columns")).each(function (col) {
                var row = $("
").appendTo(table);
                $("").text(localize(col.header, self.options.language)).appendTo(row);
                self.makeTd(col).appendTo(row);
            });
        }
        return self;
    }
});

cloudCare.CaseMainView = Backbone.View.extend({
    initialize: function () {
        var self = this;
        _.bindAll(this, 'render', 'selectCase', 'fetchCaseList');
        // adding an internal section so that the filter button displays correctly
        self.el = self.options.el;
        self.section = $('
');
        self.section.appendTo(self.el);
        // this is copy-pasted
        self.delegation = self.options.appConfig.form_index === 'task-list';
        self.listView = new cloudCare.CaseListView({
            details: self.options.listDetails,
            cases: self.options.cases,
            case_type: self.options.case_type,
            language: self.options.language,
            caseUrl: self.options.caseUrl,
            appConfig: self.options.appConfig,
            delegation: self.delegation
        });
        $(self.listView.render().el).appendTo($(self.section));
        self.detailsView = new cloudCare.CaseDetailsView({
            details: self.options.summaryDetails,
            language: self.options.language,
            appConfig: self.options.appConfig,
            casedb: self.listView.caseList.casedb,
            delegation: self.delegation
        });
        $(self.detailsView.render().el).appendTo($(self.section));
        $("
").addClass("clear").appendTo($(self.section));
    },

    selectCase: function (caseModel) {
        if (caseModel === null) {
            this.detailsView.model = null;
        } else {
            this.detailsView.model = caseModel;
        }
        this.detailsView.render();
    },

    fetchCaseList: function () {
        showLoading();
        this.listView.caseList.fetch({
            success: hideLoadingCallback,
            error: _caseListLoadError
        });
    },

    render: function () {
        return this;
    }
});

cloudCare.CaseSelectionModel = Backbone.Model.extend({
});

cloudCare.CaseSelectionView = Backbone.View.extend({
    el: $("#case-crumbs"),
    template: _.template($("#template-crumbs").html()),

    initialize: function (){
        var self = this;
        self.model = new cloudCare.CaseSelectionModel();
        self.language = this.options.language;
        self.model.on("change", self.render, this);
    },
    render: function (){
        var self = this;
        var parentCase = self.model.get("parentCase");
        var childCase = self.model.get("childCase");
        var data = {parentCase: null, childCase: null};

        if (parentCase){
            data.parentCase = {};
            data.parentCase.text = parentCase.caseDetailsLabel(self.language);
            data.parentCase.href = parentCase.childCaseUrl();
            data.parentCase.properties = parentCase.caseProperties(self.language);
        }
        if (childCase){
            data.childCase = {};
            data.childCase.text = childCase.caseDetailsLabel(self.language);
            data.childCase.properties = childCase.caseProperties(self.language);
        }
        self.$el.html(self.template(data));
        return self;
    }
});

$.extend( $.fn.dataTableExt.oStdClasses, {
    "sSortAsc": "header headerSortDown",
    "sSortDesc": "header headerSortUp",
    "sSortable": "header"
} );
<|endoftext|>


<commit_before>/**
 * @fileoverview This module detects the kind of well-known browser and version.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */

'use strict';

/**
 * Browser module
 * @module browser
 */

/**
 * This object has an information that indicate the kind of browser.
 * The list below is a detectable browser list.
 *  - ie8 ~ ie11
 *  - chrome
 *  - firefox
 *  - safari
 *  - edge
 * @memberof module:browser
 * @example
 * var browser = require('tui-code-snippet/browser/browser'); // node, commonjs
 *
 * browser.chrome === true; // chrome
 * browser.firefox === true; // firefox
 * browser.safari === true; // safari
 * browser.msie === true; // IE
 * browser.edge === true; // edge
 * browser.others === true; // other browser
 * browser.version; // browser version
 */
var browser = {
  chrome: false,
  firefox: false,
  safari: false,
  msie: false,
  edge: false,
  others: false,
  version: 0
};

if (typeof window !== 'undefined' && window.navigator) {
  detectBrowser();
}

/**
 * Detect the browser.
 * @private
 */
function detectBrowser() {
  var nav = window.navigator;
  var appName = nav.appName.replace(/\s/g, '_');
  var userAgent = nav.userAgent;

  var rIE = /MSIE\s([0-9]+[.0-9]*)/;
  var rIE11 = /Trident.*rv:11\./;
  var rEdge = /Edge\/(\d+)\./;
  var versionRegex = {
    firefox: /Firefox\/(\d+)\./,
    chrome: /Chrome\/(\d+)\./,
    safari: /Version\/([\d.]+).*Safari\/(\d+)/
  };

  var key, tmp;

  var detector = {
    Microsoft_Internet_Explorer: function() { // eslint-disable-line camelcase
      var detectedVersion = userAgent.match(rIE);

      if (detectedVersion) { // ie8 ~ ie10
        browser.msie = true;
        browser.version = parseFloat(detectedVersion[1]);
      } else { // no version information
        browser.others = true;
      }
    },
    Netscape: function() { // eslint-disable-line complexity
      var detected = false;

      if (rIE11.exec(userAgent)) {
        browser.msie = true;
        browser.version = 11;
        detected = true;
      } else if (rEdge.exec(userAgent)) {
        browser.edge = true;
        browser.version = userAgent.match(rEdge)[1];
        detected = true;
      } else {
        for (key in versionRegex) {
          if (versionRegex.hasOwnProperty(key)) {
            tmp = userAgent.match(versionRegex[key]);
            if (tmp && tmp.length > 1) { // eslint-disable-line max-depth
              browser[key] = detected = true;
              browser.version = parseFloat(tmp[1] || 0);
              break;
            }
          }
        }
      }
      if (!detected) {
        browser.others = true;
      }
    }
  };

  var fn = detector[appName];

  if (fn) {
    detector[appName]();
  }
}

module.exports = browser;
<commit_msg>docs: update jsdoc of browser<commit_after>/**
 * @fileoverview This module detects the kind of well-known browser and version.
 * @author NHN FE Development Lab <dl_javascript@nhn.com>
 */

'use strict';

/**
 * Browser module
 * @module browser
 */

/**
 * This object has an information that indicate the kind of browser. It can detect IE8 ~ IE11, Chrome, Firefox, Safari, and Edge.
 * @memberof module:browser
 * @example
 * var browser = require('tui-code-snippet/browser/browser'); // node, commonjs
 *
 * browser.chrome === true; // chrome
 * browser.firefox === true; // firefox
 * browser.safari === true; // safari
 * browser.msie === true; // IE
 * browser.edge === true; // edge
 * browser.others === true; // other browser
 * browser.version; // browser version
 */
var browser = {
  chrome: false,
  firefox: false,
  safari: false,
  msie: false,
  edge: false,
  others: false,
  version: 0
};

if (typeof window !== 'undefined' && window.navigator) {
  detectBrowser();
}

/**
 * Detect the browser.
 * @private
 */
function detectBrowser() {
  var nav = window.navigator;
  var appName = nav.appName.replace(/\s/g, '_');
  var userAgent = nav.userAgent;

  var rIE = /MSIE\s([0-9]+[.0-9]*)/;
  var rIE11 = /Trident.*rv:11\./;
  var rEdge = /Edge\/(\d+)\./;
  var versionRegex = {
    firefox: /Firefox\/(\d+)\./,
    chrome: /Chrome\/(\d+)\./,
    safari: /Version\/([\d.]+).*Safari\/(\d+)/
  };

  var key, tmp;

  var detector = {
    Microsoft_Internet_Explorer: function() { // eslint-disable-line camelcase
      var detectedVersion = userAgent.match(rIE);

      if (detectedVersion) { // ie8 ~ ie10
        browser.msie = true;
        browser.version = parseFloat(detectedVersion[1]);
      } else { // no version information
        browser.others = true;
      }
    },
    Netscape: function() { // eslint-disable-line complexity
      var detected = false;

      if (rIE11.exec(userAgent)) {
        browser.msie = true;
        browser.version = 11;
        detected = true;
      } else if (rEdge.exec(userAgent)) {
        browser.edge = true;
        browser.version = userAgent.match(rEdge)[1];
        detected = true;
      } else {
        for (key in versionRegex) {
          if (versionRegex.hasOwnProperty(key)) {
            tmp = userAgent.match(versionRegex[key]);
            if (tmp && tmp.length > 1) { // eslint-disable-line max-depth
              browser[key] = detected = true;
              browser.version = parseFloat(tmp[1] || 0);
              break;
            }
          }
        }
      }
      if (!detected) {
        browser.others = true;
      }
    }
  };

  var fn = detector[appName];

  if (fn) {
    detector[appName]();
  }
}

module.exports = browser;
<|endoftext|>


<commit_before>var expect    = require("chai").expect;
var BowlingGame = require("../js/BowlingGame.js")

describe("BowlingGame", function() {

	var game;

	beforeEach(function(){
		game = new BowlingGame();
	});

	function rollMany (n, pins) {
		for (var i = 0; i < n; i++) {
			game.roll(pins)
		}
	}

	function rollSpare() {
		game.roll(5);
		game.roll(5);
	}

	function rollStrike() {
		game.roll(10);
	}

	describe("Game 1", function() {
		it("handle gutter game", function() {
			rollMany(20,0)
			expect(game.score()).to.equal(0);
		});
	});

	describe("Game 2", function() {
		it("handle gutter game", function() {
			rollMany(20, 0);
			expect(game.score()).to.equal(0);
		});

		it("should handle all ones", function() {
			rollMany(20, 1);
			expect(game.score()).to.equal(20);
		});
	});

	describe("Game 3", function() {
		it("handle gutter game", function() {
			rollMany(20, 0);
			expect(game.score()).to.equal(0);
		});

		it("should handle all ones", function() {
			rollMany(20, 1);
			expect(game.score()).to.equal(20);
		});

		it("should handle one spare", function() {
			rollSpare();
			game.roll(3);
			rollMany(17, 0);
			expect(game.score()).to.equal(16);
		});
	});

	describe("Game 4", function() {

		it("should handle gutter game", function() {
			rollMany(20, 0);
			expect(game.score()).to.equal(0);
		});

		it("should handle all ones", function() {
			rollMany(20, 1);
			expect(game.score()).to.equal(20);
		});

		it("should handle one spare", function() {
			rollSpare();
			game.roll(3);
			rollMany(17, 0);
			expect(game.score()).to.equal(16);
		});

		it("should handle one strike", function() {
			rollStrike();
			game.roll(3);
			game.roll(4);
			rollMany(16, 0);
			expect(game.score()).to.equal(24);
		});
	});

	describe("Game 5", function() {

		it("should handle gutter game", function() {
			rollMany(20, 0);
			expect(game.score()).to.equal(0);
		});

		it("should handle all ones", function() {
			rollMany(20, 1);
			expect(game.score()).to.equal(20);
		});

		it("should handle one spare", function() {
			rollSpare();
			game.roll(3);
			rollMany(17, 0);
			expect(game.score()).to.equal(16);
		});

		it("should handle one strike", function() {
			rollStrike();
			game.roll(3);
			game.roll(4);
			rollMany(16, 0);
			expect(game.score()).to.equal(24);
		});

		it("should handle a perfect game", function() {
			rollMany(12, 10);
			expect(game.score()).to.equal(300);
		});
	});
});	<commit_msg>Clean up redundant tests group and added more test cases<commit_after>var expect    = require("chai").expect;
var BowlingGame = require("../js/BowlingGame.js")

describe("BowlingGame", function() {

	var game;

	beforeEach(function(){
		game = new BowlingGame();
	});

	function rollMany (n, pins) {
		for (var i = 0; i < n; i++) {
			game.roll(pins)
		}
	}

	function rollSpare(v) {
		v = v || 5;
		game.roll(v);
		game.roll(10 - v);
	}

	function rollStrike() {
		game.roll(10);
	}

	describe("score()", function() {

		it("should handle gutter game", function() {
			rollMany(20, 0);
			expect(game.score()).to.equal(0);
		});

		it("should handle all ones", function() {
			rollMany(20, 1);
			expect(game.score()).to.equal(20);
		});

		it("should handle one spare", function() {
			rollSpare();
			game.roll(3);
			rollMany(17, 0);
			expect(game.score()).to.equal(16);
		});

		it("should handle one strike", function() {
			rollStrike();
			game.roll(3);
			game.roll(4);
			rollMany(16, 0);
			expect(game.score()).to.equal(24);
		});

		it("should handle a perfect game", function() {
			rollMany(12, 10);
			expect(game.score()).to.equal(300);
		});

		it("should handle no miss", function() {
			rollSpare(5);
			rollSpare(8);
			rollSpare(6);
			rollSpare(7);
			rollSpare(7);
			rollSpare(9);
			rollSpare(5);
			rollSpare(8);
			rollSpare(9);
			rollSpare(9);
			rollStrike();
			expect(game.score()).to.equal(178);
		});

		it("should handle strike-spare", function() {
			rollStrike();
			rollSpare(9);
			rollStrike();
			rollSpare(7);
			rollStrike();
			rollSpare(2);
			rollStrike();
			rollSpare(5);
			rollStrike();
			rollSpare(1);
			rollStrike();
			expect(game.score()).to.equal(200);
		});

	});
});	<|endoftext|>


<commit_before>var _ = require('underscore');
var fs = require('fs');
var assert = require('assert');
var util = require('util');
var Carmen = require('..');

describe('geocode', function() {
    var geocoder = new Carmen({
        country: Carmen.auto(__dirname + '/fixtures/01-ne.country.s3'),
        province: Carmen.auto(__dirname + '/fixtures/02-ne.province.s3')
    });
    before(function(done) {
        geocoder._open(done);
    });
    it ('forward', function(done) {
        geocoder.geocode('georgia', {}, function(err, res) {
            assert.ifError(err);
            // fs.writeFileSync(__dirname + '/fixtures/geocode-forward.json', JSON.stringify(res, null, 4));
            assert.deepEqual(require(__dirname + '/fixtures/geocode-forward.json'), res);
            done();
        });
    });
    it ('reverse', function(done) {
        geocoder.geocode('0, 40', {}, function(err, res) {
            assert.ifError(err);
            // fs.writeFileSync(__dirname + '/fixtures/geocode-reverse.json', JSON.stringify(res, null, 4));
            assert.deepEqual(require(__dirname + '/fixtures/geocode-reverse.json'), res);
            done();
        });
    });
});
<commit_msg>Add (failing) test for searches with no results.<commit_after>var _ = require('underscore');
var fs = require('fs');
var assert = require('assert');
var util = require('util');
var Carmen = require('..');

describe('geocode', function() {
    var geocoder = new Carmen({
        country: Carmen.auto(__dirname + '/fixtures/01-ne.country.s3'),
        province: Carmen.auto(__dirname + '/fixtures/02-ne.province.s3')
    });
    before(function(done) {
        geocoder._open(done);
    });
    it ('forward', function(done) {
        geocoder.geocode('georgia', {}, function(err, res) {
            assert.ifError(err);
            // fs.writeFileSync(__dirname + '/fixtures/geocode-forward.json', JSON.stringify(res, null, 4));
            assert.deepEqual(require(__dirname + '/fixtures/geocode-forward.json'), res);
            done();
        });
    });
    it ('reverse', function(done) {
        geocoder.geocode('0, 40', {}, function(err, res) {
            assert.ifError(err);
            // fs.writeFileSync(__dirname + '/fixtures/geocode-reverse.json', JSON.stringify(res, null, 4));
            assert.deepEqual(require(__dirname + '/fixtures/geocode-reverse.json'), res);
            done();
        });
    });
    it ('noresults', function(done) {
        geocoder.geocode('asdfasdf', {}, function(err, res) {
            assert.ifError(err);
            // fs.writeFileSync(__dirname + '/fixtures/geocode-noresults.json', JSON.stringify(res, null, 4));
            assert.deepEqual(require(__dirname + '/fixtures/geocode-noresults.json'), res);
            done();
        });
    });
});
<|endoftext|>


<commit_before>//  Mocha ChaiJS Tests for joeypc.com JavaScript library
//  http://danmckeown.info/code/ryukyu/ryukyu-docs/randomColorsPage.html
//  May 2016    v0.1.0

var chai = require('chai');
chai.use(require('chai-jq'));

var domSimulator = require('jsdom-global')();   //  This requires jsdom
                                        //  npm install jsdom-global
                                        //  npm install jsdom
$ = require('../js/jquery.js');

var fs = require('fs');
var vm = require('vm');

//  var $ = require('../js/jquery.js');
// var jQpath = './js/jquery.js';
// var jQcode = fs.readFileSync(jQpath);
// vm.runInThisContext(jQcode);

//  joeypc_make_random_light_text = require('../js/joeypc.js');
var path = './js/joeypc.js';
var code = fs.readFileSync(path);
vm.runInThisContext(code);

//	var jq_plugin  = require("chai-jq");       //  This requires chai-jq
                                        //  npm install chai-jq
//	chai.use(jq_plugin);                    // Inject jq plugin.
//  var assert = require('chai').assert;    //  Chai needs to be installed:
                                        //  npm install chai
//  var expect = require('chai').expect;    //  Also the Mocha Testing Framework:
                                        //  npm install mocha
var expect = chai.expect;
var assert = chai.assert;

describe('joeypc_make_random_light_text', function() {
    it('the function joeypc_make_random_light_text() is a function', function () {
      expect(joeypc_make_random_light_text).to.be.a('function');
    });
});

describe('joeypc_change_background', function() {
    it('the function joeypc_change_background yellow changes the body background color to yellow', function () {
		joeypc_change_background('yellow');
			var the_body = $('body');
			expect(the_body).to.be.$visible;
			expect(the_body).to.have.$css('background-color', 'yellow');
    });
});

describe('joeypc_random_digit', function() {
    it('the function joeypc_random_digit returns a number from 0 to 9', function () {
      var the_digit = joeypc_random_digit();
		console.log('digit returned: ' + the_digit);
		assert.isBelow(the_digit, 10, 'the digit is less than 10');
    });
});<commit_msg>remove unnecessary comments from joeypc tests<commit_after>//  Mocha ChaiJS Tests for joeypc.com JavaScript library
//  http://danmckeown.info/code/ryukyu/ryukyu-docs/randomColorsPage.html
//  May 2016    v0.1.1

var chai = require('chai');		//  npm install chai
chai.use(require('chai-jq'));	//  npm install chai-jq

var domSimulator = require('jsdom-global')();   //  npm install jsdom-global
                                        		//  npm install jsdom
$ = require('../js/jquery.js');		//	link to jQuery file

var fs = require('fs');
var vm = require('vm');

var path = './js/joeypc.js';
var code = fs.readFileSync(path);
vm.runInThisContext(code);

var expect = chai.expect;
var assert = chai.assert;

describe('joeypc_make_random_light_text', function() {
    it('the function joeypc_make_random_light_text() is a function', function () {
      expect(joeypc_make_random_light_text).to.be.a('function');
    });
});

describe('joeypc_change_background', function() {
    it('the function joeypc_change_background(yellow) changes the body background color to yellow', function () {
		joeypc_change_background('yellow');
			var the_body = $('body');
			expect(the_body).to.be.$visible;
			expect(the_body).to.have.$css('background-color', 'yellow');
    });
});

describe('joeypc_random_digit', function() {
    it('the function joeypc_random_digit returns a number from 0 to 9', function () {
      var the_digit = joeypc_random_digit();
		console.log('digit returned: ' + the_digit);
		assert.isBelow(the_digit, 10, 'the digit is less than 10');
    });
});
<|endoftext|>


<commit_before>/**
 * Copyright 2015 IBM Corp. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

var app = require('../app');
var request = require('supertest');

describe('POST /api/message', function() {
  it('Generate Error when no content passed', function(done) {
    request(app).post('/api/message').expect(500, done);
  });
  it('Generate 200 when passing in context', function(done) {
    request(app).post('/api/message').send('{"context":{}, "input":{"text": "Hi"}}').expect(200, done);
  });
});
<commit_msg>Update test.express.js<commit_after>/**
 * Copyright 2015 IBM Corp. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

var app = require('../app');
var request = require('supertest');

describe('POST /api/message', function() {
  it('Generate Error when no content passed', function(done) {
    request(app).post('/api/message').expect(500, done);
  });
  // it('Generate 200 when passing in context', function(done) {
  //   request(app).post('/api/message').send('{"context":{}, "input":{"text": "Hi"}}').expect(200, done);
  // });
});
<|endoftext|>


<commit_before>module('title-format');

test('null getFormattedTitle if title-format is missing', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('datetime', '1970-01-01T00:00:00.000Z');
  equal(time.getFormattedTitle(), null);
});

test('null getFormattedTitle if datetime is missing', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('title-format', '%Y-%m-%d %H:%M');
  equal(time.getFormattedTitle(), null);
});

test('getFormattedTitle with title-formatted datetime', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('datetime', '1970-01-01T00:00:00.000Z');
  time.setAttribute('title-format', '%Y-%m-%d');
  equal(time.getFormattedTitle(), window.epochLocalDate);
});

test('getFormattedTitle with %z timezone offset', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('datetime', '1970-01-01T00:00:00.000Z');
  time.setAttribute('title-format', '%z');
  ok(time.getFormattedTitle().match(/\-\d\d\d\d/)[0]);
});

test('skips setting a title if title-format is missing', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('datetime', '1970-01-01T00:00:00.000Z');
  equal(time.getAttribute('title'), null);
});

test('skips setting a title if datetime is missing', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('title-format', '%Y-%m-%d %H:%M');
  equal(time.getAttribute('title'), null);
});

test('sets the title title-formatted datetime', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('datetime', '1970-01-01T00:00:00.000Z');
  time.setAttribute('title-format', '%Y-%m-%d');
  equal(time.getAttribute('title'), window.epochLocalDate);
});

test('set the title when parsed element is upgraded', function() {
  var root = document.createElement('div');
  root.innerHTML = '<time is="local-time" datetime="1970-01-01T00:00:00.000Z"' +
    ' title-format="%Y-%m-%d"></time>';
  if ('CustomElements' in window) {
    window.CustomElements.upgradeSubtree(root);
  }
  equal(root.children[0].getAttribute('title'), window.epochLocalDate);
});

test('updates title if title-formatted changes', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('datetime', '1970-01-01T00:00:00.000Z');
  time.setAttribute('title-format', '%Y-%m-%d');
  equal(time.getAttribute('title'), window.epochLocalDate);
  time.setAttribute('title-format', '%Y');
  equal(time.getAttribute('title'), window.epochLocalYear);
});

test('updates title if datetime changes', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('datetime', '1970-01-01T00:00:00.000Z');
  time.setAttribute('title-format', '%Y-%m-%d');
  equal(time.getAttribute('title'), window.epochLocalDate);
  time.setAttribute('datetime', '1971-01-01T00:00:00.000Z');
  equal(time.getAttribute('title'), window.yearAfterEpochLocalDate);
});
<commit_msg>Fix tests.<commit_after>module('title-format');

test('null getFormattedTitle if datetime is missing', function() {
  var time = document.createElement('time', 'local-time');
  equal(time.getFormattedTitle(), null);
});

test('locale-aware getFormattedTitle for datetime value', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('datetime', '1970-01-01T00:00:00.000Z');
  ok(time.getFormattedTitle().match(/ \d\d\d\d /)[0]);
});

test('skips setting a title attribute if already provided', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('title', 'does not change')
  time.setAttribute('datetime', '1970-01-01T00:00:00.000Z');
  equal(time.getAttribute('title'), 'does not change');
});

test('skips setting a title attribute if datetime is missing', function() {
  var time = document.createElement('time', 'local-time');
  equal(time.getAttribute('title'), null);
});

test('sets the title attribute for datetime value', function() {
  var time = document.createElement('time', 'local-time');
  time.setAttribute('datetime', '1970-01-01T00:00:00.000Z');
  ok(time.getAttribute('title').match(/ \d\d\d\d /)[0]);
});

test('set the title attribute when parsed element is upgraded', function() {
  var root = document.createElement('div');
  root.innerHTML = '<time is="local-time" datetime="1970-01-01T00:00:00.000Z"></time>';
  if ('CustomElements' in window) {
    window.CustomElements.upgradeSubtree(root);
  }
  ok(root.children[0].getAttribute('title').match(/ \d\d\d\d /)[0]);
});
<|endoftext|>


<commit_before>// var tntTreeNode = require("tnt.tree.node");

// Given a flat structure of associations
// convert and return that structure to a tree
var rootId = "cttv_root";
var childrenProperty = "children";

var flat2tree = function (config) { // cProp -- children property
    var data;
    if (Array.isArray(config)) {
        data = config;
    } else {
        data = config;
        var cProp = config.childrenProperty;
        if (cProp) {
            childrenProperty = cProp;
        }
    }
    var associations = data.data;
    var therapeuticAreas = data.therapeutic_areas;
    // Unfold the associations with more than 1 path
    var unfoldedAssociations = unfoldAssoc(associations);

    // Sort them by min path length
    var sortedAssociations = unfoldedAssociations.sort(sortByPathLength);

    var tree = {
        "__id": rootId,
        "__key": rootId,
        "name" : "cttv_disease",
        "disease": {
            "id": rootId,
            "efo_info": {
                "label": ""
            }
        }
    };

    var tas = getTAs(therapeuticAreas);

    for (var i=0; i<sortedAssociations.length; i++) {
        addNode(sortedAssociations[i], tree, tas);
    }
    return tree;
};

function unfoldAssoc (arr) {
    var unfold = [];
    for (var i=0; i<arr.length; i++) {
        var node = arr[i];
        if (node.disease.efo_info.path.length > 1) {
            for (var j=1; j<node.disease.efo_info.path.length; j++) {
                var clone = cloneNode(node);
                clone.currPath = node.disease.efo_info.path[j];
                unfold.push(clone);
            }
        }
        node.currPath = node.disease.efo_info.path[0];

        unfold.push(node);
    }

    return unfold;
}

function addNode (node, tree, tas) {
    // nodes don't have a name, we put one there
    node.__id = node.disease.id;
    node.__name = node.disease.efo_info.label;
    node.name = node.disease.efo_info.label;
    node.label = node.disease.efo_info.label;
    node.__association_score = node.association_score.overall;
    node.__evidence_count = node.evidence_count.total;
    var path = node.currPath;

    var parent = findParent(path, tree, node);
    delete(node.currPath);

    // If the parent is cttv_root and the node is not a therapeutic area (path.length > 2) we search for the TA in the set of TAs
    if ((parent.__id === rootId) && (path.length > 1)) {
        var taId = node.target.id + "-" + path[0];
        var ta = tas[taId];
        if (!ta) {
            console.error("We do not have TA: " + taId);
        }
        ta.__id = path[0];
        ta.__name = ta.disease.efo_info.label;
        ta.name = ta.disease.efo_info.label;
        ta.label = ta.disease.efo_info.label;
        ta.__association_score = ta.association_score.overall;
        ta.__evidence_count = ta.evidence_count.total;

        if (!tree[childrenProperty]) {
            tree[childrenProperty] = [];
        }
        tree[childrenProperty].push(ta);
        parent = ta;
    }

    if (!parent[childrenProperty]) {
        parent[childrenProperty] = [];
    }

    // Only push the child if it is not there
    if (!hasTwin(parent[childrenProperty], node)) {
        parent[childrenProperty].push(node);
    }
}

function hasTwin (siblings, himself) {
    for (var i=0; i<siblings.length; i++) {
        if (siblings[i].__id === himself.__id) {
            return true;
        }
    }
    return false;
}

function getTAs (arr) {
    var tas = {};
    for (var i=0; i<arr.length; i++) {
        var ta = arr[i];
        tas[ta.id] = ta;
    }
    return tas;
}

function findParent (path, tree, myself) {
    var children = tree[childrenProperty] || [];
    for (var i=0; i<path.length; i++) {
        var found = false;
        FINDINCHILDREN:
        for (var j=0; j<children.length; j++) {
            var child = children[j];
            if ((child.__id === path[i]) && (child.__id !== myself.__id)) {
                tree = child;
                children = child[childrenProperty] || [];
                found = true;
                break FINDINCHILDREN;
            }
        }
        if (!found) {
            return tree;
        }
    }
    return tree;
}

function sortByPathLength (a, b) {
    return a.currPath.length - b.currPath.length;
}

// Fast clone methods
function cloneNode (n) {
    var c = {
        __association_score: n.__association_score,
        __evidence_count: n.__evidence_count,
        id: n.id,
        __id: n.__id,
        __name: n.__name,
        target: {
            id: n.target.id,
            name: n.target.gene_info.name,
            symbol: n.target.gene_info.symbol
        },
        disease: cloneDisease(n.disease),
        association_score: cloneAssociationScore(n.association_score),
        evidence_count: cloneEvidenceCount(n.evidence_count)
    };
    if (n.is_direct) {
        c.is_direct = n.is_direct;
    }
    return c;
}

function cloneDisease (disease) {
    var d = {
        id: disease.id,
        efo_info: cloneEfoInfo(disease.efo_info)
    };

    return d;
}

function cloneEfoInfo (efo) {
    var e = {
        label: efo.label,
        path: clonePath(efo.path),
        therapeutic_area: cloneTherapeuticArea(efo.therapeutic_area)
    };
    return e;
}

function cloneTherapeuticArea (tas) {
    var ta = {
        codes: cloneArr(tas.codes),
        labels: cloneArr(tas.labels)
    };
    return ta;
}

function clonePath (p) {
    var c = [];
    for (var i=0; i<p.length; i++) {
        c.push(p[i]);
    }
    return c;
}

function cloneEvidenceCount (ect) {
    var ec = {
        total: ect.total,
        datatype: cloneObj(ect.datatype),
        datasource: cloneObj(ect.datasource)
    };
    return ec;
}

function cloneAssociationScore (asr) {
    var as = {
        overall: asr.overall,
        datatypes: cloneObj(asr.datatypes),
        datasources: cloneObj(asr.datasources)
    };

    return as;
}

function cloneArr (arr) {
    var a = [];
    for (var i=0; i<arr.length; i++) {
        a.push(arr[i]);
    }
    return a;
}

function cloneObj (obj) {
    var o = {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            o[key] = obj[key];
        }
    }
    return o;
}

module.exports = exports = {
    flat2tree: flat2tree
};
<commit_msg>Process tree data with full list of therapeutic areas; issue opentargets/platform#603<commit_after>// Given a flat structure of associations
// convert and return that structure to a tree
var rootId = "cttv_root";
var childrenProperty = "children";

var flat2tree = function (config, taslist) { // cProp -- children property
    var data;
    if (Array.isArray(config)) {
        data = config;
    } else {
        data = config;
        var cProp = config.childrenProperty;
        if (cProp) {
            childrenProperty = cProp;
        }
    }
    var associations = data.data;
    var therapeuticAreas = data.therapeutic_areas;
    // Unfold the associations with more than 1 path
    var unfoldedAssociations = unfoldAssoc(associations);

    // Sort them by min path length
    var sortedAssociations = unfoldedAssociations.sort(sortByPathLength);

    var tree = {
        "__id": rootId,
        "__key": rootId,
        "name" : "cttv_disease",
        "disease": {
            "id": rootId,
            "efo_info": {
                "label": ""
            }
        }
    };

    var tas = getTAs(therapeuticAreas, taslist);

    for (var i=0; i<sortedAssociations.length; i++) {
        addNode(sortedAssociations[i], tree, tas);
    }
    return tree;
};

function unfoldAssoc (arr) {
    var unfold = [];
    for (var i=0; i<arr.length; i++) {
        var node = arr[i];
        if (node.disease.efo_info.path.length > 1) {
            for (var j=1; j<node.disease.efo_info.path.length; j++) {
                var clone = cloneNode(node);
                clone.currPath = node.disease.efo_info.path[j];
                unfold.push(clone);
            }
        }
        node.currPath = node.disease.efo_info.path[0];

        unfold.push(node);
    }

    return unfold;
}

function addNode (node, tree, tas) {
    // nodes don't have a name, we put one there
    node.__id = node.disease.id;
    node.__name = node.disease.efo_info.label;
    node.name = node.disease.efo_info.label;
    node.label = node.disease.efo_info.label;
    node.__association_score = node.association_score.overall;
    node.__evidence_count = node.evidence_count.total;
    var path = node.currPath;

    var parent = findParent(path, tree, node);
    delete(node.currPath);

    // If the parent is cttv_root and the node is not a therapeutic area (path.length > 2) we search for the TA in the set of TAs
    if ((parent.__id === rootId) && (path.length > 1)) {
        // var taId = node.target.id + "-" + path[0];
        var taId = 'TA-' + path[0];
        var ta = tas[taId];
        if (!ta) {
            console.error("We do not have TA: " + taId);
        }
        ta.__id = path[0];
        ta.__name = ta.disease.efo_info.label;
        ta.name = ta.disease.efo_info.label;
        ta.label = ta.disease.efo_info.label;
        ta.__association_score = ta.association_score.overall;
        ta.__evidence_count = ta.evidence_count.total;

        if (!tree[childrenProperty]) {
            tree[childrenProperty] = [];
        }
        tree[childrenProperty].push(ta);
        parent = ta;
    }

    if (!parent[childrenProperty]) {
        parent[childrenProperty] = [];
    }

    // Only push the child if it is not there
    if (!hasTwin(parent[childrenProperty], node)) {
        parent[childrenProperty].push(node);
    }
}

function hasTwin (siblings, himself) {
    for (var i=0; i<siblings.length; i++) {
        if (siblings[i].__id === himself.__id) {
            return true;
        }
    }
    return false;
}

/**
 * Merge list of therapeutic areas from the associations and the full static list of TAs into a map
 * @param {*} arr array of therapeutic areas for these associations
 * @param {*} therapeuticareas the static full list of therapeautic areas as returned by the API
 */
function getTAs (arr, therapeuticareas) {
    arr = arr || [];    // avoid 'undefined'-related errors as per some associations
    var tas = {};
    for (var i=0; i<therapeuticareas.length; i++) {
        var ta = therapeuticareas[i];
        // since this is the static list of therapeutic areas, it has only label and code
        // so we initialize other params as needed
        // NOTE: the 'TA-' is used to avoid possible errors/conflicts caused by tas names
        // e.g. on therapeutic area is called 'Function' (to be precise object.Function works
        // but object.function doesn't as it's a javascript reserved word)
        tas['TA-'+ta.code] = {
            id: ta.code,
            disease: {
                efo_info: {
                    label: ta.label
                },
                id: ta.code,
            },
            association_score: {
                overall: 0,
                datatypes: {}
            },
            target: {
                gene_info: {},
                id: undefined
            },
            evidence_count : {}
        };
    }
    // update and override with association-specific TAs
    for (var i=0; i<arr.length; i++) {
        var ta = arr[i];
        tas['TA-'+ta.disease.id] = ta;
    }
    return tas;
}

function findParent (path, tree, myself) {
    var children = tree[childrenProperty] || [];
    for (var i=0; i<path.length; i++) {
        var found = false;
        FINDINCHILDREN:
        for (var j=0; j<children.length; j++) {
            var child = children[j];
            if ((child.__id === path[i]) && (child.__id !== myself.__id)) {
                tree = child;
                children = child[childrenProperty] || [];
                found = true;
                break FINDINCHILDREN;
            }
        }
        if (!found) {
            return tree;
        }
    }
    return tree;
}

function sortByPathLength (a, b) {
    return a.currPath.length - b.currPath.length;
}

// Fast clone methods
function cloneNode (n) {
    var c = {
        __association_score: n.__association_score,
        __evidence_count: n.__evidence_count,
        id: n.id,
        __id: n.__id,
        __name: n.__name,
        target: {
            id: n.target.id,
            name: n.target.gene_info.name,
            symbol: n.target.gene_info.symbol
        },
        disease: cloneDisease(n.disease),
        association_score: cloneAssociationScore(n.association_score),
        evidence_count: cloneEvidenceCount(n.evidence_count)
    };
    if (n.is_direct) {
        c.is_direct = n.is_direct;
    }
    return c;
}

function cloneDisease (disease) {
    var d = {
        id: disease.id,
        efo_info: cloneEfoInfo(disease.efo_info)
    };

    return d;
}

function cloneEfoInfo (efo) {
    var e = {
        label: efo.label,
        path: clonePath(efo.path),
        therapeutic_area: cloneTherapeuticArea(efo.therapeutic_area)
    };
    return e;
}

function cloneTherapeuticArea (tas) {
    var ta = {
        codes: cloneArr(tas.codes),
        labels: cloneArr(tas.labels)
    };
    return ta;
}

function clonePath (p) {
    var c = [];
    for (var i=0; i<p.length; i++) {
        c.push(p[i]);
    }
    return c;
}

function cloneEvidenceCount (ect) {
    var ec = {
        total: ect.total,
        datatype: cloneObj(ect.datatype),
        datasource: cloneObj(ect.datasource)
    };
    return ec;
}

function cloneAssociationScore (asr) {
    var as = {
        overall: asr.overall,
        datatypes: cloneObj(asr.datatypes),
        datasources: cloneObj(asr.datasources)
    };

    return as;
}

function cloneArr (arr) {
    var a = [];
    for (var i=0; i<arr.length; i++) {
        a.push(arr[i]);
    }
    return a;
}

function cloneObj (obj) {
    var o = {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            o[key] = obj[key];
        }
    }
    return o;
}

module.exports = exports = {
    flat2tree: flat2tree
};
<|endoftext|>


<commit_before>module.exports = (function () {
    'use strict';

    // Require dependencies
    var u = require('utils/index'),
        iterate = require('_iterator');

    /**
     * Applies CSS styles to DOM element(s).
     * @param {object} styles Object literal holding CSS style properties.
     * @return {*}
     */
    return function css(styles) {
        /**
         * @private
         * @param {object} element The HTML element to add the syyles on.
         */
        function applyStyles(element) {
            Object.keys(styles).forEach(function (key) {
                element.style[key] = styles[key];
            });
        }

        iterate(this, function (el) {
            u.is.element(el) && applyStyles(el);
        });

        return this;
    };
}());
<commit_msg>Check if argument provided is a plin object<commit_after>module.exports = (function () {
    'use strict';

    // Require dependencies
    var u = require('utils/index'),
        iterate = require('_iterator');

    /**
     * Applies CSS styles to DOM element(s).
     * @param {object} styles Object literal holding CSS style properties.
     * @return {object} The DOMQuery object
     */
    return function css(styles) {
        /**
         * @private
         * @param {object} element The HTML element to add the styles on.
         */
        function applyStyles(element) {
            u.is.plainObject(styles) && Object.keys(styles).forEach(function (key) {
                element.style[key] = styles[key];
            });
        }

        iterate(this, function (el) {
            u.is.element(el) && applyStyles(el);
        });

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


<commit_before>/**
 * Handsontable TableView constructor
 * @param {Object} instance
 */
Handsontable.TableView = function (instance) {
  var that = this
    , $window = $(window)
    , $documentElement = $(document.documentElement);

  this.instance = instance;
  this.settings = instance.getSettings();
  this.settingsFromDOM = instance.getSettingsFromDOM();

  instance.rootElement.data('originalStyle', instance.rootElement[0].getAttribute('style')); //needed to retrieve original style in jsFiddle link generator in HT examples. may be removed in future versions
  // in IE7 getAttribute('style') returns an object instead of a string, but we only support IE8+

  instance.rootElement.addClass('handsontable');

  var table = document.createElement('TABLE');
  table.className = 'htCore';
  this.THEAD = document.createElement('THEAD');
  table.appendChild(this.THEAD);
  this.TBODY = document.createElement('TBODY');
  table.appendChild(this.TBODY);

  instance.$table = $(table);
  instance.container.prepend(instance.$table);

  instance.rootElement.on('mousedown.handsontable', function (event) {
    if (!that.isTextSelectionAllowed(event.target)) {
      clearTextSelection();
      event.preventDefault();
      window.focus(); //make sure that window that contains HOT is active. Important when HOT is in iframe.
    }
  });

  $documentElement.on('keyup.' + instance.guid, function (event) {
    if (instance.selection.isInProgress() && !event.shiftKey) {
      instance.selection.finish();
    }
  });

  var isMouseDown;

  $documentElement.on('mouseup.' + instance.guid, function (event) {
    if (instance.selection.isInProgress() && event.which === 1) { //is left mouse button
      instance.selection.finish();
    }

    isMouseDown = false;

    if (instance.autofill.handle && instance.autofill.handle.isDragged) {
      if (instance.autofill.handle.isDragged > 1) {
        instance.autofill.apply();
      }
      instance.autofill.handle.isDragged = 0;
    }

    if (Handsontable.helper.isOutsideInput(document.activeElement)) {
      instance.unlisten();
    }
  });

  $documentElement.on('mousedown.' + instance.guid, function (event) {
    var next = event.target;

    if (next.shadowRoot && Handsontable.Dom.isChildOf(instance.rootElement[0], next.shadowRoot)) {
      return; //click inside Web Component
    }

    if (next !== that.wt.wtTable.spreader) { //immediate click on "spreader" means click on the right side of vertical scrollbar
      while (next !== document.documentElement) {
        if (next === null) {
          return; //click on something that was a row but now is detached (possibly because your click triggered a rerender)
        }
        if (next === instance.rootElement[0]) {
          return; //click inside container
        }
        next = next.parentNode;
      }
    }

    //function did not return until here, we have an outside click!

    if (that.settings.outsideClickDeselects) {
      instance.deselectCell();
    }
    else {
      instance.destroyEditor();
    }
  });

  instance.$table.on('selectstart', function (event) {
    if (that.settings.fragmentSelection) {
      return;
    }

    //https://github.com/warpech/jquery-handsontable/issues/160
    //selectstart is IE only event. Prevent text from being selected when performing drag down in IE8
    event.preventDefault();
  });

  var clearTextSelection = function () {
    //http://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript
    if (window.getSelection) {
      if (window.getSelection().empty) {  // Chrome
        window.getSelection().empty();
      } else if (window.getSelection().removeAllRanges) {  // Firefox
        window.getSelection().removeAllRanges();
      }
    } else if (document.selection) {  // IE?
      document.selection.empty();
    }
  };

  var walkontableConfig = {
    debug: function () {
      return that.settings.debug;
    },
    table: table,
    stretchH: this.settings.stretchH,
    data: instance.getDataAtCell,
    totalRows: instance.countRows,
    totalColumns: instance.countCols,
    offsetRow: 0,
    offsetColumn: 0,
    fixedColumnsLeft: function () {
      return that.settings.fixedColumnsLeft;
    },
    fixedRowsTop: function () {
      return that.settings.fixedRowsTop;
    },
    rowHeaders: function () {
      return instance.hasRowHeaders() ? [function (index, TH) {
        that.appendRowHeader(index, TH);
      }] : []
    },
    columnHeaders: function () {
      return instance.hasColHeaders() ? [function (index, TH) {
        that.appendColHeader(index, TH);
      }] : []
    },
    columnWidth: instance.getColWidth,
    rowHeight: instance.getRowHeight,
    cellRenderer: function (row, col, TD) {

      var prop = that.instance.colToProp(col)
        , cellProperties = that.instance.getCellMeta(row, col)
        , renderer = that.instance.getCellRenderer(cellProperties);

      var value = that.instance.getDataAtRowProp(row, prop);

      renderer(that.instance, TD, row, col, prop, value, cellProperties);

      Handsontable.hooks.run(that.instance, 'afterRenderer', TD, row, col, prop, value, cellProperties);

    },
    selections: {
      current: {
        className: 'current',
        border: {
          width: 2,
          color: '#5292F7',
          style: 'solid',
          cornerVisible: function () {
            return that.settings.fillHandle && !that.isCellEdited() && !instance.selection.isMultiple()
          }
        }
      },
      area: {
        className: 'area',
        border: {
          width: 1,
          color: '#89AFF9',
          style: 'solid',
          cornerVisible: function () {
            return that.settings.fillHandle && !that.isCellEdited() && instance.selection.isMultiple()
          }
        }
      },
      highlight: {
        highlightRowClassName: that.settings.currentRowClassName,
        highlightColumnClassName: that.settings.currentColClassName
      },
      fill: {
        className: 'fill',
        border: {
          width: 1,
          color: 'red',
          style: 'solid'
        }
      }
    },
    hideBorderOnMouseDownOver: function () {
      return that.settings.fragmentSelection;
    },
    onCellMouseDown: function (event, coords, TD, wt) {
      instance.listen();
      that.activeWt = wt;

      isMouseDown = true;
      if (event.button === 2 && instance.selection.inInSelection(coords)) { //right mouse button
        //do nothing
      }
      else if (event.shiftKey) {
        if (coords.row >= 0 && coords.col >= 0) {
          instance.selection.setRangeEnd(coords);
        }
      }
      else {
        if (coords.row < 0 || coords.col < 0) {
          if (coords.row < 0) {
            instance.selectCell(0, coords.col, instance.countRows() - 1, coords.col)
          }
          if (coords.col < 0) {
            instance.selectCell(coords.row, 0, coords.row, instance.countCols())
          }
        }
        else {
          instance.selection.setRangeStart(coords);
        }
      }

      Handsontable.hooks.run(instance, 'afterOnCellMouseDown', event, coords, TD);

      that.activeWt = that.wt;
    },
    /*onCellMouseOut: function (/*event, coords, TD* /) {
     if (isMouseDown && that.settings.fragmentSelection === 'single') {
     clearTextSelection(); //otherwise text selection blinks during multiple cells selection
     }
     },*/
    onCellMouseOver: function (event, coords, TD, wt) {
      that.activeWt = wt;
      if (coords.row >= 0 && coords.col >= 0) { //is not a header
        if (isMouseDown) {
          /*if (that.settings.fragmentSelection === 'single') {
           clearTextSelection(); //otherwise text selection blinks during multiple cells selection
           }*/
          instance.selection.setRangeEnd(coords);
        }
        else if (instance.autofill.handle && instance.autofill.handle.isDragged) {
          instance.autofill.handle.isDragged++;
          instance.autofill.showBorder(coords);
        }
      }
      Handsontable.hooks.run(instance, 'afterOnCellMouseOver', event, coords, TD);
      that.activeWt = that.wt;
    },
    onCellCornerMouseDown: function (event) {
      instance.autofill.handle.isDragged = 1;
      event.preventDefault();
      Handsontable.hooks.run(instance, 'afterOnCellCornerMouseDown', event);
    },
    onCellCornerDblClick: function () {
      instance.autofill.selectAdjacent();
    },
    beforeDraw: function (force) {
      that.beforeRender(force);
    },
    onDraw: function(force){
      that.onDraw(force);
    },
    onScrollVertically: function () {
      instance.runHooks('afterScrollVertically');
    },
    onScrollHorizontally: function () {
      instance.runHooks('afterScrollHorizontally');
    }
  };

  Handsontable.hooks.run(instance, 'beforeInitWalkontable', walkontableConfig);

  this.wt = new Walkontable(walkontableConfig);
  this.activeWt = this.wt;

  /*$window.on('resize.' + instance.guid, function () {
    instance.registerTimeout('resizeTimeout', function () {
      instance.forceFullRender = true;
      that.render();
    }, 60);
  });*/

  $(that.wt.wtTable.spreader).on('mousedown.handsontable, contextmenu.handsontable', function (event) {
    if (event.target === that.wt.wtTable.spreader && event.which === 3) { //right mouse button exactly on spreader means right clickon the right hand side of vertical scrollbar
      event.stopPropagation();
    }
  });

  $documentElement.on('click.' + instance.guid, function () {
    if (that.settings.observeDOMVisibility) {
      if (that.wt.drawInterrupted) {
        that.instance.forceFullRender = true;
        that.render();
      }
    }
  });
};

Handsontable.TableView.prototype.isTextSelectionAllowed = function (el) {
  if ( Handsontable.helper.isInput(el) ) {
    return (true);
  }
  if (this.settings.fragmentSelection && this.wt.wtDom.isChildOf(el, this.TBODY)) {
    return (true);
  }
  return false;
};

Handsontable.TableView.prototype.isCellEdited = function () {
  var activeEditor = this.instance.getActiveEditor();
  return activeEditor && activeEditor.isOpened();
};

Handsontable.TableView.prototype.getWidth = function () {
  return this.wt.wtViewport.getWorkspaceActualWidth();
};

Handsontable.TableView.prototype.getHeight = function () {
  return this.wt.wtViewport.getWorkspaceActualHeight();
};

Handsontable.TableView.prototype.beforeRender = function (force) {
  if (force) { //force = did Walkontable decide to do full render
    Handsontable.hooks.run(this.instance, 'beforeRender', this.instance.forceFullRender); //this.instance.forceFullRender = did Handsontable request full render?
  }
};

Handsontable.TableView.prototype.onDraw = function(force){
  if (force) { //force = did Walkontable decide to do full render
    Handsontable.hooks.run(this.instance, 'afterRender', this.instance.forceFullRender); //this.instance.forceFullRender = did Handsontable request full render?
  }
};

Handsontable.TableView.prototype.render = function () {
  this.wt.draw(!this.instance.forceFullRender);
  this.instance.forceFullRender = false;
  this.instance.rootElement.triggerHandler('render.handsontable');
};

/**
 * Returns td object given coordinates
 * @param {WalkontableCellCoords} coords
 */
Handsontable.TableView.prototype.getCellAtCoords = function (coords) {
  var td = this.wt.wtTable.getCell(coords);
  if (td < 0) { //there was an exit code (cell is out of bounds)
    return null;
  }
  else {
    return td;
  }
};

/**
 * Scroll viewport to selection
 * @param {WalkontableCellCoords} coords
 */
Handsontable.TableView.prototype.scrollViewport = function (coords) {
  this.wt.scrollViewport(coords);
};

/**
 * Append row header to a TH element
 * @param row
 * @param TH
 */
Handsontable.TableView.prototype.appendRowHeader = function (row, TH) {
  if (row > -1) {
    this.wt.wtDom.fastInnerHTML(TH, this.instance.getRowHeader(row));
  }
  else {
    var DIV = document.createElement('DIV');
    DIV.className = 'relative';
    this.wt.wtDom.fastInnerText(DIV, '\u00A0');
    this.wt.wtDom.empty(TH);
    TH.appendChild(DIV);
  }
};

/**
 * Append column header to a TH element
 * @param col
 * @param TH
 */
Handsontable.TableView.prototype.appendColHeader = function (col, TH) {
  var DIV = document.createElement('DIV')
    , SPAN = document.createElement('SPAN');

  DIV.className = 'relative';
  SPAN.className = 'colHeader';

  this.wt.wtDom.fastInnerHTML(SPAN, this.instance.getColHeader(col));
  DIV.appendChild(SPAN);

  this.wt.wtDom.empty(TH);
  TH.appendChild(DIV);
  Handsontable.hooks.run(this.instance, 'afterGetColHeader', col, TH);
};

/**
 * Given a element's left position relative to the viewport, returns maximum element width until the right edge of the viewport (before scrollbar)
 * @param {Number} left
 * @return {Number}
 */
Handsontable.TableView.prototype.maximumVisibleElementWidth = function (leftOffset) {
  this.wt.wtScrollbars.horizontal.readWindowSize();
  var workspaceWidth = this.wt.wtViewport.getWorkspaceWidth();
  var maxWidth = workspaceWidth - leftOffset;
  return maxWidth > 0 ? maxWidth : 0;
};

/**
 * Given a element's top position relative to the viewport, returns maximum element height until the bottom edge of the viewport (before scrollbar)
 * @param {Number} topOffset
 * @return {Number}
 */
Handsontable.TableView.prototype.maximumVisibleElementHeight = function (topOffset) {
  this.wt.wtScrollbars.vertical.readWindowSize();
  var workspaceHeight = this.wt.wtViewport.getWorkspaceHeight();
  var maxHeight = workspaceHeight - topOffset;
  return maxHeight > 0 ? maxHeight : 0;
};

Handsontable.TableView.prototype.mainViewIsActive = function () {
  return this.wt === this.activeWt;
};
<commit_msg>bug fixing<commit_after>/**
 * Handsontable TableView constructor
 * @param {Object} instance
 */
Handsontable.TableView = function (instance) {
  var that = this
    , $window = $(window)
    , $documentElement = $(document.documentElement);

  this.instance = instance;
  this.settings = instance.getSettings();
  this.settingsFromDOM = instance.getSettingsFromDOM();

  instance.rootElement.data('originalStyle', instance.rootElement[0].getAttribute('style')); //needed to retrieve original style in jsFiddle link generator in HT examples. may be removed in future versions
  // in IE7 getAttribute('style') returns an object instead of a string, but we only support IE8+

  instance.rootElement.addClass('handsontable');

  var table = document.createElement('TABLE');
  table.className = 'htCore';
  this.THEAD = document.createElement('THEAD');
  table.appendChild(this.THEAD);
  this.TBODY = document.createElement('TBODY');
  table.appendChild(this.TBODY);

  instance.$table = $(table);
  instance.container.prepend(instance.$table);

  instance.rootElement.on('mousedown.handsontable', function (event) {
    if (!that.isTextSelectionAllowed(event.target)) {
      clearTextSelection();
      event.preventDefault();
      window.focus(); //make sure that window that contains HOT is active. Important when HOT is in iframe.
    }
  });

  $documentElement.on('keyup.' + instance.guid, function (event) {
    if (instance.selection.isInProgress() && !event.shiftKey) {
      instance.selection.finish();
    }
  });

  var isMouseDown;

  $documentElement.on('mouseup.' + instance.guid, function (event) {
    if (instance.selection.isInProgress() && event.which === 1) { //is left mouse button
      instance.selection.finish();
    }

    isMouseDown = false;

    if (instance.autofill.handle && instance.autofill.handle.isDragged) {
      if (instance.autofill.handle.isDragged > 1) {
        instance.autofill.apply();
      }
      instance.autofill.handle.isDragged = 0;
    }

    if (Handsontable.helper.isOutsideInput(document.activeElement)) {
      instance.unlisten();
    }
  });

  $documentElement.on('mousedown.' + instance.guid, function (event) {
    var next = event.target;

    if (next.shadowRoot && Handsontable.Dom.isChildOf(instance.rootElement[0], next.shadowRoot)) {
      return; //click inside Web Component
    }

    if (next !== that.wt.wtTable.spreader) { //immediate click on "spreader" means click on the right side of vertical scrollbar
      while (next !== document.documentElement) {
        if (next === null) {
          return; //click on something that was a row but now is detached (possibly because your click triggered a rerender)
        }
        if (next === instance.rootElement[0]) {
          return; //click inside container
        }
        next = next.parentNode;
      }
    }

    //function did not return until here, we have an outside click!

    if (that.settings.outsideClickDeselects) {
      instance.deselectCell();
    }
    else {
      instance.destroyEditor();
    }
  });

  instance.$table.on('selectstart', function (event) {
    if (that.settings.fragmentSelection) {
      return;
    }

    //https://github.com/warpech/jquery-handsontable/issues/160
    //selectstart is IE only event. Prevent text from being selected when performing drag down in IE8
    event.preventDefault();
  });

  var clearTextSelection = function () {
    //http://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript
    if (window.getSelection) {
      if (window.getSelection().empty) {  // Chrome
        window.getSelection().empty();
      } else if (window.getSelection().removeAllRanges) {  // Firefox
        window.getSelection().removeAllRanges();
      }
    } else if (document.selection) {  // IE?
      document.selection.empty();
    }
  };

  var walkontableConfig = {
    debug: function () {
      return that.settings.debug;
    },
    table: table,
    stretchH: this.settings.stretchH,
    data: instance.getDataAtCell,
    totalRows: instance.countRows,
    totalColumns: instance.countCols,
    offsetRow: 0,
    offsetColumn: 0,
    fixedColumnsLeft: function () {
      return that.settings.fixedColumnsLeft;
    },
    fixedRowsTop: function () {
      return that.settings.fixedRowsTop;
    },
    rowHeaders: function () {
      return instance.hasRowHeaders() ? [function (index, TH) {
        that.appendRowHeader(index, TH);
      }] : []
    },
    columnHeaders: function () {
      return instance.hasColHeaders() ? [function (index, TH) {
        that.appendColHeader(index, TH);
      }] : []
    },
    columnWidth: instance.getColWidth,
    rowHeight: instance.getRowHeight,
    cellRenderer: function (row, col, TD) {

      var prop = that.instance.colToProp(col)
        , cellProperties = that.instance.getCellMeta(row, col)
        , renderer = that.instance.getCellRenderer(cellProperties);

      var value = that.instance.getDataAtRowProp(row, prop);

      renderer(that.instance, TD, row, col, prop, value, cellProperties);

      Handsontable.hooks.run(that.instance, 'afterRenderer', TD, row, col, prop, value, cellProperties);

    },
    selections: {
      current: {
        className: 'current',
        border: {
          width: 2,
          color: '#5292F7',
          style: 'solid',
          cornerVisible: function () {
            return that.settings.fillHandle && !that.isCellEdited() && !instance.selection.isMultiple()
          }
        }
      },
      area: {
        className: 'area',
        border: {
          width: 1,
          color: '#89AFF9',
          style: 'solid',
          cornerVisible: function () {
            return that.settings.fillHandle && !that.isCellEdited() && instance.selection.isMultiple()
          }
        }
      },
      highlight: {
        highlightRowClassName: that.settings.currentRowClassName,
        highlightColumnClassName: that.settings.currentColClassName
      },
      fill: {
        className: 'fill',
        border: {
          width: 1,
          color: 'red',
          style: 'solid'
        }
      }
    },
    hideBorderOnMouseDownOver: function () {
      return that.settings.fragmentSelection;
    },
    onCellMouseDown: function (event, coords, TD, wt) {
      instance.listen();
      that.activeWt = wt;

      isMouseDown = true;
      if (event.button === 2 && instance.selection.inInSelection(coords)) { //right mouse button
        //do nothing
      }
      else if (event.shiftKey) {
        if (coords.row >= 0 && coords.col >= 0) {
          instance.selection.setRangeEnd(coords);
        }
      }
      else {
        if (coords.row < 0 || coords.col < 0) {
          if (coords.row < 0) {
            instance.selectCell(0, coords.col, instance.countRows() - 1, coords.col);
          }
          if (coords.col < 0) {
            instance.selectCell(coords.row, 0, coords.row, instance.countCols() - 1);
          }
        }
        else {
          instance.selection.setRangeStart(coords);
        }
      }

      Handsontable.hooks.run(instance, 'afterOnCellMouseDown', event, coords, TD);

      that.activeWt = that.wt;
    },
    /*onCellMouseOut: function (/*event, coords, TD* /) {
     if (isMouseDown && that.settings.fragmentSelection === 'single') {
     clearTextSelection(); //otherwise text selection blinks during multiple cells selection
     }
     },*/
    onCellMouseOver: function (event, coords, TD, wt) {
      that.activeWt = wt;
      if (coords.row >= 0 && coords.col >= 0) { //is not a header
        if (isMouseDown) {
          /*if (that.settings.fragmentSelection === 'single') {
           clearTextSelection(); //otherwise text selection blinks during multiple cells selection
           }*/
          instance.selection.setRangeEnd(coords);
        }
        else if (instance.autofill.handle && instance.autofill.handle.isDragged) {
          instance.autofill.handle.isDragged++;
          instance.autofill.showBorder(coords);
        }
      }
      Handsontable.hooks.run(instance, 'afterOnCellMouseOver', event, coords, TD);
      that.activeWt = that.wt;
    },
    onCellCornerMouseDown: function (event) {
      instance.autofill.handle.isDragged = 1;
      event.preventDefault();
      Handsontable.hooks.run(instance, 'afterOnCellCornerMouseDown', event);
    },
    onCellCornerDblClick: function () {
      instance.autofill.selectAdjacent();
    },
    beforeDraw: function (force) {
      that.beforeRender(force);
    },
    onDraw: function(force){
      that.onDraw(force);
    },
    onScrollVertically: function () {
      instance.runHooks('afterScrollVertically');
    },
    onScrollHorizontally: function () {
      instance.runHooks('afterScrollHorizontally');
    }
  };

  Handsontable.hooks.run(instance, 'beforeInitWalkontable', walkontableConfig);

  this.wt = new Walkontable(walkontableConfig);
  this.activeWt = this.wt;

  /*$window.on('resize.' + instance.guid, function () {
    instance.registerTimeout('resizeTimeout', function () {
      instance.forceFullRender = true;
      that.render();
    }, 60);
  });*/

  $(that.wt.wtTable.spreader).on('mousedown.handsontable, contextmenu.handsontable', function (event) {
    if (event.target === that.wt.wtTable.spreader && event.which === 3) { //right mouse button exactly on spreader means right clickon the right hand side of vertical scrollbar
      event.stopPropagation();
    }
  });

  $documentElement.on('click.' + instance.guid, function () {
    if (that.settings.observeDOMVisibility) {
      if (that.wt.drawInterrupted) {
        that.instance.forceFullRender = true;
        that.render();
      }
    }
  });
};

Handsontable.TableView.prototype.isTextSelectionAllowed = function (el) {
  if ( Handsontable.helper.isInput(el) ) {
    return (true);
  }
  if (this.settings.fragmentSelection && this.wt.wtDom.isChildOf(el, this.TBODY)) {
    return (true);
  }
  return false;
};

Handsontable.TableView.prototype.isCellEdited = function () {
  var activeEditor = this.instance.getActiveEditor();
  return activeEditor && activeEditor.isOpened();
};

Handsontable.TableView.prototype.getWidth = function () {
  return this.wt.wtViewport.getWorkspaceActualWidth();
};

Handsontable.TableView.prototype.getHeight = function () {
  return this.wt.wtViewport.getWorkspaceActualHeight();
};

Handsontable.TableView.prototype.beforeRender = function (force) {
  if (force) { //force = did Walkontable decide to do full render
    Handsontable.hooks.run(this.instance, 'beforeRender', this.instance.forceFullRender); //this.instance.forceFullRender = did Handsontable request full render?
  }
};

Handsontable.TableView.prototype.onDraw = function(force){
  if (force) { //force = did Walkontable decide to do full render
    Handsontable.hooks.run(this.instance, 'afterRender', this.instance.forceFullRender); //this.instance.forceFullRender = did Handsontable request full render?
  }
};

Handsontable.TableView.prototype.render = function () {
  this.wt.draw(!this.instance.forceFullRender);
  this.instance.forceFullRender = false;
  this.instance.rootElement.triggerHandler('render.handsontable');
};

/**
 * Returns td object given coordinates
 * @param {WalkontableCellCoords} coords
 */
Handsontable.TableView.prototype.getCellAtCoords = function (coords) {
  var td = this.wt.wtTable.getCell(coords);
  if (td < 0) { //there was an exit code (cell is out of bounds)
    return null;
  }
  else {
    return td;
  }
};

/**
 * Scroll viewport to selection
 * @param {WalkontableCellCoords} coords
 */
Handsontable.TableView.prototype.scrollViewport = function (coords) {
  this.wt.scrollViewport(coords);
};

/**
 * Append row header to a TH element
 * @param row
 * @param TH
 */
Handsontable.TableView.prototype.appendRowHeader = function (row, TH) {
  if (row > -1) {
    this.wt.wtDom.fastInnerHTML(TH, this.instance.getRowHeader(row));
  }
  else {
    var DIV = document.createElement('DIV');
    DIV.className = 'relative';
    this.wt.wtDom.fastInnerText(DIV, '\u00A0');
    this.wt.wtDom.empty(TH);
    TH.appendChild(DIV);
  }
};

/**
 * Append column header to a TH element
 * @param col
 * @param TH
 */
Handsontable.TableView.prototype.appendColHeader = function (col, TH) {
  var DIV = document.createElement('DIV')
    , SPAN = document.createElement('SPAN');

  DIV.className = 'relative';
  SPAN.className = 'colHeader';

  this.wt.wtDom.fastInnerHTML(SPAN, this.instance.getColHeader(col));
  DIV.appendChild(SPAN);

  this.wt.wtDom.empty(TH);
  TH.appendChild(DIV);
  Handsontable.hooks.run(this.instance, 'afterGetColHeader', col, TH);
};

/**
 * Given a element's left position relative to the viewport, returns maximum element width until the right edge of the viewport (before scrollbar)
 * @param {Number} left
 * @return {Number}
 */
Handsontable.TableView.prototype.maximumVisibleElementWidth = function (leftOffset) {
  this.wt.wtScrollbars.horizontal.readWindowSize();
  var workspaceWidth = this.wt.wtViewport.getWorkspaceWidth();
  var maxWidth = workspaceWidth - leftOffset;
  return maxWidth > 0 ? maxWidth : 0;
};

/**
 * Given a element's top position relative to the viewport, returns maximum element height until the bottom edge of the viewport (before scrollbar)
 * @param {Number} topOffset
 * @return {Number}
 */
Handsontable.TableView.prototype.maximumVisibleElementHeight = function (topOffset) {
  this.wt.wtScrollbars.vertical.readWindowSize();
  var workspaceHeight = this.wt.wtViewport.getWorkspaceHeight();
  var maxHeight = workspaceHeight - topOffset;
  return maxHeight > 0 ? maxHeight : 0;
};

Handsontable.TableView.prototype.mainViewIsActive = function () {
  return this.wt === this.activeWt;
};
<|endoftext|>