<commit_before>(function ($) {
    "use strict";

    function Djupload(el, options){
        this.element = $(el);
        this.options = options;
        this.init();
    }

    /* UI element to upload files directly to S3.

     */
    Djupload.prototype = {

        _csrfToken: function() {
            var self = this;
            if( self.options.csrfToken ) { return self.options.csrfToken; }
            return getMetaCSRFToken();
        },

        _uploadSuccess: function(file, resp) {
            var self = this;
            self.element.trigger("djupload.success", resp.location);
            if( self.options.uploadSuccess ) {
                self.options.uploadSuccess(file, resp);
            } else {
                if( resp.details ) {
                    showMessages(resp.details, "success");
                } else {
                    showMessages([interpolate(gettext(
                        '"%s" uploaded sucessfully to "%s"'),
                        [file.name, response.location])], "success");
                }
            }
            return true;
        },

        _uploadError: function(file, resp) {
            var self = this;
            self.element.trigger("djupload.error", [file.name, resp]);
            if( self.options.uploadError ) {
                self.options.uploadError(file, resp);
            } else {
                if( typeof resp === "string" ) {
                    showErrorMessages(
                        interpolate(gettext("Error %s: %s"),
                            [resp.status, resp + " " + interpolate(gettext(
                                "(while uploading '%s')"), [file.name])]));
                } else {
                    showErrorMessages(resp);
                }
            }
        },

        _uploadProgress: function(file, progress) {
            var self = this;
            self.element.trigger("djupload.progress", [file.name, progress]);
            if( self.options.uploadProgress ) {
                self.options.uploadProgress(file, progress);
            }
            return true;
        },

        init: function(){
            var self = this;
            if( self.options.mediaPrefix !== ""
                && !self.options.mediaPrefix.match(/\/$/)){
                self.options.mediaPrefix += "/";
            }
            if( self.options.uploadUrl.indexOf("/api/auth/") >= 0 ) {
                $.ajax({
                    method: "GET",
                    url: self.options.uploadUrl,
                    datatype: "json",
                    contentType: "application/json; charset=utf-8",
                    success: function(data) {

                        var parser = document.createElement('a');
                        parser.href = data.location;
                        self.options.uploadUrl = parser.host + "/";
                        if( parser.protocol ) {
                            self.options.uploadUrl = parser.protocol + "//"
                                + self.options.uploadUrl;
                        }
                        self.options.mediaPrefix = parser.pathname;
                        if( self.options.mediaPrefix === 'undefined'
                            || self.options.mediaPrefix === null ) {
                            self.options.mediaPrefix = "";
                        }
                        if( self.options.mediaPrefix !== ""
                            && self.options.mediaPrefix.match(/^\//)){
                            self.options.mediaPrefix = self.options.mediaPrefix.substring(1);
                        }
                        if( self.options.mediaPrefix !== ""
                            && !self.options.mediaPrefix.match(/\/$/)){
                            self.options.mediaPrefix += "/";
                        }

                        self.options.accessKey = data.access_key;
                        self.options.policy = data.policy;
                        self.options.amzCredential = data.x_amz_credential;
                        self.options.amzDate = data.x_amz_date;
                        self.options.amzServerSideEncryption = data.x_amz_server_side_encryption;
                        self.options.securityToken = data.security_token;
                        self.options.signature = data.signature;
                        self.initDropzone();
                    },
                    error: function(resp) {
                        showErrorMessages(resp);
                    }
                });
            } else {
                self.initDropzone();
            }
        },

        initDropzone: function() {
            var self = this;
            var dropzoneUrl = (self.options.accessKey ? self.options.uploadUrl
                : (self.element.attr("data-complete-url") ?
                    self.element.attr("data-complete-url")
                    : self.options.uploadUrl));
            if( !dropzoneUrl ) {
                showErrorMessages(
                    gettext("instantiated djupload() with no uploadUrl specified."));
                throw new Error(
                    gettext("instantiated djupload() with no uploadUrl specified."));
            }
            self.element.dropzone({
                paramName: self.options.uploadParamName,
                url: dropzoneUrl,
                maxFilesize: self.options.uploadMaxFileSize,
                clickable: self.options.uploadClickableZone,
                createImageThumbnails: false,
                previewTemplate: "

",
                init: function() {
                    if( self.options.accessKey ) {
                        // We are going to remove extra input files that AWS
                        // would reject (ex: csrftoken).
                        var fields = this.element.querySelectorAll(
                            "input, textarea, select, button");
                        for( var idx = 0; idx < fields.length; ++idx ) {
                            if( fields[idx].getAttribute("name")
                                && (fields[idx].getAttribute("name")
                                    !== self.options.uploadParamName) ) {
                                fields[idx].parentNode.removeChild(fields[idx]);
                            }
                        }
                    }

                    this.on("sending", function(file, xhr, formData){
                        if( self.options.accessKey ) {
                            formData.append(
                                "key", self.options.mediaPrefix + file.name);
                            formData.append("policy", self.options.policy);
                            formData.append(
                                "x-amz-algorithm", "AWS4-HMAC-SHA256");
                            formData.append(
                                "x-amz-credential", self.options.amzCredential);
                            formData.append("x-amz-date", self.options.amzDate);
                            formData.append("x-amz-security-token",
                                self.options.securityToken);
                            formData.append(
                                "x-amz-signature", self.options.signature);
                            if( self.options.acl ) {
                                formData.append("acl", self.options.acl);
                            } else {
                                formData.append("acl", "private");
                            }
                            if( self.options.amzServerSideEncryption ) {
                                formData.append("x-amz-server-side-encryption",
                                    self.options.amzServerSideEncryption);
                            } else if( !self.options.acl
                                || self.options.acl !== "public-read" ) {
                                formData.append("x-amz-server-side-encryption",
                                    "AES256");
                            }
                            var ext = file.name.slice(
                                file.name.lastIndexOf('.')).toLowerCase();
                            if( ext === ".jpg" ) {
                                formData.append("Content-Type", "image/jpeg");
                            } else if( ext === ".png" ) {
                                formData.append("Content-Type", "image/png");
                            } else if( ext === ".gif" ) {
                                formData.append("Content-Type", "image/gif");
                            } else if( ext === ".mp4" ) {
                                formData.append("Content-Type", "video/mp4");
                            } else {
                                formData.append(
                                    "Content-Type", "binary/octet-stream");
                            }
                        } else {
                            formData.append(
                                "csrfmiddlewaretoken", getMetaCSRFToken());
                            var data = self.element.data();
                            for( var key in data ) {
                                if( data.hasOwnProperty(key)
                                    && key != 'djupload' ) {
                                    formData.append(key, data[key]);
                                }
                            }
                        }
                    });

                    this.on("success", function(file, response){
                        if( self.options.accessKey) {
                            // With a direct upload to S3, we need to build
                            // a custom response with location url ourselves.
                            response = {
                                location: file.xhr.responseURL + self.options.mediaPrefix + file.name
                            };
                            // We will also call back a completion url
                            // on the server.
                            var completeUrl = self.element.attr(
                                "data-complete-url");
                            if( completeUrl ) {
                                var data = {};
                                [].forEach.call(self.element[0].attributes, function(attr) {
                                    if (/^data-/.test(attr.name)) {
                                        var camelCaseName = attr.name.substr(5).replace(/-(.)/g, function ($0, $1) {
                                            return $1.toUpperCase();
                                        });
                                        data[camelCaseName] = attr.value;
                                    }
                                });
                                for( var key in data ) {
                                    if( data.hasOwnProperty(key)
                                        && key != 'djupload' ) {
                                        response[key] = data[key];
                                    }
                                }
                                $.ajax({
                                    type: "POST",
                                    url: completeUrl,
                                    beforeSend: function(xhr) {
                                        xhr.setRequestHeader(
                                            "X-CSRFToken", getMetaCSRFToken());
                                    },
                                    data: JSON.stringify(response),
                                    datatype: "json",
                                    contentType: "application/json; charset=utf-8",
                                    success: function(resp) {
                                        self._uploadSuccess(file, resp);
                                    },
                                    error: function(resp) {
                                        self._uploadError(file, resp);
                                    }
                                });
                            } else {
                                self._uploadSuccess(file, response);
                            }
                        } else {
                            self._uploadSuccess(file, response);
                        }
                    });

                    this.on("error", function(file, message){
                        self._uploadError(file, message);
                    });

                    this.on("uploadprogress", function(file, progress){
                        self._uploadProgress(file, progress);
                    });
                }
            });
        }
    };

    $.fn.djupload = function(options) {
        var opts = $.extend( {}, $.fn.djupload.defaults, options );
        return this.each(function() {
            if (!$.data(this, "djupload")) {
                $.data(this, "djupload", new Djupload(this, opts));
            }
        });
    };

    $.fn.djupload.defaults = {
        // location
        uploadUrl: null,
        mediaPrefix: "",

        uploadZone: "body",
        uploadClickableZone: true,
        uploadParamName: "file",
        uploadMaxFileSize: 250,

        // Django upload
        csrfToken: null,

        // S3 direct upload
        accessKey: null,
        securityToken: null,
        acl: null, // defaults to "private".
        policy: "",
        signature: null,
        amzCredential: null,
        amzDate: null,
        amzServerSideEncryption: null,

        // callback
        uploadSuccess: null,
        uploadError: null,
        uploadProgress: null
    };

    Dropzone.autoDiscover = false;

})(jQuery);

<commit_msg>reverts because private files should not display location<commit_after>(function ($) {
    "use strict";

    function Djupload(el, options){
        this.element = $(el);
        this.options = options;
        this.init();
    }

    /* UI element to upload files directly to S3.

     */
    Djupload.prototype = {

        _csrfToken: function() {
            var self = this;
            if( self.options.csrfToken ) { return self.options.csrfToken; }
            return getMetaCSRFToken();
        },

        _uploadSuccess: function(file, resp) {
            var self = this;
            self.element.trigger("djupload.success", resp.location);
            if( self.options.uploadSuccess ) {
                self.options.uploadSuccess(file, resp);
            } else {
                if( resp.details ) {
                    showMessages(resp.details, "success");
                } else {
                    showMessages([interpolate(gettext(
                        '"%s" uploaded sucessfully'),
                        [file.name])], "success");
                }
            }
            return true;
        },

        _uploadError: function(file, resp) {
            var self = this;
            self.element.trigger("djupload.error", [file.name, resp]);
            if( self.options.uploadError ) {
                self.options.uploadError(file, resp);
            } else {
                if( typeof resp === "string" ) {
                    showErrorMessages(
                        interpolate(gettext("Error %s: %s"),
                            [resp.status, resp + " " + interpolate(gettext(
                                "(while uploading '%s')"), [file.name])]));
                } else {
                    showErrorMessages(resp);
                }
            }
        },

        _uploadProgress: function(file, progress) {
            var self = this;
            self.element.trigger("djupload.progress", [file.name, progress]);
            if( self.options.uploadProgress ) {
                self.options.uploadProgress(file, progress);
            }
            return true;
        },

        init: function(){
            var self = this;
            if( self.options.mediaPrefix !== ""
                && !self.options.mediaPrefix.match(/\/$/)){
                self.options.mediaPrefix += "/";
            }
            if( self.options.uploadUrl.indexOf("/api/auth/") >= 0 ) {
                $.ajax({
                    method: "GET",
                    url: self.options.uploadUrl,
                    datatype: "json",
                    contentType: "application/json; charset=utf-8",
                    success: function(data) {

                        var parser = document.createElement('a');
                        parser.href = data.location;
                        self.options.uploadUrl = parser.host + "/";
                        if( parser.protocol ) {
                            self.options.uploadUrl = parser.protocol + "//"
                                + self.options.uploadUrl;
                        }
                        self.options.mediaPrefix = parser.pathname;
                        if( self.options.mediaPrefix === 'undefined'
                            || self.options.mediaPrefix === null ) {
                            self.options.mediaPrefix = "";
                        }
                        if( self.options.mediaPrefix !== ""
                            && self.options.mediaPrefix.match(/^\//)){
                            self.options.mediaPrefix = self.options.mediaPrefix.substring(1);
                        }
                        if( self.options.mediaPrefix !== ""
                            && !self.options.mediaPrefix.match(/\/$/)){
                            self.options.mediaPrefix += "/";
                        }

                        self.options.accessKey = data.access_key;
                        self.options.policy = data.policy;
                        self.options.amzCredential = data.x_amz_credential;
                        self.options.amzDate = data.x_amz_date;
                        self.options.amzServerSideEncryption = data.x_amz_server_side_encryption;
                        self.options.securityToken = data.security_token;
                        self.options.signature = data.signature;
                        self.initDropzone();
                    },
                    error: function(resp) {
                        showErrorMessages(resp);
                    }
                });
            } else {
                self.initDropzone();
            }
        },

        initDropzone: function() {
            var self = this;
            var dropzoneUrl = (self.options.accessKey ? self.options.uploadUrl
                : (self.element.attr("data-complete-url") ?
                    self.element.attr("data-complete-url")
                    : self.options.uploadUrl));
            if( !dropzoneUrl ) {
                showErrorMessages(
                    gettext("instantiated djupload() with no uploadUrl specified."));
                throw new Error(
                    gettext("instantiated djupload() with no uploadUrl specified."));
            }
            self.element.dropzone({
                paramName: self.options.uploadParamName,
                url: dropzoneUrl,
                maxFilesize: self.options.uploadMaxFileSize,
                clickable: self.options.uploadClickableZone,
                createImageThumbnails: false,
                previewTemplate: "

",
                init: function() {
                    if( self.options.accessKey ) {
                        // We are going to remove extra input files that AWS
                        // would reject (ex: csrftoken).
                        var fields = this.element.querySelectorAll(
                            "input, textarea, select, button");
                        for( var idx = 0; idx < fields.length; ++idx ) {
                            if( fields[idx].getAttribute("name")
                                && (fields[idx].getAttribute("name")
                                    !== self.options.uploadParamName) ) {
                                fields[idx].parentNode.removeChild(fields[idx]);
                            }
                        }
                    }

                    this.on("sending", function(file, xhr, formData){
                        if( self.options.accessKey ) {
                            formData.append(
                                "key", self.options.mediaPrefix + file.name);
                            formData.append("policy", self.options.policy);
                            formData.append(
                                "x-amz-algorithm", "AWS4-HMAC-SHA256");
                            formData.append(
                                "x-amz-credential", self.options.amzCredential);
                            formData.append("x-amz-date", self.options.amzDate);
                            formData.append("x-amz-security-token",
                                self.options.securityToken);
                            formData.append(
                                "x-amz-signature", self.options.signature);
                            if( self.options.acl ) {
                                formData.append("acl", self.options.acl);
                            } else {
                                formData.append("acl", "private");
                            }
                            if( self.options.amzServerSideEncryption ) {
                                formData.append("x-amz-server-side-encryption",
                                    self.options.amzServerSideEncryption);
                            } else if( !self.options.acl
                                || self.options.acl !== "public-read" ) {
                                formData.append("x-amz-server-side-encryption",
                                    "AES256");
                            }
                            var ext = file.name.slice(
                                file.name.lastIndexOf('.')).toLowerCase();
                            if( ext === ".jpg" ) {
                                formData.append("Content-Type", "image/jpeg");
                            } else if( ext === ".png" ) {
                                formData.append("Content-Type", "image/png");
                            } else if( ext === ".gif" ) {
                                formData.append("Content-Type", "image/gif");
                            } else if( ext === ".mp4" ) {
                                formData.append("Content-Type", "video/mp4");
                            } else {
                                formData.append(
                                    "Content-Type", "binary/octet-stream");
                            }
                        } else {
                            formData.append(
                                "csrfmiddlewaretoken", getMetaCSRFToken());
                            var data = self.element.data();
                            for( var key in data ) {
                                if( data.hasOwnProperty(key)
                                    && key != 'djupload' ) {
                                    formData.append(key, data[key]);
                                }
                            }
                        }
                    });

                    this.on("success", function(file, response){
                        if( self.options.accessKey) {
                            // With a direct upload to S3, we need to build
                            // a custom response with location url ourselves.
                            response = {
                                location: file.xhr.responseURL + self.options.mediaPrefix + file.name
                            };
                            // We will also call back a completion url
                            // on the server.
                            var completeUrl = self.element.attr(
                                "data-complete-url");
                            if( completeUrl ) {
                                var data = {};
                                [].forEach.call(self.element[0].attributes, function(attr) {
                                    if (/^data-/.test(attr.name)) {
                                        var camelCaseName = attr.name.substr(5).replace(/-(.)/g, function ($0, $1) {
                                            return $1.toUpperCase();
                                        });
                                        data[camelCaseName] = attr.value;
                                    }
                                });
                                for( var key in data ) {
                                    if( data.hasOwnProperty(key)
                                        && key != 'djupload' ) {
                                        response[key] = data[key];
                                    }
                                }
                                $.ajax({
                                    type: "POST",
                                    url: completeUrl,
                                    beforeSend: function(xhr) {
                                        xhr.setRequestHeader(
                                            "X-CSRFToken", getMetaCSRFToken());
                                    },
                                    data: JSON.stringify(response),
                                    datatype: "json",
                                    contentType: "application/json; charset=utf-8",
                                    success: function(resp) {
                                        self._uploadSuccess(file, resp);
                                    },
                                    error: function(resp) {
                                        self._uploadError(file, resp);
                                    }
                                });
                            } else {
                                self._uploadSuccess(file, response);
                            }
                        } else {
                            self._uploadSuccess(file, response);
                        }
                    });

                    this.on("error", function(file, message){
                        self._uploadError(file, message);
                    });

                    this.on("uploadprogress", function(file, progress){
                        self._uploadProgress(file, progress);
                    });
                }
            });
        }
    };

    $.fn.djupload = function(options) {
        var opts = $.extend( {}, $.fn.djupload.defaults, options );
        return this.each(function() {
            if (!$.data(this, "djupload")) {
                $.data(this, "djupload", new Djupload(this, opts));
            }
        });
    };

    $.fn.djupload.defaults = {
        // location
        uploadUrl: null,
        mediaPrefix: "",

        uploadZone: "body",
        uploadClickableZone: true,
        uploadParamName: "file",
        uploadMaxFileSize: 250,

        // Django upload
        csrfToken: null,

        // S3 direct upload
        accessKey: null,
        securityToken: null,
        acl: null, // defaults to "private".
        policy: "",
        signature: null,
        amzCredential: null,
        amzDate: null,
        amzServerSideEncryption: null,

        // callback
        uploadSuccess: null,
        uploadError: null,
        uploadProgress: null
    };

    Dropzone.autoDiscover = false;

})(jQuery);

<|endoftext|>


<commit_before>//# sourceURL=conceptBox.js

'use strict';

window.smartRApp.directive('conceptBox', [
    '$rootScope',
    '$http',
    function($rootScope, $http) {
        return {
            restrict: 'E',
            scope: {
                conceptGroup: '=',
                label: '@',
                tooltip: '@',
                min: '@',
                max: '@',
                type: '@'
            },
            templateUrl: $rootScope.smartRPath +  '/js/smartR/_angular/templates/conceptBox.html',
            link: function(scope, element) {
                var max = parseInt(scope.max);
                var min = parseInt(scope.min);

                var template_box = element[0].querySelector('.sr-drop-input'),
                    template_btn = element[0].querySelector('.sr-drop-btn'),
                    template_tooltip = element[0].querySelector('.sr-tooltip-dialog');

                // instantiate tooltips
                $(template_tooltip).tooltip({track: true, tooltipClass:"sr-ui-tooltip"});

                var _clearWindow = function() {
                    $(template_box).children().remove();
                };

                var _getConcepts = function() {
                    return $(template_box).children().toArray().map(function(childNode) {
                        return childNode.getAttribute('conceptid');
                    });
                };

                var _activateDragAndDrop = function() {
                    var extObj = Ext.get(template_box);
                    var dtgI = new Ext.dd.DropTarget(extObj, {ddGroup: 'makeQuery'});
                    dtgI.notifyDrop = dropOntoCategorySelection; // jshint ignore:line
                };

                var typeMap = {
                    hleaficon: 'HD',
                    alphaicon: 'LD-categorical',
                    null: 'LD-categorical', // a fix for older tm version without alphaicon
                    valueicon: 'LD-numerical'
                };
                var _containsOnlyCorrectType = function() {
                    return $(template_box).children().toArray().every(function(childNode) {
                        return typeMap[childNode.getAttribute('setnodetype')] === scope.type;
                    });
                };

                var _getNodeDetails = function(conceptKeys, callback) {
                    var request = $http({
                        url: pageInfo.basePath + '/SmartR/nodeDetails',
                        method: 'POST',
                        config: {
                            timeout: 10000
                        },
                        data: {
                            conceptKeys: conceptKeys
                        }
                    });

                    request.then(
                        callback,
                        function() {
                            alert('Could not fetch node details. Network connection lost?');
                        });
                };

                // activate drag & drop for our conceptBox and color it once it is rendered
                scope.$evalAsync(function() {
                    _activateDragAndDrop();
                });

                // bind the button to its clearing functionality
                template_btn.addEventListener('click', function() {
                    _clearWindow();
                });

                // this watches the childNodes of the conceptBox and updates the model on change
                new MutationObserver(function() {
                    scope.conceptGroup.concepts = _getConcepts(); // update the model
                    scope.validate();
                    scope.$apply();
                }).observe(template_box, { childList: true });

                scope.validate = function() {
                    scope.instructionMinNodes = scope.conceptGroup.concepts.length < min;
                    scope.instructionMaxNodes = max !== -1 && scope.conceptGroup.concepts.length > max;
                    scope.instructionNodeType = !_containsOnlyCorrectType();
                    if (scope.type === 'HD' && scope.conceptGroup.concepts.length > 1) {
                        _getNodeDetails(scope.conceptGroup.concepts, function(response) {
                            if (Object.keys(response.data).length < 2) {
                                var platforms = response.data[Object.keys(response.data)[0]].platforms;
                                scope.instructionNodePlatform = !platforms.every(function(el) {
                                    return el.title === platforms[0].title;
                                });
                            } else {
                                scope.instructionNodePlatform = true;
                            }
                        });
                    } else {
                        scope.instructionNodePlatform = false;
                    }
                };

                scope.$watchGroup([
                    'instructionNodeType',
                    'instructionNodePlatform',
                    'instructionMaxNodes',
                    'instructionMinNodes'],
                    function(newValue) {
                        var instructionNodeType = newValue[0],
                            instructionNodePlatform = newValue[1],
                            instructionMaxNodes = newValue[2],
                            instructionMinNodes = newValue[3];
                        scope.conceptGroup.valid = !(instructionNodeType ||
                                                     instructionNodePlatform ||
                                                     instructionMaxNodes ||
                                                     instructionMinNodes);
                    });

                scope.validate();
            }
        };
    }]);
<commit_msg>SH-226 (LineGraph: During Fetch Data, high dim box becomes red with message “Nodes must have the same platform”)<commit_after>//# sourceURL=conceptBox.js

'use strict';

window.smartRApp.directive('conceptBox', [
    '$rootScope',
    '$http',
    function($rootScope, $http) {
        return {
            restrict: 'E',
            scope: {
                conceptGroup: '=',
                label: '@',
                tooltip: '@',
                min: '@',
                max: '@',
                type: '@'
            },
            templateUrl: $rootScope.smartRPath +  '/js/smartR/_angular/templates/conceptBox.html',
            link: function(scope, element) {
                var max = parseInt(scope.max);
                var min = parseInt(scope.min);

                var template_box = element[0].querySelector('.sr-drop-input'),
                    template_btn = element[0].querySelector('.sr-drop-btn'),
                    template_tooltip = element[0].querySelector('.sr-tooltip-dialog');

                // instantiate tooltips
                $(template_tooltip).tooltip({track: true, tooltipClass:"sr-ui-tooltip"});

                var _clearWindow = function() {
                    $(template_box).children().remove();
                };

                var _getConcepts = function() {
                    return $(template_box).children().toArray().map(function(childNode) {
                        return childNode.getAttribute('conceptid');
                    });
                };

                var _activateDragAndDrop = function() {
                    var extObj = Ext.get(template_box);
                    var dtgI = new Ext.dd.DropTarget(extObj, {ddGroup: 'makeQuery'});
                    dtgI.notifyDrop = dropOntoCategorySelection; // jshint ignore:line
                };

                var typeMap = {
                    hleaficon: 'HD',
                    alphaicon: 'LD-categorical',
                    null: 'LD-categorical', // a fix for older tm version without alphaicon
                    valueicon: 'LD-numerical'
                };
                var _containsOnlyCorrectType = function() {
                    return $(template_box).children().toArray().every(function(childNode) {
                        return typeMap[childNode.getAttribute('setnodetype')] === scope.type;
                    });
                };

                var _getNodeDetails = function(conceptKeys, callback) {
                    var request = $http({
                        url: pageInfo.basePath + '/SmartR/nodeDetails',
                        method: 'POST',
                        config: {
                            timeout: 10000
                        },
                        data: {
                            conceptKeys: conceptKeys
                        }
                    });

                    request.then(
                        callback,
                        function() {
                            alert('Could not fetch node details. Network connection lost?');
                        });
                };

                // activate drag & drop for our conceptBox and color it once it is rendered
                scope.$evalAsync(function() {
                    _activateDragAndDrop();
                });

                // bind the button to its clearing functionality
                template_btn.addEventListener('click', function() {
                    _clearWindow();
                });

                // this watches the childNodes of the conceptBox and updates the model on change
                new MutationObserver(function() {
                    scope.conceptGroup.concepts = _getConcepts(); // update the model
                    scope.validate();
                    scope.$apply();
                }).observe(template_box, { childList: true });

                scope.validate = function() {
                    scope.instructionMinNodes = scope.conceptGroup.concepts.length < min;
                    scope.instructionMaxNodes = max !== -1 && scope.conceptGroup.concepts.length > max;
                    scope.instructionNodeType = !_containsOnlyCorrectType();
                    // FIXME: Disabled for now because this causes problems with certain datasets for unknown reasons
                    // if (scope.type === 'HD' && scope.conceptGroup.concepts.length > 1) {
                    //     _getNodeDetails(scope.conceptGroup.concepts, function(response) {
                    //         if (Object.keys(response.data).length < 2) {
                    //             var platforms = response.data[Object.keys(response.data)[0]].platforms;
                    //             scope.instructionNodePlatform = !platforms.every(function(el) {
                    //                 return el.title === platforms[0].title;
                    //             });
                    //         } else {
                    //             scope.instructionNodePlatform = true;
                    //         }
                    //     });
                    // } else {
                    //     scope.instructionNodePlatform = false;
                    // }
                    scope.instructionNodePlatform = false;
                };

                scope.$watchGroup([
                    'instructionNodeType',
                    'instructionNodePlatform',
                    'instructionMaxNodes',
                    'instructionMinNodes'],
                    function(newValue) {
                        var instructionNodeType = newValue[0],
                            instructionNodePlatform = newValue[1],
                            instructionMaxNodes = newValue[2],
                            instructionMinNodes = newValue[3];
                        scope.conceptGroup.valid = !(instructionNodeType ||
                                                     instructionNodePlatform ||
                                                     instructionMaxNodes ||
                                                     instructionMinNodes);
                    });

                scope.validate();
            }
        };
    }]);
<|endoftext|>


<commit_before>var crypto = require('crypto');
var md5 = crypto.createHash('md5');

module.exports = function() {
  var args = [].concat.apply([], arguments);
  var hashVar = args + '_' + (+new Date()) + '_' + (Math.random() * 1000);

  md5.update(hashVar);

  return md5.digest('base64');
};
<commit_msg>don't update hash multiple times<commit_after>var crypto = require('crypto');

module.exports = function() {
  var md5 = crypto.createHash('md5');
  var args = [].concat.apply([], arguments);
  var hashVar = args + '_' + (+new Date()) + '_' + (Math.random() * 1000);

  md5.update(hashVar);

  return md5.digest('base64');
};
<|endoftext|>


<commit_before>import Bluebird from 'bluebird'

import 'isomorphic-fetch'
import {status, json, text} from './fetch-helpers'
import stringifyError from './stringify-error'

import range from 'idb-range'
import uniq from 'lodash/uniq'
import flatMap from 'lodash/flatMap'
import filter from 'lodash/filter'
import size from 'lodash/size'
import startsWith from 'lodash/startsWith'
import map from 'lodash/map'
import groupBy from 'lodash/groupBy'
import sortBy from 'lodash/sortBy'
import fromPairs from 'lodash/fromPairs'
import some from 'lodash/some'
import round from 'lodash/round'
import present from 'present'
import yaml from 'js-yaml'

import db from './db'
import buildDept from '../helpers/build-dept'
import buildDeptNum from '../helpers/build-dept-num'
import splitParagraph from '../helpers/split-paragraph'
import {convertTimeStringsToOfferings} from 'sto-sis-time-parser'

const debug = console.log.bind(console)

function dispatch(type, action, ...args) {
	self.postMessage([null, 'dispatch', {type, action, args}])
}

function prepareCourse(course) {
	const nameWords = splitParagraph(course.name)
	const notesWords = splitParagraph(course.notes)
	const titleWords = splitParagraph(course.title)
	const descWords = splitParagraph(course.desc)

	return {
		name: course.name || course.title,
		dept: course.dept || buildDept(course),
		deptnum: course.deptnum || buildDeptNum(course),
		offerings: course.offerings || convertTimeStringsToOfferings(course),
		words: uniq([...nameWords, ...notesWords, ...titleWords, ...descWords]),
		profWords: uniq(flatMap(course.instructors, splitParagraph)),
	}
}

function cacheItemHash(path, type, hash) {
	debug(`cacheItemHash(): ${path}`)

	return db.store(getCacheStoreName(type)).put({id: path, path, hash})
}

function getCacheStoreName(type) {
	if (type === 'courses') {
		return 'courseCache'
	}
	else if (type === 'areas') {
		return 'areaCache'
	}
	else {
		throw new TypeError(`needsUpdate(): "${type}" is not a valid store type`)
	}
}

function storeCourses(path, data) {
	debug(`storeCourses(): ${path}`)

	let coursesToStore = map(data, course => {
		course.sourcePath = path
		return {...course, ...prepareCourse(course)}
	})

	const start = present()
	return db.store('courses').batch(coursesToStore)
		.then(() => {
			debug(`Stored ${size(coursesToStore)} courses in ${round(present() - start, 2)}ms.`)
		})
		.catch(err => {
			const db = err.target.db.name
			const errorName = err.target.error.name

			if (errorName === 'QuotaExceededError') {
				dispatch('notifications', 'logError', {
					id: 'db-storage-quota-exceeded',
					message: `The database "${db}" has exceeded its storage quota.`,
				})
			}

			throw err
		})
}

function storeArea(path, data) {
	debug(`storeArea(): ${path}`)

	let area = {
		...data,
		type: data.type.toLowerCase(),
		sourcePath: path,
		dateAdded: new Date(),
	}

	return db.store('areas').put(area)
		.catch(err => {
			throw err
		})
}

async function cleanPriorData(path, type) {
	debug(`cleanPriorData(): ${path}`)

	let ops = []

	try {
		if (type === 'courses') {
			// erase any old items from this sourcePath
			let oldItems = await db.store('courses').index('sourcePath').getAll(range({ eq: path }))
			ops = fromPairs(map(oldItems, item => ([item.clbid, null])))
		}
		else if (type === 'areas') {
			// erase any old items with this path
			let oldItems = await db.store('areas').getAll(range({ eq: path }))
			ops = fromPairs(map(oldItems, item => ([item.sourcePath, null])))
		}
		else {
			throw new TypeError(`cleanPriorData(): "${type}" is not a valid store type`)
		}

		await db.store(type).batch(ops)
	}
	catch (err) {
		throw err
	}

	try {
		await db.store(getCacheStoreName(type)).del(path)
	}
	catch (err) {
		throw err
	}
}

const updateDatabase = (type, infoFileBase, notificationId) => async infoFromServer => {
	// Get the path to the current file and the hash of the file
	const {path, hash} = infoFromServer
	// Append the hash, to act as a sort of cache-busting mechanism
	const itemUrl = `/${path}?v=${hash}`

	debug(`updateDatabase(): ${path}`)

	const url = infoFileBase + itemUrl

	// go fetch the data!
	let rawData = undefined
	try {
		rawData = await (fetch(url).then(status).then(text))
	}
	catch (err) {
		console.warn('Could not fetch ${url}')
		return false
	}

	// now parse the data into a usable form
	let data
	if (type === 'courses') {
		data = JSON.parse(rawData)
	}
	else if (type === 'areas') {
		data = yaml.safeLoad(rawData)
		data.source = rawData
	}

	try {
		// clear out any old data
		await cleanPriorData(path, type)

		// store the new data
		if (type === 'courses') {
			await storeCourses(path, data)
		}
		else if (type === 'areas') {
			await storeArea(path, data)
		}

		// record that we stored the new data
		await cacheItemHash(path, type, hash)
	}
	catch (e) {
		throw e
	}

	debug(`added ${path}`)
	dispatch('notifications', 'incrementProgress', notificationId)
}

async function needsUpdate(type, path, hash) {
	const {hash: oldHash} = await db.store(getCacheStoreName(type)).get(path) || {}

	return hash !== oldHash
}

async function removeDuplicateAreas() {
	let allAreas
	try {
		allAreas = await db.store('areas').getAll()
	}
	catch (err) {
		throw err
	}

	// now de-duplicate, based on name, type, and revision
	// reasons for duplicates:
	// - a major adds a new revision
	// 		- the old one will have already been replaced by the new one, because of cleanPriorData
	// - a major … are there any other cases?

	const grouped = groupBy(allAreas, area => `{${area.name}, ${area.type}, ${area.revision}}`)
	const withDuplicates = filter(grouped, list => list.length > 1)

	let ops = {}
	for (let duplicatesList of withDuplicates) {
		duplicatesList = sortBy(duplicatesList, area => area.sourcePath.length)
		duplicatesList.shift() // take off the shortest one
		ops = {...ops, ...fromPairs(map(duplicatesList, item => ([item.sourcePath, null])))}
	}

	// remove any that are invalid
	// --- something about any values that aren't objects

	const invalidAreas = filter(allAreas, area => some(['name', 'revision', 'type'], key => area[key] === undefined))
	ops = {...ops, ...fromPairs(map(invalidAreas, item => ([item.sourcePath, null])))}

	try {
		await db.store('areas').batch(ops)
	}
	catch (err) {
		throw err
	}
}

async function loadFiles(url, infoFileBase) {
	debug(`loadFiles(): ${url}`)

	let infoFile
	try {
		infoFile = await (fetch(url).then(status).then(json))
	}
	catch (err) {
		if (startsWith(err.message, 'Failed to fetch')) {
			console.warn(`loadFiles(): Failed to fetch ${url}`)
			return false
		}
		throw err
	}

	const type = infoFile.type
	const notificationId = type
	let filesToLoad = infoFile.files

	if (type === 'courses') {
		// Only get the last four years of data
		const oldestYear = new Date().getFullYear() - 4
		filesToLoad = filter(filesToLoad, file => file.year >= oldestYear)
	}

	// For each file, see if it needs loading.
	const filesThatNeedUpdate = map(filesToLoad, file => needsUpdate(type, file.path, file.hash))
	const fileNeedsLoading = await Bluebird.all(filesThatNeedUpdate)

	// Cross-reference each file to load with the list of files that need loading
	filesToLoad = filter(filesToLoad, (file, index) => fileNeedsLoading[index])

	// Exit early if nothing needs to happen
	if (filesToLoad.length === 0) {
		return true
	}

	// Fire off the progress bar
	dispatch('notifications', 'startProgress', notificationId, `Loading ${type}`, {max: size(filesToLoad)}, true)

	// Load them into the database
	try {
		const update = updateDatabase(type, infoFileBase, notificationId)
		await Bluebird.all(map(filesToLoad, update))
	}
	catch (err) {
		throw err
	}

	// Clean up the database a bit
	try {
		if (type === 'areas') {
			removeDuplicateAreas()
		}
	}
	catch (err) {
		throw err
	}

	// Remove the progress bar after 1.5 seconds
	dispatch('notifications', 'removeNotification', notificationId, 1500)
	if (type === 'courses') {
		dispatch('courses', 'refreshCourses')
	}
	else if (type === 'areas') {
		dispatch('areas', 'refreshAreas')
	}

	return true
}

self.addEventListener('message', ({data}) => {
	const [id, ...args] = data
	debug('[load-data] received message:', args)

	loadFiles(...args)
		.then(result => self.postMessage([id, 'result', result]))
		.catch(err => self.postMessage([id, 'error', JSON.parse(stringifyError(err))]))
})
<commit_msg>fix an argument to startProgress<commit_after>import Bluebird from 'bluebird'

import 'isomorphic-fetch'
import {status, json, text} from './fetch-helpers'
import stringifyError from './stringify-error'

import range from 'idb-range'
import uniq from 'lodash/uniq'
import flatMap from 'lodash/flatMap'
import filter from 'lodash/filter'
import size from 'lodash/size'
import startsWith from 'lodash/startsWith'
import map from 'lodash/map'
import groupBy from 'lodash/groupBy'
import sortBy from 'lodash/sortBy'
import fromPairs from 'lodash/fromPairs'
import some from 'lodash/some'
import round from 'lodash/round'
import present from 'present'
import yaml from 'js-yaml'

import db from './db'
import buildDept from '../helpers/build-dept'
import buildDeptNum from '../helpers/build-dept-num'
import splitParagraph from '../helpers/split-paragraph'
import {convertTimeStringsToOfferings} from 'sto-sis-time-parser'

const debug = console.log.bind(console)

function dispatch(type, action, ...args) {
	self.postMessage([null, 'dispatch', {type, action, args}])
}

function prepareCourse(course) {
	const nameWords = splitParagraph(course.name)
	const notesWords = splitParagraph(course.notes)
	const titleWords = splitParagraph(course.title)
	const descWords = splitParagraph(course.desc)

	return {
		name: course.name || course.title,
		dept: course.dept || buildDept(course),
		deptnum: course.deptnum || buildDeptNum(course),
		offerings: course.offerings || convertTimeStringsToOfferings(course),
		words: uniq([...nameWords, ...notesWords, ...titleWords, ...descWords]),
		profWords: uniq(flatMap(course.instructors, splitParagraph)),
	}
}

function cacheItemHash(path, type, hash) {
	debug(`cacheItemHash(): ${path}`)

	return db.store(getCacheStoreName(type)).put({id: path, path, hash})
}

function getCacheStoreName(type) {
	if (type === 'courses') {
		return 'courseCache'
	}
	else if (type === 'areas') {
		return 'areaCache'
	}
	else {
		throw new TypeError(`needsUpdate(): "${type}" is not a valid store type`)
	}
}

function storeCourses(path, data) {
	debug(`storeCourses(): ${path}`)

	let coursesToStore = map(data, course => {
		course.sourcePath = path
		return {...course, ...prepareCourse(course)}
	})

	const start = present()
	return db.store('courses').batch(coursesToStore)
		.then(() => {
			debug(`Stored ${size(coursesToStore)} courses in ${round(present() - start, 2)}ms.`)
		})
		.catch(err => {
			const db = err.target.db.name
			const errorName = err.target.error.name

			if (errorName === 'QuotaExceededError') {
				dispatch('notifications', 'logError', {
					id: 'db-storage-quota-exceeded',
					message: `The database "${db}" has exceeded its storage quota.`,
				})
			}

			throw err
		})
}

function storeArea(path, data) {
	debug(`storeArea(): ${path}`)

	let area = {
		...data,
		type: data.type.toLowerCase(),
		sourcePath: path,
		dateAdded: new Date(),
	}

	return db.store('areas').put(area)
		.catch(err => {
			throw err
		})
}

async function cleanPriorData(path, type) {
	debug(`cleanPriorData(): ${path}`)

	let ops = []

	try {
		if (type === 'courses') {
			// erase any old items from this sourcePath
			let oldItems = await db.store('courses').index('sourcePath').getAll(range({ eq: path }))
			ops = fromPairs(map(oldItems, item => ([item.clbid, null])))
		}
		else if (type === 'areas') {
			// erase any old items with this path
			let oldItems = await db.store('areas').getAll(range({ eq: path }))
			ops = fromPairs(map(oldItems, item => ([item.sourcePath, null])))
		}
		else {
			throw new TypeError(`cleanPriorData(): "${type}" is not a valid store type`)
		}

		await db.store(type).batch(ops)
	}
	catch (err) {
		throw err
	}

	try {
		await db.store(getCacheStoreName(type)).del(path)
	}
	catch (err) {
		throw err
	}
}

const updateDatabase = (type, infoFileBase, notificationId) => async infoFromServer => {
	// Get the path to the current file and the hash of the file
	const {path, hash} = infoFromServer
	// Append the hash, to act as a sort of cache-busting mechanism
	const itemUrl = `/${path}?v=${hash}`

	debug(`updateDatabase(): ${path}`)

	const url = infoFileBase + itemUrl

	// go fetch the data!
	let rawData = undefined
	try {
		rawData = await (fetch(url).then(status).then(text))
	}
	catch (err) {
		console.warn('Could not fetch ${url}')
		return false
	}

	// now parse the data into a usable form
	let data
	if (type === 'courses') {
		data = JSON.parse(rawData)
	}
	else if (type === 'areas') {
		data = yaml.safeLoad(rawData)
		data.source = rawData
	}

	try {
		// clear out any old data
		await cleanPriorData(path, type)

		// store the new data
		if (type === 'courses') {
			await storeCourses(path, data)
		}
		else if (type === 'areas') {
			await storeArea(path, data)
		}

		// record that we stored the new data
		await cacheItemHash(path, type, hash)
	}
	catch (e) {
		throw e
	}

	debug(`added ${path}`)
	dispatch('notifications', 'incrementProgress', notificationId)
}

async function needsUpdate(type, path, hash) {
	const {hash: oldHash} = await db.store(getCacheStoreName(type)).get(path) || {}

	return hash !== oldHash
}

async function removeDuplicateAreas() {
	let allAreas
	try {
		allAreas = await db.store('areas').getAll()
	}
	catch (err) {
		throw err
	}

	// now de-duplicate, based on name, type, and revision
	// reasons for duplicates:
	// - a major adds a new revision
	// 		- the old one will have already been replaced by the new one, because of cleanPriorData
	// - a major … are there any other cases?

	const grouped = groupBy(allAreas, area => `{${area.name}, ${area.type}, ${area.revision}}`)
	const withDuplicates = filter(grouped, list => list.length > 1)

	let ops = {}
	for (let duplicatesList of withDuplicates) {
		duplicatesList = sortBy(duplicatesList, area => area.sourcePath.length)
		duplicatesList.shift() // take off the shortest one
		ops = {...ops, ...fromPairs(map(duplicatesList, item => ([item.sourcePath, null])))}
	}

	// remove any that are invalid
	// --- something about any values that aren't objects

	const invalidAreas = filter(allAreas, area => some(['name', 'revision', 'type'], key => area[key] === undefined))
	ops = {...ops, ...fromPairs(map(invalidAreas, item => ([item.sourcePath, null])))}

	try {
		await db.store('areas').batch(ops)
	}
	catch (err) {
		throw err
	}
}

async function loadFiles(url, infoFileBase) {
	debug(`loadFiles(): ${url}`)

	let infoFile
	try {
		infoFile = await (fetch(url).then(status).then(json))
	}
	catch (err) {
		if (startsWith(err.message, 'Failed to fetch')) {
			console.warn(`loadFiles(): Failed to fetch ${url}`)
			return false
		}
		throw err
	}

	const type = infoFile.type
	const notificationId = type
	let filesToLoad = infoFile.files

	if (type === 'courses') {
		// Only get the last four years of data
		const oldestYear = new Date().getFullYear() - 4
		filesToLoad = filter(filesToLoad, file => file.year >= oldestYear)
	}

	// For each file, see if it needs loading.
	const filesThatNeedUpdate = map(filesToLoad, file => needsUpdate(type, file.path, file.hash))
	const fileNeedsLoading = await Bluebird.all(filesThatNeedUpdate)

	// Cross-reference each file to load with the list of files that need loading
	filesToLoad = filter(filesToLoad, (file, index) => fileNeedsLoading[index])

	// Exit early if nothing needs to happen
	if (filesToLoad.length === 0) {
		return true
	}

	// Fire off the progress bar
	dispatch('notifications', 'startProgress', notificationId, `Loading ${type}`, {max: size(filesToLoad), showButton: true})

	// Load them into the database
	try {
		const update = updateDatabase(type, infoFileBase, notificationId)
		await Bluebird.all(map(filesToLoad, update))
	}
	catch (err) {
		throw err
	}

	// Clean up the database a bit
	try {
		if (type === 'areas') {
			removeDuplicateAreas()
		}
	}
	catch (err) {
		throw err
	}

	// Remove the progress bar after 1.5 seconds
	dispatch('notifications', 'removeNotification', notificationId, 1500)
	if (type === 'courses') {
		dispatch('courses', 'refreshCourses')
	}
	else if (type === 'areas') {
		dispatch('areas', 'refreshAreas')
	}

	return true
}

self.addEventListener('message', ({data}) => {
	const [id, ...args] = data
	debug('[load-data] received message:', args)

	loadFiles(...args)
		.then(result => self.postMessage([id, 'result', result]))
		.catch(err => self.postMessage([id, 'error', JSON.parse(stringifyError(err))]))
})
<|endoftext|>


<commit_before>'use strict';

exports.BattleScripts = {
	init: function () {
		Object.values(this.data.Abilities).forEach(ability => {
			if (ability.id === 'trace') return;
			let id = 'other' + ability.id;
			this.data.Statuses[id] = Object.assign({}, ability);
			this.data.Statuses[id].id = id;
			this.data.Statuses[id].noCopy = true;
			this.data.Statuses[id].effectType = "Ability";
			this.data.Statuses[id].fullname = 'ability: ' + ability.name;
		});
	},
	pokemon: {
		hasAbility: function (ability) {
			if (this.ignoringAbility()) return false;
			if (Array.isArray(ability)) return ability.some(ability => this.hasAbility(ability));
			ability = toId(ability);
			return this.ability === ability || !!this.volatiles[ability];
		},
	},
};
<commit_msg>Update Pokébilities (#3076)<commit_after>'use strict';

exports.BattleScripts = {
	init: function () {
		Object.values(this.data.Abilities).forEach(ability => {
			if (ability.id === 'trace') return;
			let id = 'other' + ability.id;
			this.data.Statuses[id] = Object.assign({}, ability);
			this.data.Statuses[id].id = id;
			this.data.Statuses[id].noCopy = true;
			this.data.Statuses[id].effectType = "Ability";
			this.data.Statuses[id].fullname = 'ability: ' + ability.name;
		});
	},
	pokemon: {
		hasAbility: function (ability) {
			if (this.ignoringAbility()) return false;
			if (Array.isArray(ability)) return ability.some(ability => this.hasAbility(ability));
			ability = toId(ability);
			return this.ability === ability || !!this.volatiles["other" + ability];
		},
		transformInto: function (pokemon, user, effect) {
			let template = pokemon.template;
			if (pokemon.fainted || pokemon.illusion || (pokemon.volatiles['substitute'] && this.battle.gen >= 5)) {
				return false;
			}
			if (!template.abilities || (pokemon && pokemon.transformed && this.battle.gen >= 2) || (user && user.transformed && this.battle.gen >= 5)) {
				return false;
			}
			if (!this.formeChange(template, true)) {
				return false;
			}
			this.transformed = true;

			this.types = pokemon.types;
			this.addedType = pokemon.addedType;
			this.knownType = this.side === pokemon.side && pokemon.knownType;

			for (let statName in this.stats) {
				this.stats[statName] = pokemon.stats[statName];
			}
			this.moveset = [];
			this.moves = [];
			this.set.ivs = (this.battle.gen >= 5 ? this.set.ivs : pokemon.set.ivs);
			this.hpType = (this.battle.gen >= 5 ? this.hpType : pokemon.hpType);
			this.hpPower = (this.battle.gen >= 5 ? this.hpPower : pokemon.hpPower);
			for (let i = 0; i < pokemon.moveset.length; i++) {
				let moveData = pokemon.moveset[i];
				let moveName = moveData.move;
				if (moveData.id === 'hiddenpower') {
					moveName = 'Hidden Power ' + this.hpType;
				}
				this.moveset.push({
					move: moveName,
					id: moveData.id,
					pp: moveData.maxpp === 1 ? 1 : 5,
					maxpp: this.battle.gen >= 5 ? (moveData.maxpp === 1 ? 1 : 5) : moveData.maxpp,
					target: moveData.target,
					disabled: false,
					used: false,
					virtual: true,
				});
				this.moves.push(toId(moveName));
			}
			for (let j in pokemon.boosts) {
				this.boosts[j] = pokemon.boosts[j];
			}
			if (effect) {
				this.battle.add('-transform', this, pokemon, '[from] ' + effect.fullname);
			} else {
				this.battle.add('-transform', this, pokemon);
			}
			this.setAbility(pokemon.ability, this, {id: 'transform'});
			this.innates.forEach(innate => this.removeVolatile("other" + innate, this));
			pokemon.innates.forEach(innate => this.addVolatile("other" + innate, this));
			return true;
		},
	},
};
<|endoftext|>


<commit_before>/*
 * Internal support module for config tables.
 */

var jQD = require('../../util/jquery-ext.js');

exports.markConfigForm = function(configTable) {
    var form = configTable.closest('form');
    form.addClass('jenkins-config');
    return form;
};

exports.findConfigTables = function() {
    var $ = jQD.getJQuery();
    // The config tables are the immediate child
 elements of <form> elements
    // with a name of "config"?
    return $('form[name="config"] > table');
};

exports.decorateConfigTable = function(configTable) {
    var $ = jQD.getJQuery();
    var sectionHeaders = $('.section-header', configTable);
    var configForm = exports.markConfigForm(configTable);

    // Mark the ancestor
s of the section headers and add a title
    sectionHeaders.each(function () {
        var sectionHeader = $(this);
        var sectionRow = sectionHeader.closest('tr');
        var sectionTitle = sectionRow.text();

        // Remove leading hash from accumulated text in title (from  element).
        if (sectionTitle.indexOf('#') === 0) {
            sectionTitle = sectionTitle.substring(1);
        }

        sectionRow.addClass('section-header-row');
        sectionRow.attr('title', sectionTitle);
    });

    // Go through the top level
 elements (immediately inside the
)
    // and group the related
s based on the "section-header-row", using a "normalized"
    // version of the section title as the section id.
    var tbody = $('> tbody', configTable);
    var topRows = $('> tr', tbody);
    var configTableMetadata = new ConfigTableMetaData(configForm, configTable, topRows);
    var curSection = new ConfigSection(configTableMetadata, 'General');

    configTableMetadata.sections.push(curSection);
    curSection.id = exports.toId(curSection.title);

    topRows.each(function () {
        var tr = $(this);
        if (tr.hasClass('section-header-row')) {
            // a new section
            var title = tr.attr('title');
            curSection = new ConfigSection(configTableMetadata, title);
            configTableMetadata.sections.push(curSection);
        }

        curSection.rows.push(tr);
        tr.addClass(curSection.id);
    });

    var buttonsRow = $('#bottom-sticker', configTable).closest('tr');
    buttonsRow.removeClass(curSection.id);
    buttonsRow.addClass(exports.toId('buttons'));

    return configTableMetadata;
};

exports.toId = function(string) {
    string = string.trim();
    return 'config_' + string.replace(/[\W_]+/g, '_').toLowerCase();
};

/*
 * =======================================================================================
 * Configuration table section.
 * =======================================================================================
 */
function ConfigSection(parentCMD, title) {
    this.parentCMD = parentCMD;
    this.title = title;
    this.id = exports.toId(title);
    this.rows = [];
    this.rowSets = undefined;
    this.activator = undefined;
}

/*
 * Set the element (jquery) that activates the section (on click).
 */
ConfigSection.prototype.setActivator = function(activator) {
    this.activator = activator;

    var section = this;
    section.activator.click(function() {
        section.parentCMD.showSection(section);
    });
};

ConfigSection.prototype.activate = function() {
    if (this.activator) {
        this.activator.click();
    } else {
        console.warn('No activator attached to config section object.');
    }
};

ConfigSection.prototype.activeRowCount = function() {
    var activeRowCount = 0;
    for (var i = 0; i < this.rows.length; i++) {
        if (this.rows[i].hasClass('active')) {
            activeRowCount++;
        }
    }
    return activeRowCount;
};

ConfigSection.prototype.updateRowSetVisibility = function() {
    if (this.rowSets === undefined) {
        // Lazily gather rowset information.
        this.gatherRowSets();
    }
    for (var i = 0; i < this.rowSets.length; i++) {
        var rowSet = this.rowSets[i];
        if (rowSet.toggleWidget !== undefined) {
            var isChecked = rowSet.toggleWidget.is(':checked');
            for (var ii = 0; ii < rowSet.rows.length; ii++) {
                if (isChecked) {
                    rowSet.rows[ii].show();
                } else {
                    rowSet.rows[ii].hide();
                }
            }
        }
    }
};

ConfigSection.prototype.gatherRowSets = function() {
    this.rowSets = [];

    // Only tracking row-sets that are bounded by 'row-set-start' and 'row-set-end' (for now).
    // Also, only capturing the rows after the 'block-control' input (checkbox, radio etc)
    // and before the 'row-set-end'.
    // TODO: Find out how these actually work. It seems like they can be nested into a hierarchy :(
    // Also seems like you can have these "optional-block" thingies which are not wrapped
    // in 'row-set-start' etc. Grrrrrr :(

    var curRowSet = undefined; // jshint ignore:line
    for (var i = 0; i < this.rows.length; i++) {
        var row = this.rows[i];

        if (row.hasClass('row-set-start')) {
            curRowSet = new ConfigRowSet(row);
            curRowSet.findToggleWidget(row);
        } else if (curRowSet !== undefined) {
            if (row.hasClass('row-set-end')) {
                curRowSet.endRow = row;
                // Only capture the row-set if we find a 'row-set-end'.
                // Yeah, this does not handle hierarchical stuff (see above TO-DO).
                this.rowSets.push(curRowSet);
                curRowSet = undefined;
            } else if (curRowSet.toggleWidget === undefined) {
                curRowSet.findToggleWidget(row);
            } else {
                // we have the toggleWidget, which means that this row is
                // one of the rows after that row and is one of the rows that's
                // subject to being made visible/hidden when the input is
                // checked or unchecked.
                curRowSet.rows.push(row);
            }
        }
    }
};

ConfigSection.prototype.getRowSetLabels = function() {
    var labels = [];
    for (var i = 0; i < this.rowSets.length; i++) {
        var rowSet = this.rowSets[i];
        if (rowSet.label) {
            labels.push(rowSet.label);
        }
    }
    return labels;
};

ConfigSection.prototype.highlightText = function(text) {
    var $ = jQD.getJQuery();
    var selector = ":containsci('" + text + "')";

    for (var i = 0; i < this.rows.length; i++) {
        var row = this.rows[i];

        /*jshint loopfunc: true */
        $('span.highlight-split', row).each(function() { // jshint ignore:line
            var highlightSplit = $(this);
            highlightSplit.before(highlightSplit.text());
            highlightSplit.remove();
        });

        if (text !== '') {
            var regex = new RegExp('(' + text + ')',"gi");

            /*jshint loopfunc: true */
            $(selector, row).find(':not(:input)').each(function() {
                var $this = $(this);
                $this.contents().each(function () {
                    // We specifically only mess with text nodes
                    if (this.nodeType === 3) {
                        var highlightedMarkup = this.wholeText.replace(regex, '$1');
                        $(this).replaceWith('' + highlightedMarkup + '');
                    }
                });
            });
        }
    }
};

/*
 * =======================================================================================
 * Configuration table section.
 * =======================================================================================
 */
function ConfigRowSet(startRow) {
    this.startRow = startRow;
    this.rows = [];
    this.endRow = undefined;
    this.toggleWidget = undefined;
    this.label = undefined;
}

/*
 * Find the row-set toggle widget i.e. the input element that indicates that
 * the row-set rows should be made visible or not.
 */
ConfigRowSet.prototype.findToggleWidget = function(row) {
    var $ = jQD.getJQuery();
    var input = $(':input.block-control', row);
    if (input.size() === 1) {
        this.toggleWidget = input;
        this.label = input.parent().find('label').text();
        input.addClass('disable-behavior');
    }
};

/*
 * =======================================================================================
 * ConfigTable MetaData class.
 * =======================================================================================
 */
function ConfigTableMetaData(configForm, configTable, topRows) {
    this.configForm = configForm;
    this.configTable = configTable;
    this.topRows = topRows;
    this.sections = [];
    this.findInput = undefined;
    this.showListeners = [];
    this.configWidgets = undefined;
    this.addWidgetsContainer();
    this.addFindWidget();
}

ConfigTableMetaData.prototype.addWidgetsContainer = function() {
    var $ = jQD.getJQuery();
    this.configWidgets = $('

');
    this.configWidgets.insertBefore(this.configForm);
};

ConfigTableMetaData.prototype.addFindWidget = function() {
    var $ = jQD.getJQuery();
    var thisTMD = this;
    var findWidget = $('

x<input placeholder="find"/>

');

    thisTMD.findInput = $('input', findWidget);

    // Add the find text clearer
    $('.clear', findWidget).click(function() {
        thisTMD.findInput.val('');
        thisTMD.showSections('');
    });

    var findTimeout;
    thisTMD.findInput.keydown(function() {
        if (findTimeout) {
            clearTimeout(findTimeout);
            findTimeout = undefined;
        }
        findTimeout = setTimeout(function() {
            findTimeout = undefined;
            thisTMD.showSections(thisTMD.findInput.val());
        }, 300);
    });

    this.configWidgets.append(findWidget);
};

ConfigTableMetaData.prototype.sectionCount = function() {
    return this.sections.length;
};

ConfigTableMetaData.prototype.hasSections = function() {
    var hasSections = (this.sectionCount() > 0);
    if (!hasSections) {
        console.warn('Jenkins configuration without sections?');
    }
    return  hasSections;
};

ConfigTableMetaData.prototype.sectionIds = function() {
    var sectionIds = [];
    for (var i = 0; i < this.sections.length; i++) {
        sectionIds.push(this.sections[i].id);
    }
    return sectionIds;
};

ConfigTableMetaData.prototype.activateSection = function(sectionId) {
    if (!sectionId) {
        throw 'Invalid section id "' + sectionId + '"';
    }

    var section = this.getSection(sectionId);
    if (section) {
        section.activate();
    }
};

ConfigTableMetaData.prototype.activeSection = function() {
    if (this.hasSections()) {
        for (var i = 0; i < this.sections.length; i++) {
            var section = this.sections[i];
            if (section.activator.hasClass('active')) {
                return section;
            }
        }
    }
};

ConfigTableMetaData.prototype.getSection = function(sectionId) {
    if (this.hasSections()) {
        for (var i = 0; i < this.sections.length; i++) {
            var section = this.sections[i];
            if (section.id === sectionId) {
                return section;
            }
        }
    }
    return undefined;
};

ConfigTableMetaData.prototype.activateFirstSection = function() {
    if (this.hasSections()) {
        this.activateSection(this.sections[0].id);
    }
};

ConfigTableMetaData.prototype.activeSectionCount = function() {
    var activeSectionCount = 0;
    if (this.hasSections()) {
        for (var i = 0; i < this.sections.length; i++) {
            var section = this.sections[i];
            if (section.activator.hasClass('active')) {
                activeSectionCount++;
            }
        }
    }
    return activeSectionCount;
};

ConfigTableMetaData.prototype.showSection = function(section) {
    if (typeof section === 'string') {
        section = this.getSection(section);
    }

    if (section) {
        var $ = jQD.getJQuery();

        // Deactivate currently active section ...
        this.deactivateActiveSection();

        // Active the specified section
        section.activator.addClass('active');
        this.topRows.filter('.' + section.id).addClass('active').show();

        // Hide the section header row. No need for it now because the
        // tab text acts as the section label.
        $('.section-header-row').hide();

        // and always show the buttons
        this.topRows.filter('.config_buttons').show();

        // Update the row-set visibility
        section.updateRowSetVisibility();
        section.highlightText(this.findInput.val());

        fireListeners(this.showListeners, section);
    }
};

ConfigTableMetaData.prototype.deactivateActiveSection = function() {
    var $ = jQD.getJQuery();

    $('.config-section-activator.active', this.activatorContainer).removeClass('active');
    this.topRows.filter('.active').removeClass('active');
    this.topRows.hide();
};

ConfigTableMetaData.prototype.onShowSection = function(listener) {
    this.showListeners.push(listener);
};

ConfigTableMetaData.prototype.showSections = function(withText) {
    if (withText === '') {
        if (this.hasSections()) {
            for (var i1 = 0; i1 < this.sections.length; i1++) {
                this.sections[i1].activator.show();
            }
            var activeSection = this.activeSection();
            if (!activeSection) {
                this.showSection(this.sections[0]);
            } else {
                activeSection.highlightText(this.findInput.val());
            }
        }
    } else {
        if (this.hasSections()) {
            var $ = jQD.getJQuery();
            var selector = ":containsci('" + withText + "')";
            var sectionsWithText = [];

            for (var i2 = 0; i2 < this.sections.length; i2++) {
                var section = this.sections[i2];
                var containsText = false;

                for (var i3 = 0; i3
 0) {
                        containsText = true;
                        break;
                    }
                }

                if (containsText) {
                    sectionsWithText.push(section);
                } else {
                    section.activator.hide();
                }
            }

            // Select the first section to contain the text.
            if (sectionsWithText.length > 0) {
                this.showSection(sectionsWithText[0]);
            } else {
                this.deactivateActiveSection();
            }
        }
    }
};

function fireListeners(listeners, contextObject) {
    for (var i = 0; i < listeners.length; i++) {
        fireListener(listeners[i], contextObject);
    }
    function fireListener(listener, contextObject) {
        setTimeout(function() {
            listener.call(contextObject);
        }, 1);
    }
}<commit_msg>Put focus back onto the find field if the clear widget is pressed<commit_after>/*
 * Internal support module for config tables.
 */

var jQD = require('../../util/jquery-ext.js');

exports.markConfigForm = function(configTable) {
    var form = configTable.closest('form');
    form.addClass('jenkins-config');
    return form;
};

exports.findConfigTables = function() {
    var $ = jQD.getJQuery();
    // The config tables are the immediate child
 elements of <form> elements
    // with a name of "config"?
    return $('form[name="config"] > table');
};

exports.decorateConfigTable = function(configTable) {
    var $ = jQD.getJQuery();
    var sectionHeaders = $('.section-header', configTable);
    var configForm = exports.markConfigForm(configTable);

    // Mark the ancestor
s of the section headers and add a title
    sectionHeaders.each(function () {
        var sectionHeader = $(this);
        var sectionRow = sectionHeader.closest('tr');
        var sectionTitle = sectionRow.text();

        // Remove leading hash from accumulated text in title (from  element).
        if (sectionTitle.indexOf('#') === 0) {
            sectionTitle = sectionTitle.substring(1);
        }

        sectionRow.addClass('section-header-row');
        sectionRow.attr('title', sectionTitle);
    });

    // Go through the top level
 elements (immediately inside the
)
    // and group the related
s based on the "section-header-row", using a "normalized"
    // version of the section title as the section id.
    var tbody = $('> tbody', configTable);
    var topRows = $('> tr', tbody);
    var configTableMetadata = new ConfigTableMetaData(configForm, configTable, topRows);
    var curSection = new ConfigSection(configTableMetadata, 'General');

    configTableMetadata.sections.push(curSection);
    curSection.id = exports.toId(curSection.title);

    topRows.each(function () {
        var tr = $(this);
        if (tr.hasClass('section-header-row')) {
            // a new section
            var title = tr.attr('title');
            curSection = new ConfigSection(configTableMetadata, title);
            configTableMetadata.sections.push(curSection);
        }

        curSection.rows.push(tr);
        tr.addClass(curSection.id);
    });

    var buttonsRow = $('#bottom-sticker', configTable).closest('tr');
    buttonsRow.removeClass(curSection.id);
    buttonsRow.addClass(exports.toId('buttons'));

    return configTableMetadata;
};

exports.toId = function(string) {
    string = string.trim();
    return 'config_' + string.replace(/[\W_]+/g, '_').toLowerCase();
};

/*
 * =======================================================================================
 * Configuration table section.
 * =======================================================================================
 */
function ConfigSection(parentCMD, title) {
    this.parentCMD = parentCMD;
    this.title = title;
    this.id = exports.toId(title);
    this.rows = [];
    this.rowSets = undefined;
    this.activator = undefined;
}

/*
 * Set the element (jquery) that activates the section (on click).
 */
ConfigSection.prototype.setActivator = function(activator) {
    this.activator = activator;

    var section = this;
    section.activator.click(function() {
        section.parentCMD.showSection(section);
    });
};

ConfigSection.prototype.activate = function() {
    if (this.activator) {
        this.activator.click();
    } else {
        console.warn('No activator attached to config section object.');
    }
};

ConfigSection.prototype.activeRowCount = function() {
    var activeRowCount = 0;
    for (var i = 0; i < this.rows.length; i++) {
        if (this.rows[i].hasClass('active')) {
            activeRowCount++;
        }
    }
    return activeRowCount;
};

ConfigSection.prototype.updateRowSetVisibility = function() {
    if (this.rowSets === undefined) {
        // Lazily gather rowset information.
        this.gatherRowSets();
    }
    for (var i = 0; i < this.rowSets.length; i++) {
        var rowSet = this.rowSets[i];
        if (rowSet.toggleWidget !== undefined) {
            var isChecked = rowSet.toggleWidget.is(':checked');
            for (var ii = 0; ii < rowSet.rows.length; ii++) {
                if (isChecked) {
                    rowSet.rows[ii].show();
                } else {
                    rowSet.rows[ii].hide();
                }
            }
        }
    }
};

ConfigSection.prototype.gatherRowSets = function() {
    this.rowSets = [];

    // Only tracking row-sets that are bounded by 'row-set-start' and 'row-set-end' (for now).
    // Also, only capturing the rows after the 'block-control' input (checkbox, radio etc)
    // and before the 'row-set-end'.
    // TODO: Find out how these actually work. It seems like they can be nested into a hierarchy :(
    // Also seems like you can have these "optional-block" thingies which are not wrapped
    // in 'row-set-start' etc. Grrrrrr :(

    var curRowSet = undefined; // jshint ignore:line
    for (var i = 0; i < this.rows.length; i++) {
        var row = this.rows[i];

        if (row.hasClass('row-set-start')) {
            curRowSet = new ConfigRowSet(row);
            curRowSet.findToggleWidget(row);
        } else if (curRowSet !== undefined) {
            if (row.hasClass('row-set-end')) {
                curRowSet.endRow = row;
                // Only capture the row-set if we find a 'row-set-end'.
                // Yeah, this does not handle hierarchical stuff (see above TO-DO).
                this.rowSets.push(curRowSet);
                curRowSet = undefined;
            } else if (curRowSet.toggleWidget === undefined) {
                curRowSet.findToggleWidget(row);
            } else {
                // we have the toggleWidget, which means that this row is
                // one of the rows after that row and is one of the rows that's
                // subject to being made visible/hidden when the input is
                // checked or unchecked.
                curRowSet.rows.push(row);
            }
        }
    }
};

ConfigSection.prototype.getRowSetLabels = function() {
    var labels = [];
    for (var i = 0; i < this.rowSets.length; i++) {
        var rowSet = this.rowSets[i];
        if (rowSet.label) {
            labels.push(rowSet.label);
        }
    }
    return labels;
};

ConfigSection.prototype.highlightText = function(text) {
    var $ = jQD.getJQuery();
    var selector = ":containsci('" + text + "')";

    for (var i = 0; i < this.rows.length; i++) {
        var row = this.rows[i];

        /*jshint loopfunc: true */
        $('span.highlight-split', row).each(function() { // jshint ignore:line
            var highlightSplit = $(this);
            highlightSplit.before(highlightSplit.text());
            highlightSplit.remove();
        });

        if (text !== '') {
            var regex = new RegExp('(' + text + ')',"gi");

            /*jshint loopfunc: true */
            $(selector, row).find(':not(:input)').each(function() {
                var $this = $(this);
                $this.contents().each(function () {
                    // We specifically only mess with text nodes
                    if (this.nodeType === 3) {
                        var highlightedMarkup = this.wholeText.replace(regex, '$1');
                        $(this).replaceWith('' + highlightedMarkup + '');
                    }
                });
            });
        }
    }
};

/*
 * =======================================================================================
 * Configuration table section.
 * =======================================================================================
 */
function ConfigRowSet(startRow) {
    this.startRow = startRow;
    this.rows = [];
    this.endRow = undefined;
    this.toggleWidget = undefined;
    this.label = undefined;
}

/*
 * Find the row-set toggle widget i.e. the input element that indicates that
 * the row-set rows should be made visible or not.
 */
ConfigRowSet.prototype.findToggleWidget = function(row) {
    var $ = jQD.getJQuery();
    var input = $(':input.block-control', row);
    if (input.size() === 1) {
        this.toggleWidget = input;
        this.label = input.parent().find('label').text();
        input.addClass('disable-behavior');
    }
};

/*
 * =======================================================================================
 * ConfigTable MetaData class.
 * =======================================================================================
 */
function ConfigTableMetaData(configForm, configTable, topRows) {
    this.configForm = configForm;
    this.configTable = configTable;
    this.topRows = topRows;
    this.sections = [];
    this.findInput = undefined;
    this.showListeners = [];
    this.configWidgets = undefined;
    this.addWidgetsContainer();
    this.addFindWidget();
}

ConfigTableMetaData.prototype.addWidgetsContainer = function() {
    var $ = jQD.getJQuery();
    this.configWidgets = $('

');
    this.configWidgets.insertBefore(this.configForm);
};

ConfigTableMetaData.prototype.addFindWidget = function() {
    var $ = jQD.getJQuery();
    var thisTMD = this;
    var findWidget = $('

x<input placeholder="find"/>

');

    thisTMD.findInput = $('input', findWidget);

    // Add the find text clearer
    $('.clear', findWidget).click(function() {
        thisTMD.findInput.val('');
        thisTMD.showSections('');
        thisTMD.findInput.focus();
    });

    var findTimeout;
    thisTMD.findInput.keydown(function() {
        if (findTimeout) {
            clearTimeout(findTimeout);
            findTimeout = undefined;
        }
        findTimeout = setTimeout(function() {
            findTimeout = undefined;
            thisTMD.showSections(thisTMD.findInput.val());
        }, 300);
    });

    this.configWidgets.append(findWidget);
};

ConfigTableMetaData.prototype.sectionCount = function() {
    return this.sections.length;
};

ConfigTableMetaData.prototype.hasSections = function() {
    var hasSections = (this.sectionCount() > 0);
    if (!hasSections) {
        console.warn('Jenkins configuration without sections?');
    }
    return  hasSections;
};

ConfigTableMetaData.prototype.sectionIds = function() {
    var sectionIds = [];
    for (var i = 0; i < this.sections.length; i++) {
        sectionIds.push(this.sections[i].id);
    }
    return sectionIds;
};

ConfigTableMetaData.prototype.activateSection = function(sectionId) {
    if (!sectionId) {
        throw 'Invalid section id "' + sectionId + '"';
    }

    var section = this.getSection(sectionId);
    if (section) {
        section.activate();
    }
};

ConfigTableMetaData.prototype.activeSection = function() {
    if (this.hasSections()) {
        for (var i = 0; i < this.sections.length; i++) {
            var section = this.sections[i];
            if (section.activator.hasClass('active')) {
                return section;
            }
        }
    }
};

ConfigTableMetaData.prototype.getSection = function(sectionId) {
    if (this.hasSections()) {
        for (var i = 0; i < this.sections.length; i++) {
            var section = this.sections[i];
            if (section.id === sectionId) {
                return section;
            }
        }
    }
    return undefined;
};

ConfigTableMetaData.prototype.activateFirstSection = function() {
    if (this.hasSections()) {
        this.activateSection(this.sections[0].id);
    }
};

ConfigTableMetaData.prototype.activeSectionCount = function() {
    var activeSectionCount = 0;
    if (this.hasSections()) {
        for (var i = 0; i < this.sections.length; i++) {
            var section = this.sections[i];
            if (section.activator.hasClass('active')) {
                activeSectionCount++;
            }
        }
    }
    return activeSectionCount;
};

ConfigTableMetaData.prototype.showSection = function(section) {
    if (typeof section === 'string') {
        section = this.getSection(section);
    }

    if (section) {
        var $ = jQD.getJQuery();

        // Deactivate currently active section ...
        this.deactivateActiveSection();

        // Active the specified section
        section.activator.addClass('active');
        this.topRows.filter('.' + section.id).addClass('active').show();

        // Hide the section header row. No need for it now because the
        // tab text acts as the section label.
        $('.section-header-row').hide();

        // and always show the buttons
        this.topRows.filter('.config_buttons').show();

        // Update the row-set visibility
        section.updateRowSetVisibility();
        section.highlightText(this.findInput.val());

        fireListeners(this.showListeners, section);
    }
};

ConfigTableMetaData.prototype.deactivateActiveSection = function() {
    var $ = jQD.getJQuery();

    $('.config-section-activator.active', this.activatorContainer).removeClass('active');
    this.topRows.filter('.active').removeClass('active');
    this.topRows.hide();
};

ConfigTableMetaData.prototype.onShowSection = function(listener) {
    this.showListeners.push(listener);
};

ConfigTableMetaData.prototype.showSections = function(withText) {
    if (withText === '') {
        if (this.hasSections()) {
            for (var i1 = 0; i1 < this.sections.length; i1++) {
                this.sections[i1].activator.show();
            }
            var activeSection = this.activeSection();
            if (!activeSection) {
                this.showSection(this.sections[0]);
            } else {
                activeSection.highlightText(this.findInput.val());
            }
        }
    } else {
        if (this.hasSections()) {
            var $ = jQD.getJQuery();
            var selector = ":containsci('" + withText + "')";
            var sectionsWithText = [];

            for (var i2 = 0; i2 < this.sections.length; i2++) {
                var section = this.sections[i2];
                var containsText = false;

                for (var i3 = 0; i3
 0) {
                        containsText = true;
                        break;
                    }
                }

                if (containsText) {
                    sectionsWithText.push(section);
                } else {
                    section.activator.hide();
                }
            }

            // Select the first section to contain the text.
            if (sectionsWithText.length > 0) {
                this.showSection(sectionsWithText[0]);
            } else {
                this.deactivateActiveSection();
            }
        }
    }
};

function fireListeners(listeners, contextObject) {
    for (var i = 0; i < listeners.length; i++) {
        fireListener(listeners[i], contextObject);
    }
    function fireListener(listener, contextObject) {
        setTimeout(function() {
            listener.call(contextObject);
        }, 1);
    }
}<|endoftext|>


<commit_before>var mbaasApi = require('fh-mbaas-api');
var express = require('express');
var mbaasExpress = mbaasApi.mbaasExpress();
var cors = require('cors');
var bodyParser = require('body-parser');

var app = express();

// Enable CORS for all requests
app.use(cors());

// Note: the order which we add middleware to Express here is important!
app.use('/sys', mbaasExpress.sys([]));
app.use('/mbaas', mbaasExpress.mbaas);

// allow serving of static files from the public directory
app.use(express.static(__dirname + '/public'));

// Note: important that this is added just before your own Routes
app.use(mbaasExpress.fhmiddleware());

app.get('/list/:session', function(req,res) {
  mbaasApi.db({
          "act": "list",
          "type": 'accountSession',
          "eq":{"session":req.params.session}
  }, function(err, data) {
    if (err) {
     res.end('Boo! ' + err);
    } else {
      var accountId = data.list[0].fields.accountId;
      mbaasApi.db({
          "act": "list",
          "type": 'account',
          "eq":{"sub":accountId}
      }, function(err, data2) {
        if (err) {
        res.end('Boo! ' + err);
        } else {
          res.end('Yay! ' + JSON.stringify(data2));
        }
      })
    }
  });
  });

app.get('/list', function(req,res) {
  mbaasApi.db({
          "act": "list",
          "type": 'accountSession'
  }, function(err, noterr) {
    if (err) {
     res.end('Boo! ' + err);
    } else {
      res.end('OOB! ' + JSON.stringify(noterr));
    }
  });
  });

app.get('/drop', function(req,res) {
  mbaasApi.db({
          "act": "deleteall",
          "type": 'account'
  }, function(err, noterr) {
    if (err) {
     res.end('Boo! ' + err);
    } else {
      res.end('OOB! ' + JSON.stringify(noterr));
    }
  });
  });

app.use('/hello', require('./lib/hello.js')());
app.use(bodyParser());

app.use('/auth', require('./lib/auth.js')());

// Important that this is last!
app.use(mbaasExpress.errorHandler());

var port = process.env.FH_PORT || process.env.OPENSHIFT_NODEJS_PORT || 8001;
var host = process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0';
app.listen(port, host, function() {
  console.log("App started at: " + new Date() + " on port: " + port);
});
<commit_msg>update file /application.js<commit_after>var mbaasApi = require('fh-mbaas-api');
var express = require('express');
var mbaasExpress = mbaasApi.mbaasExpress();
var cors = require('cors');
var bodyParser = require('body-parser');

var app = express();

// Enable CORS for all requests
app.use(cors());

// Note: the order which we add middleware to Express here is important!
app.use('/sys', mbaasExpress.sys([]));
app.use('/mbaas', mbaasExpress.mbaas);

// allow serving of static files from the public directory
app.use(express.static(__dirname + '/public'));

// Note: important that this is added just before your own Routes
app.use(mbaasExpress.fhmiddleware());

app.get('/list/:session', function(req,res) {
  mbaasApi.db({
          "act": "list",
          "type": 'accountSession',
          "eq":{"session":req.params.session}
  }, function(err, data) {
    if (err) {
     res.end('Boo! ' + err);
    } else {
      var accountId = data.list[0].fields.accountId;
      mbaasApi.db({
          "act": "list",
          "type": 'account',
          "eq":{"sub":accountId}
      }, function(err, data2) {
        if (err) {
          res.end(err);
        } else {
          res.end(JSON.stringify(data2));
        }
      })
    }
  });
  });

app.get('/list', function(req,res) {
  mbaasApi.db({
          "act": "list",
          "type": 'accountSession'
  }, function(err, noterr) {
    if (err) {
     res.end('Boo! ' + err);
    } else {
      res.end('OOB! ' + JSON.stringify(noterr));
    }
  });
  });

app.get('/drop', function(req,res) {
  mbaasApi.db({
          "act": "deleteall",
          "type": 'account'
  }, function(err, noterr) {
    if (err) {
     res.end('Boo! ' + err);
    } else {
      res.end('OOB! ' + JSON.stringify(noterr));
    }
  });
  });

app.use('/hello', require('./lib/hello.js')());
app.use(bodyParser());

app.use('/auth', require('./lib/auth.js')());

// Important that this is last!
app.use(mbaasExpress.errorHandler());

var port = process.env.FH_PORT || process.env.OPENSHIFT_NODEJS_PORT || 8001;
var host = process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0';
app.listen(port, host, function() {
  console.log("App started at: " + new Date() + " on port: " + port);
});
<|endoftext|>