<commit_before>/**
 * QueueItem
 * https://github.com/Camme/webdriverjs
 *
 * @author Camilo Tapia <camilo.tapia@gmail.com>
 *
 */

var QueueItem = module.exports = function(commandName, method, scope, args) {

    'use strict';

    this.children = [];
    this.commandName = commandName;
    this.method = method;
    this.scope = scope;
    this.currentChildIndex = 0;
    this.isDone = false;
    this.args = [];

    var self = this,
        hasCallback = false,
        nextCallback = function(method, methodName) {
            return function(err,result) {

                function errorCB() {
                    self.next();
                }

                process.on('uncaughtexception', errorCB);

                // call the callback
                method.call(scope, err, result);

                // call the next item
                self.next();

                // remove the listener to avoid memoryleakes. but do it after self.next so we know everything went well
                process.removeListener('uncaughtexception', errorCB);

            };
        };

    // change callback
    for(var i = 0, ii = args.length; i < ii; i++) {
        var currentArg = args[i];
        if (typeof currentArg == "function" && i == (args.length - 1)) {
            hasCallback = true;
            this.args.push(nextCallback(currentArg, commandName));
        } else {
            this.args.push(currentArg);
        }
    }

    if (!hasCallback) {
        this.args.push((function(){
            return function() {
                // continue queue after callback
                self.next();
            };
        })());
    }

    // queue item run command
    this.run = function() {

        // switch to the current queue item to make future addings to the correct queue item
        this.scope.currentQueueItem = this;

        // save the current length in case new items are added
        var currentLength = this.children.length;

        // run the command
        this.method.apply(this.scope, this.args);

        // if the command added new items to the queue, we make sure we run those commans
        if (currentLength < this.children.length) {
            this.next();
        }
    };

    // add queue item to the current item
    this.add = function(item)
    {

        // make a reference to its parent so we can travel back
        item.parent = this;

        // add the new item to this childrens list
        this.children.push(item);

        // if we arent running, its time now
        if (!this.scope.queueIsRunning) {
            // make sure we switch the running flag so that we dont run .next again when a new item is added.
            this.scope.queueIsRunning = true;

            // begin the que
            this.next();
        }
    };

    // go to next queu item
    this.next = function() {
        // if we have more children, run the next
        // otherwise tell the item we are done
        if (this.currentChildIndex < this.children.length) {
            var runIndex = this.currentChildIndex;

            // increase currentChildIndex first in case the command callback has no asynchronicity (e.g. call)
            this.currentChildIndex++;
            this.children[runIndex].run();
        } else {
            this.done();
        }
    };

    // the done method has to check if we are done for real, ie all children are done.
    // if not, we check what children are left to run
    this.done = function(scope) {

        if (!this.isDone) {
            // get the next undone child
            var checkDoneChildren = this.getNextChildToRun();

            // if its null, we know all children are done and we dont need to go further
            if (checkDoneChildren === null) {
                // mark this as done
                this.isDone = true;

                // if we have a parent, run its next command, otherwise we are in the root and are totally finished
                if (this.parent) {
                    this.parent.next();
                } else {
                    // and if we are finished we can turn off the running flag
                    this.scope.queueIsRunning = false;
                }
            }
        } else {
            // if we are done, we when wheter everything in the queue is done. if so, set the running flag to false
            var nextToRun = this.scope.currentQueueItem.getNextChildToRun();
            if (nextToRun === null) {
                this.scope.queueIsRunning = false;
            }
        }
    };

    // recursive function to get the next undone item
    this.getNextChildToRun = function()
    {
        var child = null;
        for(var i = 0, ii = this.children.length; i < ii; i++) {

            if (this.children[i] && !this.children[i].isDone) {
                return this.children[i];
            } else {
                child = this.children[i].getNextChildToRun();
            }

        }

        return child;
    };
};
<commit_msg>Fixed a problem where uncaught exceptions were not caught properly.<commit_after>/**
 * QueueItem
 * https://github.com/Camme/webdriverjs
 *
 * @author Camilo Tapia <camilo.tapia@gmail.com>
 *
 */

var QueueItem = module.exports = function(commandName, method, scope, args) {

    'use strict';

    this.children = [];
    this.commandName = commandName;
    this.method = method;
    this.scope = scope;
    this.currentChildIndex = 0;
    this.isDone = false;
    this.args = [];

    var self = this,
        hasCallback = false,
        nextCallback = function(method, methodName) {
            return function(err,result) {

                function errorCB() {
                    self.next();
                }

                process.on('uncaughtException', errorCB);

                // call the callback
                method.call(scope, err, result);

                // call the next item
                self.next();

                // remove the listener to avoid memoryleakes. but do it after self.next so we know everything went well
                process.removeListener('uncaughtException', errorCB);

            };
        };

    // change callback
    for(var i = 0, ii = args.length; i < ii; i++) {
        var currentArg = args[i];
        if (typeof currentArg == "function" && i == (args.length - 1)) {
            hasCallback = true;
            this.args.push(nextCallback(currentArg, commandName));
        } else {
            this.args.push(currentArg);
        }
    }

    if (!hasCallback) {
        this.args.push((function(){
            return function() {
                // continue queue after callback
                self.next();
            };
        })());
    }

    // queue item run command
    this.run = function() {

        // switch to the current queue item to make future addings to the correct queue item
        this.scope.currentQueueItem = this;

        // save the current length in case new items are added
        var currentLength = this.children.length;

        // run the command
        this.method.apply(this.scope, this.args);

        // if the command added new items to the queue, we make sure we run those commans
        if (currentLength < this.children.length) {
            this.next();
        }
    };

    // add queue item to the current item
    this.add = function(item)
    {

        // make a reference to its parent so we can travel back
        item.parent = this;

        // add the new item to this childrens list
        this.children.push(item);

        // if we arent running, its time now
        if (!this.scope.queueIsRunning) {
            // make sure we switch the running flag so that we dont run .next again when a new item is added.
            this.scope.queueIsRunning = true;

            // begin the que
            this.next();
        }
    };

    // go to next queu item
    this.next = function() {
        // if we have more children, run the next
        // otherwise tell the item we are done
        if (this.currentChildIndex < this.children.length) {
            var runIndex = this.currentChildIndex;

            // increase currentChildIndex first in case the command callback has no asynchronicity (e.g. call)
            this.currentChildIndex++;
            this.children[runIndex].run();
        } else {
            this.done();
        }
    };

    // the done method has to check if we are done for real, ie all children are done.
    // if not, we check what children are left to run
    this.done = function(scope) {

        if (!this.isDone) {
            // get the next undone child
            var checkDoneChildren = this.getNextChildToRun();

            // if its null, we know all children are done and we dont need to go further
            if (checkDoneChildren === null) {
                // mark this as done
                this.isDone = true;

                // if we have a parent, run its next command, otherwise we are in the root and are totally finished
                if (this.parent) {
                    this.parent.next();
                } else {
                    // and if we are finished we can turn off the running flag
                    this.scope.queueIsRunning = false;
                }
            }
        } else {
            // if we are done, we when wheter everything in the queue is done. if so, set the running flag to false
            var nextToRun = this.scope.currentQueueItem.getNextChildToRun();
            if (nextToRun === null) {
                this.scope.queueIsRunning = false;
            }
        }
    };

    // recursive function to get the next undone item
    this.getNextChildToRun = function()
    {
        var child = null;
        for(var i = 0, ii = this.children.length; i < ii; i++) {

            if (this.children[i] && !this.children[i].isDone) {
                return this.children[i];
            } else {
                child = this.children[i].getNextChildToRun();
            }

        }

        return child;
    };
};
<|endoftext|>


<commit_before>'use strict';

var angular = require('angular');

var app = angular.module('formulate');

/**
 * @description regular expressing validator for angularjs ngModel $validator object
 *
 *
 * @param validation
 * @returns {Function} accepts value to validate
 *
 */
function regexValidation(validation) {
    var regex = new RegExp(validation.configuration.pattern);

    return function (value) {
        return regex.test(value || "");
    };
}

function mandatoryValidation() {
    return function (value) {
        if (angular.isArray(value)) {
            return value.length > 0;
        }

        return !!value;
    };
}

function validationFactory(validation) {
    switch (validation.validationType) {
    case 'regex':
        return regexValidation(validation);
    default:
        return mandatoryValidation();
    }
}

function formulateValidation() {
    /*jslint unparam: true */
    function link(scope, el, attrs, ctrls) {
        var ngModelCtrl = ctrls[0];
        var formCtrl = ctrls[1];
        var fieldCtrl = scope.fieldCtrl;

        function setValidation(validation) {
            ngModelCtrl.$validators[validation.id] = validationFactory(validation);
        }

        // Setup validations
        if (angular.isArray(fieldCtrl.validations)) {
            fieldCtrl.validations.forEach(setValidation);
        }

        // Register form control
        formCtrl.$addControl(ngModelCtrl);
    }

    return {
        restrict: 'A',
        require: ['ngModel', '^^form'],
        link: link
    };
}
app.directive('formulateValidation', formulateValidation);<commit_msg>Expose custom validation factory<commit_after>'use strict';

var angular = require('angular');

var app = angular.module('formulate');

/**
 * @description regular expressing validator for angularjs ngModel $validator object
 *
 *
 * @param validation
 * @returns {Function} accepts value to validate
 *
 */
function regexValidation(validation) {
    var regex = new RegExp(validation.configuration.pattern);

    return function (value) {
        return regex.test(value || "");
    };
}

function mandatoryValidation() {
    return function (value) {
        if (angular.isArray(value)) {
            return value.length > 0;
        }

        return !!value;
    };
}

function validationFactory(validation) {
    switch (validation.validationType) {
    case 'regex':
        return regexValidation(validation);
    default:
        return mandatoryValidation();
    }
}
app.factory('formulateValidationFactory', function () {
    return {
        getInstance: validationFactory
    };
});

function formulateValidation() {
    /*jslint unparam: true */
    function link(scope, el, attrs, ctrls) {
        var ngModelCtrl = ctrls[0];
        var formCtrl = ctrls[1];
        var fieldCtrl = scope.fieldCtrl;

        function setValidation(validation) {
            ngModelCtrl.$validators[validation.id] = validationFactory(validation);
        }

        // Setup validations
        if (angular.isArray(fieldCtrl.validations)) {
            fieldCtrl.validations.forEach(setValidation);
        }

        // Register form control
        formCtrl.$addControl(ngModelCtrl);
    }

    return {
        restrict: 'A',
        require: ['ngModel', '^^form'],
        link: link
    };
}
app.directive('formulateValidation', formulateValidation);<|endoftext|>


<commit_before>'use strict';

const helper = require('./helper');

// this function should include commands that create indexes (if any)
// for any collections that the service may be using

// it should always return a promise
module.exports = function() {

  // SAMPLE CODE
  return helper.getCollection('decks').then((decks) => {
    return decks.createIndexes([
      { key: {'revisions.contentItems.ref.id': 1} },
      { key: {'revisions.contentItems.ref.revision': 1} },
      { key: {'revisions.contentItems.kind': 1} },
    ]);
  });

};
<commit_msg>SWIK-1667 create indexes<commit_after>'use strict';

const helper = require('./helper');

// this function should include commands that create indexes (if any)
// for any collections that the service may be using

// it should always return a promise
module.exports = function() {

  return helper.getCollection('discussions').then((decks) => {
    return decks.createIndexes([
      { key: {'content_kind': 1, 'content_id': 1} }
    ]);
  });

};
<|endoftext|>


<commit_before><commit_msg>Update ogario.v4.js<commit_after><|endoftext|>


<commit_before>//= require jquery
//= require stylr/prettify/prettify.js

// TODO: Refactor me!!!!

var Stylr = {
  dropdownButtonSelector: '.dropdown-button'
};

(function($) {

  $(function() {

    Stylr.closeDropdown = function(dropdown) {
      $(dropdown).removeClass('open');
      return dropdown;
    };

    Stylr.toggleDropdown = function(container, dropdown) {
      var height   = $(container).outerHeight(true);
      var dropdown = $(dropdown);

      $(Stylr.dropdownButtonSelector).find('> ul').each(function(i, e) {
        if (!$(e).is(dropdown)) {
          $(e).removeClass('open');
        }
      });

      if (!dropdown.is(':visible')) $('body').click();

      dropdown.toggleClass('open');
      dropdown.css('top', height);

      return dropdown;
    };

    // Code pretty printing
    // ====================

    prettyPrint();

    // Dropdown Navigation
    // ===================

    $('body').live('click', function (e) {
      Stylr.closeDropdown($('.dropdown > a').next('ul'));
      return true;
    });

    $('.dropdown > a').live('click', function (e) {
      Stylr.toggleDropdown($(this), $(this).next('ul'));
      return false;
    });

    // Dropdown Buttons
    // ================

    $('body').live('click', function(e) {
      Stylr.closeDropdown($(Stylr.dropdownButtonSelector).find('> ul'));
      return true;
    });

    $(Stylr.dropdownButtonSelector).live('click', function(e) {
      Stylr.toggleDropdown($(this), $(this).find('> ul'));
      return false;
    });

    // Alerts
    // ======

    $('.alert > a.close').live('click', function() {
      $(this).parent().remove();
      return false;
    });
  });
})(jQuery);
<commit_msg>live is deprecated<commit_after>//= require jquery
//= require stylr/prettify/prettify.js

// TODO: Refactor me!!!!

var Stylr = {
  dropdownButtonSelector: '.dropdown-button'
};

(function($) {

  $(function() {

    Stylr.closeDropdown = function(dropdown) {
      $(dropdown).removeClass('open');
      return dropdown;
    };

    Stylr.toggleDropdown = function(container, dropdown) {
      var height   = $(container).outerHeight(true);
      var dropdown = $(dropdown);

      $(Stylr.dropdownButtonSelector).find('> ul').each(function(i, e) {
        if (!$(e).is(dropdown)) {
          $(e).removeClass('open');
        }
      });

      if (!dropdown.is(':visible')) $('body').click();

      dropdown.toggleClass('open');
      dropdown.css('top', height);

      return dropdown;
    };

    // Code pretty printing
    // ====================

    prettyPrint();

    // Dropdown Navigation
    // ===================

    $(document).on('click', 'body', function (e) {
      Stylr.closeDropdown($('.dropdown > a').next('ul'));
      return true;
    });

    $('.dropdown > a').live('click', function (e) {
      Stylr.toggleDropdown($(this), $(this).next('ul'));
      return false;
    });

    // Dropdown Buttons
    // ================

    $(document).on('click', 'body', function(e) {
      Stylr.closeDropdown($(Stylr.dropdownButtonSelector).find('> ul'));
      return true;
    });

    $(document).on('click', Stylr.dropdownButtonSelector, function(e) {
      Stylr.toggleDropdown($(this), $(this).find('> ul'));
      return false;
    });

    // Alerts
    // ======

    $(document).on('click', '.alert > a.close', function() {
      $(this).parent().remove();
      return false;
    });
  });
})(jQuery);
<|endoftext|>


<commit_before>/**
 * Created by Lijingjing on 15/12/16.
 */
'use strict';

angular.module("myApp.reservation", ['ngRoute', 'smart-table', 'ui-notification', 'ngDialog'])
    .config(['$routeProvider', function($routeProvider) {
        $routeProvider.when('/reservation', {
            templateUrl: 'view_reservation/reservation.html',
            controller: 'ReservationCtrl'
        });
    }])
    .controller("ReservationCtrl", ["$scope", "Notification", "ngDialog", function ($scope, Notification, ngDialog) {
        var orderNum = [, 'SH00002', 'SH00003', 'SH00004'];
        var orderState = ['未完成', '未完成', '未完成'];
        var orderDates = ['1987-05-21', '1987-04-25', '1955-08-27', '1966-06-06'];
        var id = 1;

        var items = [
            {
                orderNum: 'SH00001',
                orderState: '未完成',
                orderDate: '2015-12-17 14:28',
                orderTime: '晚餐',
                orderInfo: '李菁菁,女,15601861921,白金会员',
                orderPeopleNum: '3',
                orderAmount: '300',
                orderRemark: "",
                orderHandle: false
            },
            {
                orderNum: 'SH00002',
                orderState: '未完成',
                orderDate: '2015-12-17 14:29',
                orderTime: '晚餐',
                orderInfo: '李菁菁,女,15601861921,白金会员',
                orderPeopleNum: '3',
                orderAmount: '300',
                orderRemark: "",
                orderHandle: false
            },
            {
                orderNum: 'SH00003',
                orderState: '已完成',
                orderDate: '2015-12-17 14:30',
                orderTime: '晚餐',
                orderInfo: '李菁菁,女,15601861921,白金会员',
                orderPeopleNum: '3',
                orderAmount: '300',
                orderRemark: "",
                orderHandle: true
            }
        ];

        $scope.rowCollection = items;

        //copy the references (you could clone ie angular.copy but then have to go through a dirty checking for the matches)
        $scope.displayedCollection = [].concat($scope.rowCollection);

        //remove to the real data holder
        $scope.removeItem = function removeItem(row) {
            alert(JSON.stringify(row));
            var index = $scope.rowCollection.indexOf(row);
            if (index !== -1) {
                $scope.rowCollection.splice(index, 1);
            }
        };

        $scope.notifications = [];
        $scope.notificationNum = 1;
        $scope.close = function () {
            console.log($scope.notifications.length);
            $scope.notifications[$scope.notifications.length - 1].then(function (notification) {
                notification.kill(true);
                $scope.notificationNum--;
                $scope.notifications.splice($scope.notifications.length - 1, 1)
            });
        };
        $scope.notify = function () {
            var newScope = $scope.$new(true);
            //alertScope.title = 'Hello';
            newScope.name = "李菁菁" + $scope.notificationNum;
            newScope.phone = "156——" + $scope.notificationNum;
            newScope.level = "白金" + $scope.notificationNum;
            newScope.date = "2015-12-22&" + $scope.notificationNum;
            newScope.time = "18:15&" + $scope.notificationNum;
            newScope.peopleNum = "3";
            newScope.money = "300";
            newScope.remark = "测试";
            newScope.cancel = function(){
                console.log("id"+$scope.notificationNum);
                //发送请求,将订单ID作为参数,取消订单的接口
                this.closeThisDialog();
                newScope.notification.then(function (notification) {
                    notification.kill(true);
                });
            };

            newScope.showConfirm = function () {
                ngDialog.open({
                    template: 'templateId',
                    scope: newScope
                });
            };

            newScope.notification = Notification.warning({
                message: "test" + $scope.notificationNum,
                templateUrl: "custom_template.html",
                scope: newScope,
                delay: null
            });

            $scope.notificationNum++;
        };
    }]);<commit_msg>IE8兼容<commit_after>/**
 * Created by Lijingjing on 15/12/16.
 */
'use strict';

angular.module("myApp.reservation", ['ngRoute', 'smart-table', 'ui-notification', 'ngDialog'])
    .config(['$routeProvider', function($routeProvider) {
        $routeProvider.when('/reservation', {
            templateUrl: 'view_reservation/reservation.html',
            controller: 'ReservationCtrl'
        });
    }])
    .controller("ReservationCtrl", ["$scope", "Notification", "ngDialog", function ($scope, Notification, ngDialog) {
        var orderNum = [, 'SH00002', 'SH00003', 'SH00004'];
        var orderState = ['未完成', '未完成', '未完成'];
        var orderDates = ['1987-05-21', '1987-04-25', '1955-08-27', '1966-06-06'];
        var id = 1;

        var items = [
            {
                orderNum: 'SH00001',
                orderState: '未完成',
                orderDate: '2015-12-17 14:28',
                orderTime: '晚餐',
                orderInfo: '李菁菁,女,15601861921,白金会员',
                orderPeopleNum: '3',
                orderAmount: '300',
                orderRemark: "",
                orderHandle: false
            },
            {
                orderNum: 'SH00002',
                orderState: '未完成',
                orderDate: '2015-12-17 14:29',
                orderTime: '晚餐',
                orderInfo: '李菁菁,女,15601861921,白金会员',
                orderPeopleNum: '3',
                orderAmount: '300',
                orderRemark: "",
                orderHandle: false
            },
            {
                orderNum: 'SH00003',
                orderState: '已完成',
                orderDate: '2015-12-17 14:30',
                orderTime: '晚餐',
                orderInfo: '李菁菁,女,15601861921,白金会员',
                orderPeopleNum: '3',
                orderAmount: '300',
                orderRemark: "",
                orderHandle: true
            }
        ];

        $scope.rowCollection = items;

        //copy the references (you could clone ie angular.copy but then have to go through a dirty checking for the matches)
        $scope.displayedCollection = [].concat($scope.rowCollection);

        //remove to the real data holder
        $scope.removeItem = function removeItem(row) {
            alert(JSON.stringify(row));
            var index = $scope.rowCollection.indexOf(row);
            if (index !== -1) {
                $scope.rowCollection.splice(index, 1);
            }
        };

        $scope.notifications = [];
        $scope.notificationNum = 1;
        $scope.close = function () {
            console.log($scope.notifications.length);
            $scope.notifications[$scope.notifications.length - 1].then(function (notification) {
                notification.kill(true);
                $scope.notificationNum--;
                $scope.notifications.splice($scope.notifications.length - 1, 1)
            });
        };
        $scope.notify = function () {
            var newScope = $scope.$new(true);
            //alertScope.title = 'Hello';
            newScope.name = "李菁菁" + $scope.notificationNum;
            newScope.phone = "156——" + $scope.notificationNum;
            newScope.level = "白金" + $scope.notificationNum;
            newScope.date = "2015-12-22&" + $scope.notificationNum;
            newScope.time = "18:15&" + $scope.notificationNum;
            newScope.peopleNum = "3";
            newScope.money = "300";
            newScope.remark = "测试";
            newScope.cancel = function(){
                //console.log("id"+$scope.notificationNum);
                //发送请求,将订单ID作为参数,取消订单的接口
                this.closeThisDialog();
                newScope.notification.then(function (notification) {
                    notification.kill(true);
                });
            };

            newScope.showConfirm = function () {
                ngDialog.open({
                    template: 'templateId',
                    scope: newScope
                });
            };

            newScope.notification = Notification.warning({
                message: "test" + $scope.notificationNum,
                templateUrl: "custom_template.html",
                scope: newScope,
                delay: null
            });

            $scope.notificationNum++;
        };
    }]);<|endoftext|>


<commit_before>const HELP_GROUP = 'Help options:';
const CONFIG_GROUP = 'Config options:';
const BASIC_GROUP = 'Basic options:';
const OUTPUT_GROUP = 'Output options:';
const ADVANCED_GROUP = 'Advanced options:';
const DISPLAY_GROUP = 'Stats options:';

module.exports = {
    commands: [
        {
            name: 'create',
            alias: 'c',
            type: String,
            usage: 'create | create <scaffold>',
            description: 'Initialize a new webpack configuration',
        },
        {
            name: 'migrate',
            alias: 'm',
            type: String,
            usage: 'migrate',
            description: 'Migrate a configuration to a new version',
        },
        {
            name: 'loader',
            scope: 'external',
            alias: 'l',
            type: String,
            usage: 'loader',
            description: 'Scaffold a loader repository',
        },
        {
            name: 'plugin',
            alias: 'p',
            scope: 'external',
            type: String,
            usage: 'plugin',
            description: 'Scaffold a plugin repository',
        },
        {
            name: 'info',
            scope: 'external',
            type: String,
            usage: 'info [options] [output-format]',
            description: 'Outputs information about your system and dependencies',
            flags: [
                {
                    name: 'output-json',
                    type: Boolean,
                    group: OUTPUT_GROUP,
                    description: 'To get the output as JSON',
                },
                {
                    name: 'output-markdown',
                    type: Boolean,
                    group: OUTPUT_GROUP,
                    description: 'To get the output as markdown',
                },
                {
                    name: 'help',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Shows help',
                },
                {
                    name: 'version',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Show version number of webpack-cli',
                },
                {
                    name: 'system',
                    alias: 's',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'System information ( OS, CPU )',
                },
                {
                    name: 'binaries',
                    alias: 'b',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Installed binaries (Node, yarn, npm)',
                },
                {
                    name: 'browsers',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Installed web browsers',
                },
                {
                    name: 'npmg',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Globally installed NPM packages ( webpack & webpack-cli only )',
                },
                {
                    name: 'npmPackages',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Info about packages related to webpack installed in the project',
                },
            ],
        },
        {
            name: 'serve',
            scope: 'external',
            type: String,
            usage: 'serve',
            description: 'Run the webpack Dev Server',
        },
    ],
    core: [
        {
            name: 'entry',
            type: String,
            multiple: false,
            defaultValue: null,
            defaultOption: true,
            group: BASIC_GROUP,
            description: 'The entry point of your application',
        },
        {
            name: 'config',
            alias: 'c',
            type: String,
            defaultValue: null,
            group: CONFIG_GROUP,
            description: 'Provide path to a webpack configuration file',
        },
        {
            name: 'merge',
            alias: 'm',
            type: String,
            group: CONFIG_GROUP,
            description: 'Merge a configuration file',
        },
        {
            name: 'progress',
            type: Boolean,
            group: BASIC_GROUP,
            description: 'Print compilation progress during build',
        },
        {
            name: 'silent',
            type: Boolean,
            group: DISPLAY_GROUP,
            description: 'Disable any output that webpack makes',
        },
        {
            name: 'help',
            type: Boolean,
            group: HELP_GROUP,
            description: 'Outputs list of supported flags',
        },
        {
            name: 'defaults',
            type: Boolean,
            group: BASIC_GROUP,
            description: 'Allow webpack to set defaults aggresively',
        },
        {
            name: 'output',
            alias: 'o',
            group: OUTPUT_GROUP,
            type: String,
            description: 'Output location of the file generated by webpack',
        },
        {
            name: 'plugin',
            group: ADVANCED_GROUP,
            type: String,
            description: 'Load a given plugin',
        },
        {
            name: 'global',
            alias: 'g',
            type: String,
            multiple: true,
            group: ADVANCED_GROUP,
            description: 'Declares and exposes a global variable',
        },
        {
            name: 'target',
            alias: 't',
            defaultValue: 'web',
            type: String,
            group: ADVANCED_GROUP,
            description: 'Sets the build target',
        },
        {
            name: 'watch',
            type: Boolean,
            alias: 'w',
            group: BASIC_GROUP,
            description: 'Watch for files changes',
        },
        {
            name: 'hot',
            alias: 'h',
            type: Boolean,
            group: ADVANCED_GROUP,
            description: 'Enables Hot Module Replacement',
        },
        {
            name: 'sourcemap',
            type: String,
            alias: 's',
            defaultValue: undefined,
            group: BASIC_GROUP,
            description: 'Determine source maps to use',
        },
        {
            name: 'prefetch',
            type: String,
            group: ADVANCED_GROUP,
            description: 'Prefetch this request',
        },
        {
            name: 'json',
            type: Boolean,
            alias: 'j',
            description: 'Prints result as JSON',
            group: DISPLAY_GROUP,
        },
        {
            name: 'dev',
            alias: 'd',
            type: Boolean,
            defaultValue: undefined,
            group: BASIC_GROUP,
            description: 'Run development build',
        },
        {
            name: 'prod',
            alias: 'p',
            type: Boolean,
            defaultValue: undefined,
            group: BASIC_GROUP,
            description: 'Run production build',
        },
        {
            name: 'version',
            type: Boolean,
            group: BASIC_GROUP,
            description: 'Get current version',
        },
        /* 		{
			name: "analyze",
			type: Boolean,
			group: BASIC_GROUP,
			description: "analyze build for performance improvements"
		}, */
        /* 		{
			name: "interactive",
			type: Boolean,
			alias: "i",
			description: "Use webpack interactively",
			group: BASIC_GROUP
		} */
    ],
};
<commit_msg>chore: Remove redundant multiple property<commit_after>const HELP_GROUP = 'Help options:';
const CONFIG_GROUP = 'Config options:';
const BASIC_GROUP = 'Basic options:';
const OUTPUT_GROUP = 'Output options:';
const ADVANCED_GROUP = 'Advanced options:';
const DISPLAY_GROUP = 'Stats options:';

module.exports = {
    commands: [
        {
            name: 'create',
            alias: 'c',
            type: String,
            usage: 'create | create <scaffold>',
            description: 'Initialize a new webpack configuration',
        },
        {
            name: 'migrate',
            alias: 'm',
            type: String,
            usage: 'migrate',
            description: 'Migrate a configuration to a new version',
        },
        {
            name: 'loader',
            scope: 'external',
            alias: 'l',
            type: String,
            usage: 'loader',
            description: 'Scaffold a loader repository',
        },
        {
            name: 'plugin',
            alias: 'p',
            scope: 'external',
            type: String,
            usage: 'plugin',
            description: 'Scaffold a plugin repository',
        },
        {
            name: 'info',
            scope: 'external',
            type: String,
            usage: 'info [options] [output-format]',
            description: 'Outputs information about your system and dependencies',
            flags: [
                {
                    name: 'output-json',
                    type: Boolean,
                    group: OUTPUT_GROUP,
                    description: 'To get the output as JSON',
                },
                {
                    name: 'output-markdown',
                    type: Boolean,
                    group: OUTPUT_GROUP,
                    description: 'To get the output as markdown',
                },
                {
                    name: 'help',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Shows help',
                },
                {
                    name: 'version',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Show version number of webpack-cli',
                },
                {
                    name: 'system',
                    alias: 's',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'System information ( OS, CPU )',
                },
                {
                    name: 'binaries',
                    alias: 'b',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Installed binaries (Node, yarn, npm)',
                },
                {
                    name: 'browsers',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Installed web browsers',
                },
                {
                    name: 'npmg',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Globally installed NPM packages ( webpack & webpack-cli only )',
                },
                {
                    name: 'npmPackages',
                    type: Boolean,
                    group: BASIC_GROUP,
                    description: 'Info about packages related to webpack installed in the project',
                },
            ],
        },
        {
            name: 'serve',
            scope: 'external',
            type: String,
            usage: 'serve',
            description: 'Run the webpack Dev Server',
        },
    ],
    core: [
        {
            name: 'entry',
            type: String,
            defaultValue: null,
            defaultOption: true,
            group: BASIC_GROUP,
            description: 'The entry point of your application',
        },
        {
            name: 'config',
            alias: 'c',
            type: String,
            defaultValue: null,
            group: CONFIG_GROUP,
            description: 'Provide path to a webpack configuration file',
        },
        {
            name: 'merge',
            alias: 'm',
            type: String,
            group: CONFIG_GROUP,
            description: 'Merge a configuration file',
        },
        {
            name: 'progress',
            type: Boolean,
            group: BASIC_GROUP,
            description: 'Print compilation progress during build',
        },
        {
            name: 'silent',
            type: Boolean,
            group: DISPLAY_GROUP,
            description: 'Disable any output that webpack makes',
        },
        {
            name: 'help',
            type: Boolean,
            group: HELP_GROUP,
            description: 'Outputs list of supported flags',
        },
        {
            name: 'defaults',
            type: Boolean,
            group: BASIC_GROUP,
            description: 'Allow webpack to set defaults aggresively',
        },
        {
            name: 'output',
            alias: 'o',
            group: OUTPUT_GROUP,
            type: String,
            description: 'Output location of the file generated by webpack',
        },
        {
            name: 'plugin',
            group: ADVANCED_GROUP,
            type: String,
            description: 'Load a given plugin',
        },
        {
            name: 'global',
            alias: 'g',
            type: String,
            multiple: true,
            group: ADVANCED_GROUP,
            description: 'Declares and exposes a global variable',
        },
        {
            name: 'target',
            alias: 't',
            defaultValue: 'web',
            type: String,
            group: ADVANCED_GROUP,
            description: 'Sets the build target',
        },
        {
            name: 'watch',
            type: Boolean,
            alias: 'w',
            group: BASIC_GROUP,
            description: 'Watch for files changes',
        },
        {
            name: 'hot',
            alias: 'h',
            type: Boolean,
            group: ADVANCED_GROUP,
            description: 'Enables Hot Module Replacement',
        },
        {
            name: 'sourcemap',
            type: String,
            alias: 's',
            defaultValue: undefined,
            group: BASIC_GROUP,
            description: 'Determine source maps to use',
        },
        {
            name: 'prefetch',
            type: String,
            group: ADVANCED_GROUP,
            description: 'Prefetch this request',
        },
        {
            name: 'json',
            type: Boolean,
            alias: 'j',
            description: 'Prints result as JSON',
            group: DISPLAY_GROUP,
        },
        {
            name: 'dev',
            alias: 'd',
            type: Boolean,
            defaultValue: undefined,
            group: BASIC_GROUP,
            description: 'Run development build',
        },
        {
            name: 'prod',
            alias: 'p',
            type: Boolean,
            defaultValue: undefined,
            group: BASIC_GROUP,
            description: 'Run production build',
        },
        {
            name: 'version',
            type: Boolean,
            group: BASIC_GROUP,
            description: 'Get current version',
        },
        /* 		{
			name: "analyze",
			type: Boolean,
			group: BASIC_GROUP,
			description: "analyze build for performance improvements"
		}, */
        /* 		{
			name: "interactive",
			type: Boolean,
			alias: "i",
			description: "Use webpack interactively",
			group: BASIC_GROUP
		} */
    ],
};
<|endoftext|>


<commit_before>/*
 * ***** BEGIN LICENSE BLOCK *****
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright (C) 2010-2014 Matthew Turnbull <sparky@bluefang-logic.com>. All Rights Reserved.
 *
 * ***** END LICENSE BLOCK *****
*/

"use strict";

const EXPORTED_SYMBOLS = ["S4EStatusService"];

const CU = Components.utils;

CU.import("resource://gre/modules/Services.jsm");
CU.import("resource://gre/modules/XPCOMUtils.jsm");

function S4EStatusService(window, service, getters)
{
	this._window = window;
	this._service = service;
	this._getters = getters;

	this._overLinkService = new S4EOverlinkService(this._window, this._service, this);
}

S4EStatusService.prototype =
{
	_window:                 null,
	_service:                null,
	_getters:                null,
	_overLinkService:        null,

	_overLink:               { val: "", type: "" },
	_network:                { val: "", type: "" },
	_networkXHR:             { val: "", type: "" },
	_status:                 { val: "", type: "" },
	_jsStatus:               { val: "", type: "" },
	_defaultStatus:          { val: "", type: "" },

	_isFullScreen:           false,
	_isFullScreenVideo:      false,

	_statusText:             { val: "", type: "" },
	_noUpdate:               false,
	_statusChromeTimeoutID:  0,
	_statusContentTimeoutID: 0,

	getCompositeStatusText: function()
	{
		return this._statusText.val;
	},

	getStatusText: function()
	{
		return this._status.val;
	},

	setNetworkStatus: function(status, busy)
	{
		if(busy)
		{
			this._network = { val: status, type: "network" };
			this._networkXHR = { val: "", type: "network xhr" };
		}
		else
		{
			this._networkXHR = { val: status, type: "network xhr" };
		}
		this.updateStatusField();
	},

	setStatusText: function(status)
	{
		this._status = { val: status, type: "status chrome" };
		this.updateStatusField();
	},

	setJSStatus: function(status)
	{
		this._jsStatus = { val: status, type: "status content" };
		this.updateStatusField();
	},

	setJSDefaultStatus: function(status)
	{
		// This was removed from Firefox in Bug 862917
	},

	setDefaultStatus: function(status)
	{
		this._defaultStatus = { val: status, type: "status chrome default" };
		this.updateStatusField();
	},

	setOverLink: function(link, aAnchor)
	{
		this._overLinkService.update(link, aAnchor);
	},

	setOverLinkInternal: function(link, aAnchor)
	{
		let status = this._service.status;
		let statusLinkOver = this._service.statusLinkOver;

		if(statusLinkOver)
		{
			link = link.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g, encodeURIComponent);
			if(this._getters.urlbar && this._getters.urlbar._mayTrimURLs)
			{
				link = this._window.trimURL(link);
			}

			if(status == statusLinkOver)
			{
				this._overLink = { val: link, type: "overLink", anchor: aAnchor };
				this.updateStatusField();
			}
			else
			{
				this.setStatusField(statusLinkOver, { val: link, type: "overLink", anchor: aAnchor }, true);
			}
		}
	},

	setNoUpdate: function(nu)
	{
		this._noUpdate = nu;
	},

	buildBinding: function() {
		let XULBWPropHandler = function(prop, oldval, newval) {
			CU.reportError("Attempt to modify XULBrowserWindow." + prop);
			return oldval;
		};

		["updateStatusField", "onStatusChange"].forEach(function(prop)
		{
			this._window.XULBrowserWindow.unwatch(prop);
			this._window.XULBrowserWindow[prop] = function() {};
			this._window.XULBrowserWindow.watch(prop, XULBWPropHandler);
		}, this);

		["getCompositeStatusText", "getStatusText", "setStatusText", "setJSStatus",
		"setJSDefaultStatus", "setDefaultStatus", "setOverLink"].forEach(function(prop)
		{
			this._window.XULBrowserWindow.unwatch(prop);
			this._window.XULBrowserWindow[prop] = this[prop].bind(this);
			this._window.XULBrowserWindow.watch(prop, XULBWPropHandler);
		}, this);

		let XULBWHandler = function(prop, oldval, newval) {
			if(!newval)
			{
				return newval;
			}
			CU.reportError("XULBrowserWindow changed. Updating S4E bindings.");
			this._window.setTimeout(function(self)
			{
				self.buildBinding();
			}, 0, this);
			return newval;
		};

		this._window.watch("XULBrowserWindow", XULBWHandler);
	},

	destroy: function()
	{
		// No need to unbind from the XULBrowserWindow, it's already null at this point

		this.clearTimer("_statusChromeTimeoutID");
		this.clearTimer("_statusContentTimeoutID");

		this._overLinkService.destroy();

		["_overLink", "_network", "_networkXHR", "_status", "_jsStatus", "_defaultStatus",
		"_statusText", "_window", "_service", "_getters", "_overLinkService"].forEach(function(prop)
		{
			delete this[prop];
		}, this);
	},

	buildTextOrder: function()
	{
		this.__defineGetter__("_textOrder", function()
		{
			let textOrder = ["_overLink"];
			if(this._service.statusNetwork)
			{
				textOrder.push("_network");
				if(this._service.statusNetworkXHR)
				{
					textOrder.push("_networkXHR");
				}
			}
			textOrder.push("_status", "_jsStatus");
			if(this._service.statusDefault)
			{
				textOrder.push("_defaultStatus");
			}

			delete this._textOrder;
			return this._textOrder = textOrder;
		});
	},

	updateStatusField: function(force)
	{
		let text = { val: "", type: "" };
		for(let i = 0; !text.val && i < this._textOrder.length; i++)
		{
			text = this[this._textOrder[i]];
		}

		if(this._statusText.val != text.val || force)
		{
			if(this._noUpdate)
			{
				return;
			}

			this._statusText = text;

			this.setStatusField(this._service.status, text, false);

			if(text.val && this._service.statusTimeout)
			{
				this.setTimer(text.type);
			}
		}
	},

	updateFullScreen: function()
	{
		this._isFullScreen = this._window.fullScreen;
		this._isFullScreenVideo = false;
		if(this._isFullScreen)
		{
			let fsEl = this._window.content.document.mozFullScreenElement;
			if(fsEl && (fsEl.nodeName == "VIDEO" || fsEl.getElementsByTagName("VIDEO").length > 0))
			{
				this._isFullScreenVideo = true;
			}
		}

		this.clearStatusField();
		this.updateStatusField(true);
	},

	setTimer: function(type)
	{
		let typeArgs = type.split(" ", 3);

		if(typeArgs.length < 2 || typeArgs[0] != "status")
		{
			return;
		}

		if(typeArgs[1] == "chrome")
		{
			this.clearTimer("_statusChromeTimeoutID");
			this._statusChromeTimeoutID = this._window.setTimeout(function(self, isDefault)
			{
				self._statusChromeTimeoutID = 0;
				if(isDefault)
				{
					self.clearStatusField();
				}
				else
				{
					self.setStatusText("");
				}
			}, this._service.statusTimeout, this, (typeArgs.length == 3 && typeArgs[2] == "default"));
		}
		else
		{
			this.clearTimer("_statusContentTimeoutID");
			this._statusContentTimeoutID = this._window.setTimeout(function(self)
			{
				self._statusContentTimeoutID = 0;
				self.setJSStatus("");
			}, this._service.statusTimeout, this);
		}
	},

	clearTimer: function(timerName)
	{
		if(this[timerName] != 0)
		{
			this._window.clearTimeout(this[timerName]);
			this[timerName] = 0;
		}
	},

	clearStatusField: function()
	{
		this._getters.statusOverlay.value = "";

		let status_label = this._getters.statusWidgetLabel;
		if(status_label)
		{
			status_label.value = "";
		}

		let urlbar = this._getters.urlbar;
		if(urlbar)
		{
			urlbar.setStatus("");
		}
	},

	setStatusField: function(location, text, allowTooltip)
	{
		if(!location)
		{
			return;
		}

		let label = null;

		if(this._isFullScreen && this._service.advancedStatusDetectFullScreen)
		{
			switch(location)
			{
				case 1: // Toolbar
					location = 3
					break;
				case 2: // URL bar
					if(Services.prefs.getBoolPref("browser.fullscreen.autohide"))
					{
						location = 3
					}
					break;
			}
		}

		switch(location)
		{
			case 1: // Toolbar
				label = this._getters.statusWidgetLabel;
				break;
			case 2: // URL Bar
				let urlbar = this._getters.urlbar;
				if(urlbar)
				{
					urlbar.setStatusType(text.type);
					urlbar.setStatus(text.val);
				}
				break;
			case 3: // Popup
			default:
				if(this._isFullScreenVideo && this._service.advancedStatusDetectVideo)
				{
					return;
				}
				label = this._getters.statusOverlay;
				break;
		}

		if(label)
		{
			label.setAttribute("previoustype", label.getAttribute("type"));
			label.setAttribute("type", text.type);
			label.value = text.val;
			label.setAttribute("crop", text.type == "overLink" ? "center" : "end");
		}
	}
};

function S4EOverlinkService(window, service, statusService) {
	this._window = window;
	this._service = service;
	this._statusService = statusService;
}

S4EOverlinkService.prototype =
{
	_window:        null,
	_service:       null,
	_statusService: null,

	_timer:         0,
	_currentLink:   { link: "", anchor: null },
	_pendingLink:   { link: "", anchor: null },
	_listening:     false,

	update: function(aLink, aAnchor)
	{
		this.clearTimer();
		this.stopListen();
		this._pendingLink = { link: aLink, anchor: aAnchor };

		if(!aLink)
		{
			if(this._window.XULBrowserWindow.hideOverLinkImmediately || !this._service.statusLinkOverDelayHide)
			{
				this._show();
			}
			else
			{
				this._showDelayed();
			}
		}
		else if(this._currentLink.link || !this._service.statusLinkOverDelayShow)
		{
			this._show();
		}
		else
		{
			this._showDelayed();
			this.startListen();
		}
	},

	destroy: function()
	{
		this.clearTimer();
		this.stopListen();

		["_currentLink", "_pendingLink",  "_statusService", "_window"].forEach(function(prop)
		{
			delete this[prop];
		}, this);
	},

	startListen: function()
	{
		if(!this._listening)
		{
			this._window.addEventListener("mousemove", this, true);
			this._listening = true;
		}
	},

	stopListen: function()
	{
		if(this._listening)
		{
			this._window.removeEventListener("mousemove", this, true);
			this._listening = false;
		}
	},

	clearTimer: function()
	{
		if(this._timer != 0)
		{
			this._window.clearTimeout(this._timer);
			this._timer = 0;
		}
	},

	handleEvent: function(event)
	{
		switch(event.type)
		{
			case "mousemove":
				this.clearTimer();
				this._showDelayed();
		}
	},

	_showDelayed: function()
	{
		let delay = ((this._pendingLink.link)
			? this._service.statusLinkOverDelayShow
			: this._service.statusLinkOverDelayHide);

		this._timer = this._window.setTimeout(function(self)
		{
			self._timer = 0;
			self._show();
			self.stopListen();
		}, delay, this);
	},

	_show: function()
	{
		this._currentLink = this._pendingLink;
		this._statusService.setOverLinkInternal(this._currentLink.link, this._currentLink.anchor);
	}
};

<commit_msg>Fix race condition clearing default status<commit_after>/*
 * ***** BEGIN LICENSE BLOCK *****
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright (C) 2010-2014 Matthew Turnbull <sparky@bluefang-logic.com>. All Rights Reserved.
 *
 * ***** END LICENSE BLOCK *****
*/

"use strict";

const EXPORTED_SYMBOLS = ["S4EStatusService"];

const CU = Components.utils;

CU.import("resource://gre/modules/Services.jsm");
CU.import("resource://gre/modules/XPCOMUtils.jsm");

function S4EStatusService(window, service, getters)
{
	this._window = window;
	this._service = service;
	this._getters = getters;

	this._overLinkService = new S4EOverlinkService(this._window, this._service, this);
}

S4EStatusService.prototype =
{
	_window:                 null,
	_service:                null,
	_getters:                null,
	_overLinkService:        null,

	_overLink:               { val: "", type: "" },
	_network:                { val: "", type: "" },
	_networkXHR:             { val: "", type: "" },
	_status:                 { val: "", type: "" },
	_jsStatus:               { val: "", type: "" },
	_defaultStatus:          { val: "", type: "" },

	_isFullScreen:           false,
	_isFullScreenVideo:      false,

	_statusText:             { val: "", type: "" },
	_noUpdate:               false,
	_statusChromeTimeoutID:  0,
	_statusContentTimeoutID: 0,

	getCompositeStatusText: function()
	{
		return this._statusText.val;
	},

	getStatusText: function()
	{
		return this._status.val;
	},

	setNetworkStatus: function(status, busy)
	{
		if(busy)
		{
			this._network = { val: status, type: "network" };
			this._networkXHR = { val: "", type: "network xhr" };
		}
		else
		{
			this._networkXHR = { val: status, type: "network xhr" };
		}
		this.updateStatusField();
	},

	setStatusText: function(status)
	{
		this._status = { val: status, type: "status chrome" };
		this.updateStatusField();
	},

	setJSStatus: function(status)
	{
		this._jsStatus = { val: status, type: "status content" };
		this.updateStatusField();
	},

	setJSDefaultStatus: function(status)
	{
		// This was removed from Firefox in Bug 862917
	},

	setDefaultStatus: function(status)
	{
		this._defaultStatus = { val: status, type: "status chrome default" };
		this.updateStatusField();
	},

	setOverLink: function(link, aAnchor)
	{
		this._overLinkService.update(link, aAnchor);
	},

	setOverLinkInternal: function(link, aAnchor)
	{
		let status = this._service.status;
		let statusLinkOver = this._service.statusLinkOver;

		if(statusLinkOver)
		{
			link = link.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g, encodeURIComponent);
			if(this._getters.urlbar && this._getters.urlbar._mayTrimURLs)
			{
				link = this._window.trimURL(link);
			}

			if(status == statusLinkOver)
			{
				this._overLink = { val: link, type: "overLink", anchor: aAnchor };
				this.updateStatusField();
			}
			else
			{
				this.setStatusField(statusLinkOver, { val: link, type: "overLink", anchor: aAnchor }, true);
			}
		}
	},

	setNoUpdate: function(nu)
	{
		this._noUpdate = nu;
	},

	buildBinding: function() {
		let XULBWPropHandler = function(prop, oldval, newval) {
			CU.reportError("Attempt to modify XULBrowserWindow." + prop);
			return oldval;
		};

		["updateStatusField", "onStatusChange"].forEach(function(prop)
		{
			this._window.XULBrowserWindow.unwatch(prop);
			this._window.XULBrowserWindow[prop] = function() {};
			this._window.XULBrowserWindow.watch(prop, XULBWPropHandler);
		}, this);

		["getCompositeStatusText", "getStatusText", "setStatusText", "setJSStatus",
		"setJSDefaultStatus", "setDefaultStatus", "setOverLink"].forEach(function(prop)
		{
			this._window.XULBrowserWindow.unwatch(prop);
			this._window.XULBrowserWindow[prop] = this[prop].bind(this);
			this._window.XULBrowserWindow.watch(prop, XULBWPropHandler);
		}, this);

		let XULBWHandler = function(prop, oldval, newval) {
			if(!newval)
			{
				return newval;
			}
			CU.reportError("XULBrowserWindow changed. Updating S4E bindings.");
			this._window.setTimeout(function(self)
			{
				self.buildBinding();
			}, 0, this);
			return newval;
		};

		this._window.watch("XULBrowserWindow", XULBWHandler);
	},

	destroy: function()
	{
		// No need to unbind from the XULBrowserWindow, it's already null at this point

		this.clearTimer("_statusChromeTimeoutID");
		this.clearTimer("_statusContentTimeoutID");

		this._overLinkService.destroy();

		["_overLink", "_network", "_networkXHR", "_status", "_jsStatus", "_defaultStatus",
		"_statusText", "_window", "_service", "_getters", "_overLinkService"].forEach(function(prop)
		{
			delete this[prop];
		}, this);
	},

	buildTextOrder: function()
	{
		this.__defineGetter__("_textOrder", function()
		{
			let textOrder = ["_overLink"];
			if(this._service.statusNetwork)
			{
				textOrder.push("_network");
				if(this._service.statusNetworkXHR)
				{
					textOrder.push("_networkXHR");
				}
			}
			textOrder.push("_status", "_jsStatus");
			if(this._service.statusDefault)
			{
				textOrder.push("_defaultStatus");
			}

			delete this._textOrder;
			return this._textOrder = textOrder;
		});
	},

	updateStatusField: function(force)
	{
		let text = { val: "", type: "" };
		for(let i = 0; !text.val && i < this._textOrder.length; i++)
		{
			text = this[this._textOrder[i]];
		}

		if(this._statusText.val != text.val || force)
		{
			if(this._noUpdate)
			{
				return;
			}

			this._statusText = text;

			this.setStatusField(this._service.status, text, false);

			if(text.val && this._service.statusTimeout)
			{
				this.setTimer(text.type);
			}
		}
	},

	updateFullScreen: function()
	{
		this._isFullScreen = this._window.fullScreen;
		this._isFullScreenVideo = false;
		if(this._isFullScreen)
		{
			let fsEl = this._window.content.document.mozFullScreenElement;
			if(fsEl && (fsEl.nodeName == "VIDEO" || fsEl.getElementsByTagName("VIDEO").length > 0))
			{
				this._isFullScreenVideo = true;
			}
		}

		this.clearStatusField();
		this.updateStatusField(true);
	},

	setTimer: function(type)
	{
		let typeArgs = type.split(" ", 3);

		if(typeArgs.length < 2 || typeArgs[0] != "status")
		{
			return;
		}

		if(typeArgs[1] == "chrome")
		{
			this.clearTimer("_statusChromeTimeoutID");
			this._statusChromeTimeoutID = this._window.setTimeout(function(self, isDefault)
			{
				self._statusChromeTimeoutID = 0;
				if(isDefault)
				{
					self.setDefaultStatus("");
				}
				else
				{
					self.setStatusText("");
				}
			}, this._service.statusTimeout, this, (typeArgs.length == 3 && typeArgs[2] == "default"));
		}
		else
		{
			this.clearTimer("_statusContentTimeoutID");
			this._statusContentTimeoutID = this._window.setTimeout(function(self)
			{
				self._statusContentTimeoutID = 0;
				self.setJSStatus("");
			}, this._service.statusTimeout, this);
		}
	},

	clearTimer: function(timerName)
	{
		if(this[timerName] != 0)
		{
			this._window.clearTimeout(this[timerName]);
			this[timerName] = 0;
		}
	},

	clearStatusField: function()
	{
		this._getters.statusOverlay.value = "";

		let status_label = this._getters.statusWidgetLabel;
		if(status_label)
		{
			status_label.value = "";
		}

		let urlbar = this._getters.urlbar;
		if(urlbar)
		{
			urlbar.setStatus("");
		}
	},

	setStatusField: function(location, text, allowTooltip)
	{
		if(!location)
		{
			return;
		}

		let label = null;

		if(this._isFullScreen && this._service.advancedStatusDetectFullScreen)
		{
			switch(location)
			{
				case 1: // Toolbar
					location = 3
					break;
				case 2: // URL bar
					if(Services.prefs.getBoolPref("browser.fullscreen.autohide"))
					{
						location = 3
					}
					break;
			}
		}

		switch(location)
		{
			case 1: // Toolbar
				label = this._getters.statusWidgetLabel;
				break;
			case 2: // URL Bar
				let urlbar = this._getters.urlbar;
				if(urlbar)
				{
					urlbar.setStatusType(text.type);
					urlbar.setStatus(text.val);
				}
				break;
			case 3: // Popup
			default:
				if(this._isFullScreenVideo && this._service.advancedStatusDetectVideo)
				{
					return;
				}
				label = this._getters.statusOverlay;
				break;
		}

		if(label)
		{
			label.setAttribute("previoustype", label.getAttribute("type"));
			label.setAttribute("type", text.type);
			label.value = text.val;
			label.setAttribute("crop", text.type == "overLink" ? "center" : "end");
		}
	}
};

function S4EOverlinkService(window, service, statusService) {
	this._window = window;
	this._service = service;
	this._statusService = statusService;
}

S4EOverlinkService.prototype =
{
	_window:        null,
	_service:       null,
	_statusService: null,

	_timer:         0,
	_currentLink:   { link: "", anchor: null },
	_pendingLink:   { link: "", anchor: null },
	_listening:     false,

	update: function(aLink, aAnchor)
	{
		this.clearTimer();
		this.stopListen();
		this._pendingLink = { link: aLink, anchor: aAnchor };

		if(!aLink)
		{
			if(this._window.XULBrowserWindow.hideOverLinkImmediately || !this._service.statusLinkOverDelayHide)
			{
				this._show();
			}
			else
			{
				this._showDelayed();
			}
		}
		else if(this._currentLink.link || !this._service.statusLinkOverDelayShow)
		{
			this._show();
		}
		else
		{
			this._showDelayed();
			this.startListen();
		}
	},

	destroy: function()
	{
		this.clearTimer();
		this.stopListen();

		["_currentLink", "_pendingLink",  "_statusService", "_window"].forEach(function(prop)
		{
			delete this[prop];
		}, this);
	},

	startListen: function()
	{
		if(!this._listening)
		{
			this._window.addEventListener("mousemove", this, true);
			this._listening = true;
		}
	},

	stopListen: function()
	{
		if(this._listening)
		{
			this._window.removeEventListener("mousemove", this, true);
			this._listening = false;
		}
	},

	clearTimer: function()
	{
		if(this._timer != 0)
		{
			this._window.clearTimeout(this._timer);
			this._timer = 0;
		}
	},

	handleEvent: function(event)
	{
		switch(event.type)
		{
			case "mousemove":
				this.clearTimer();
				this._showDelayed();
		}
	},

	_showDelayed: function()
	{
		let delay = ((this._pendingLink.link)
			? this._service.statusLinkOverDelayShow
			: this._service.statusLinkOverDelayHide);

		this._timer = this._window.setTimeout(function(self)
		{
			self._timer = 0;
			self._show();
			self.stopListen();
		}, delay, this);
	},

	_show: function()
	{
		this._currentLink = this._pendingLink;
		this._statusService.setOverLinkInternal(this._currentLink.link, this._currentLink.anchor);
	}
};

<|endoftext|>


<commit_before>
    // Match any single/double quoted string
var REGEX_BEGINS_WITH_STRING = new RegExp('^(?:"(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\']*(?:\'\'[^\']*)*)\')', '');

var REGEX_ENDS_WITH_BEFORE_CALL_CHAR = /[a-zA-Z0-9_\]\)]\s*$/;
var REGEX_ENDS_WITH_KEY = /([a-zA-Z0-9_]+)\s*\:$/;
var REGEX_ENDS_WITH_ALPHANUMERIC = /([a-zA-Z0-9_]+)$/;
var REGEX_BEGINS_WITH_KEY = /^([a-zA-Z0-9_]+)\s*\:/;

module.exports = {

        // Parse a composed haxe type (that can be a whole function signature)
        // and return an object with all the informations walkable recursively (json-friendly)
        // A function type will have an `args` value next to the `type` value
        // while a regular type will only have a `type` value.
        // In case the type is itself named inside another function signature, a `name` value
        // Will be added to it.
    parse_composed_type: function(raw_composed_type, ctx, delimiter) {

        var info = {};
        var len = raw_composed_type.length;

        var current_item = '';
        var items = [];
        var item_params = [];
        var c, item, sub_item, params;

        if (ctx == null) {
            ctx = {
                i: 0, // index
                stop: null // the character that stopped the last recursive call
            };
        }

            // Iterate over each characters and parse groups recursively
        while (ctx.i < len) {
            c = raw_composed_type.charAt(ctx.i);

            if (c === '(') {
                ctx.i++;
                if (current_item.length > 0 && current_item.charAt(current_item.length - 1) === ':') {
                        // New group, continue parsing in a sub call until the end of the group
                    item = {
                        name: current_item.slice(0, current_item.length-1),
                        type: this.parse_composed_type(raw_composed_type, ctx)
                    };
                    if (item.name.charAt(0) === '?') {
                        item.optional = true;
                        item.name = item.name.slice(1);
                    }
                    items.push(item);
                } else {
                    items.push(this.parse_composed_type(raw_composed_type, ctx));
                }
                current_item = '';
            }
            else if (c === '<') {
                ctx.i++;

                    // Add type parameters
                params = [];
                do {
                    params.push(this.parse_composed_type(raw_composed_type, ctx));
                }
                while (ctx.stop === ',');

                if (current_item.length > 0) {
                    item = this.parse_composed_type(current_item);

                        // Nullable?
                    if (item.type === 'Null' && params.length === 1) {
                        item.nullable = true;
                        item.type = params[0];

                    } else {
                        item.type = {
                            type: item.type,
                            params: params
                        };
                    }

                    items.push(item);
                }
                item_params.push([]);
                current_item = '';
            }
            else if (c === ')') {
                ctx.i++;
                ctx.stop = ')';
                break;
            }
            else if (c === '>') {
                ctx.i++;
                ctx.stop = '>';
                break;
            }
            else if (c === ',') {
                ctx.i++;
                ctx.stop = ',';
                break;
            }
            else if (c === '-' && raw_composed_type.charAt(ctx.i + 1) === '>') {
                if (current_item.length > 0) {
                        // Parse the current item as a composed type in case there are
                        // nested groups inside
                    items.push(this.parse_composed_type(current_item));
                }
                current_item = '';
                ctx.i += 2;
            }
            else if (c.trim() === '') {
                ctx.i++;
            }
            else {
                current_item += c;
                ctx.i++;
            }
        }

            // Stopped by end of string
        if (ctx.i >= len) {
            ctx.stop = null;
        }

        if (current_item.length > 0) {
            if (current_item.indexOf('->') != -1) {
                    // Parse the current item as a composed type as there as still
                    // nested groups inside
                items.push(this.parse_composed_type(current_item));
            }
            else {
                items.push(this.parse_type(current_item));
            }
        }

        if (items.length > 1) {
                // If multiple items were parsed, that means it is a function signature
                // Extract arguments and return type
            info.args = [].concat(items);
            info.type = info.args.pop();
            if (info.args.length === 1 && info.args[0].type === 'Void') {
                info.args = [];
            }
        }
        else if (items.length === 1) {
                // If only 1 item was parsed, this is a simple type
            info = items[0];
        }

        return info;
    },

        // Parse haxe type / haxe named argument
        // It will return an object with a `type` value or with both a `type` and `name` values
    parse_type: function(raw_type) {

        var parts = raw_type.split(':');
        var result = {};

        if (parts.length === 2) {
            result.type = parts[1];
            result.name = parts[0];

        } else {
            result.type = parts[0];
        }

            // Optional?
        if (result.name != null && result.name.charAt(0) === '?') {
            result.optional = true;
            result.name = result.name.slice(1);
        }

        return result;
    },

        // Get string from parsed haxe type
        // It may be useful to stringify a sub-type (group)
        // of a previously parsed type
    string_from_parsed_type: function(parsed_type) {
        if (parsed_type == null) {
            return '';
        }

        if (typeof(parsed_type) == 'object') {
            var result;

            if (parsed_type.args != null) {
                var str_args;
                if (parsed_type.args.length > 0) {
                    var arg_items = [];
                    for (var i = 0; i < parsed_type.args.length; i++) {
                        arg_items.push(this.string_from_parsed_type(parsed_type.args[i]));
                    }
                    str_args = arg_items.join('->');
                }
                else {
                    str_args = 'Void';
                }

                if (parsed_type.type != null && parsed_type.type.args != null) {
                    result = str_args + '->(' + this.string_from_parsed_type(parsed_type.type) + ')';
                } else {
                    result = str_args + '->' + this.string_from_parsed_type(parsed_type.type)
                }
            }
            else {
                result = this.string_from_parsed_type(parsed_type.type);
            }

            if (parsed_type.params != null && parsed_type.params.length > 0) {
                var params = [];
                for (var i = 0; i < parsed_type.params.length; i++) {
                    params.push(this.string_from_parsed_type(parsed_type.params[i]));
                }

                result += '<' + params.join(',') + '>';
            }

            return result;
        }

        return String(parsed_type);
    },

        // Try to match a partial function call from the given
        // text and index position and return info if succeeded or null.
        // The provided info are:
        //  `signature_start`   the index of the opening parenthesis starting the function call signature
        //  `number_of_args`    the number of arguments between the signature start and the given index
        //  `key_path`          (optional) an array of keys, in case the index is inside an anonymous structure given as argument
        //  `partial_key`       (optional) a string of the key being written at the given index if inside an anonymous structure given as argument
    parse_partial_call: function(text, index) {
            // Cleanup text
        text = this.code_with_empty_comments_and_strings(text.slice(0, index));

        var i = index - 1;
        var number_of_args = 0;
        var number_of_parens = 0;
        var number_of_braces = 0;
        var number_of_unclosed_parens = 0;
        var number_of_unclosed_braces = 0;
        var signature_start = -1;
        var did_extract_used_keys = false;
        var c, arg, m;

            // A key path will be detected when giving
            // anonymous structure as argument. The key path will allow to
            // know exactly which key or value we are currently writing.
            // Coupled with typedefs, it can allow to compute suggestions for
            // anonymous structure keys and values
        var can_set_colon_index = true;
        var colon_index = -1;
        var key_path = [];
        var used_keys = [];
        var partial_key = null;

        while (i > 0) {
            c = text.charAt(i);

            if (c === '"' || c === '\'') {
                    // Continue until we reach the beginning of the string
                while (i >= 0) {
                    i--;
                    if (text.charAt(i) === c) {
                        i--;
                        break;
                    }
                }
            }
            else if (c === ',') {
                if (number_of_parens === 0 && number_of_braces === 0) {
                    can_set_colon_index = false;
                    if (number_of_args == 0) {
                        arg = text.slice(i + 1, index);
                        if (arg.length > 0) {
                            number_of_args++;
                        }
                    }
                    else {
                        number_of_args++;
                    }
                }
                i--;
            }
            else if (c === ')') {
                number_of_parens++;
                i--;
            }
            else if (c === '}') {
                number_of_braces++;
                i--;
            }
            else if (c === ':') {
                if (can_set_colon_index && number_of_braces === 0 && number_of_parens == 0) {
                    colon_index = i;
                    can_set_colon_index = false;
                }
                i--;
            }
            else if (c === '{') {
                if (number_of_braces === 0) {
                        // Reset number of arguments because we found that
                        // all the already parsed text is inside an unclosed brace token
                    number_of_args = 0;
                    number_of_unclosed_braces++;
                    can_set_colon_index = true;

                    if (!did_extract_used_keys) {
                            // Extract already used keys
                        used_keys = this.extract_used_keys_in_structure(text.slice(i+1));
                        did_extract_used_keys = true;
                    }

                        // Match key
                    if (colon_index != -1) {
                        REGEX_ENDS_WITH_KEY.lastIndex = -1;
                        if (m = text.slice(0, colon_index + 1).match(REGEX_ENDS_WITH_KEY)) {
                            key_path.unshift(m[1]);
                        }
                    }
                    else if (key_path.length === 0) {
                        REGEX_ENDS_WITH_ALPHANUMERIC.lastIndex = -1;
                        if (m = text.slice(0, index).match(REGEX_ENDS_WITH_ALPHANUMERIC)) {
                            partial_key = m[1];
                        } else {
                            partial_key = '';
                        }
                    }
                }
                else {
                    number_of_braces--;
                }
                i--;
            }
            else if (c === '(') {
                if (number_of_parens > 0) {
                    number_of_parens--;
                    i--;
                }
                else {
                    REGEX_ENDS_WITH_BEFORE_CALL_CHAR.lastIndex = -1;
                    if (REGEX_ENDS_WITH_BEFORE_CALL_CHAR.test(text.slice(0, i))) {
                        number_of_args++;
                        signature_start = i;
                        break;
                    }
                    else {
                            // Reset number of arguments because we found that
                            // all the already parsed text is inside an unclosed paren token
                        number_of_args = 0;

                            // Reset key path also if needed
                        can_set_colon_index = true;
                        colon_index = -1;

                        number_of_unclosed_parens++;
                        i--;
                    }
                }
            }
            else {
                i--;
            }
        }

        if (signature_start === -1) {
            return null;
        }

        var result = {
            signature_start: signature_start,
            number_of_args: number_of_args
        };

        if (number_of_unclosed_braces > 0) {
            result.key_path = key_path;
            result.partial_key = partial_key;
            result.used_keys = used_keys;
        }

        return result;
    },

        // Extract used keys in structure
        // For instance, when writing: `{a: {b: "c"}, d: |`, used_keys will contain 'a' and 'd'.
    extract_used_keys_in_structure: function(cleaned_text) {

        var i = 0, len = cleaned_text.length;
        var number_of_braces = 0;
        var number_of_parens = 0;
        var c;
        var used_keys = [];

        while (i < len) {
            c = cleaned_text.charAt(i);
            if (c === '{') {
                number_of_braces++;
                i++;
            }
            else if (c === '}') {
                number_of_braces--;
                i++;
            }
            else if (c === '(') {
                number_of_parens++;
                i++;
            }
            else if (c === ')') {
                number_of_parens--;
                i++;
            }
            else if (number_of_braces === 0 && number_of_parens === 0) {
                REGEX_BEGINS_WITH_KEY.lastIndex = -1;
                if (m = cleaned_text.slice(i).match(REGEX_BEGINS_WITH_KEY)) {
                    i += m[0].length;
                    used_keys.push(m[1]);
                }
                else {
                    i++;
                }
            } else {
                i++;
            }
        }

        return used_keys;
    },

        // Return the given code after replacing single-line/multiline comments
        // and string contents with white spaces
        // In other words, the output will be the same haxe code, with the same text length
        // but strings will be only composed of spaces and comments completely replaced with spaces
        // Use this method to simplify later parsing of the code and/or make it more efficient
        // where you don't need string and comment contents
    code_with_empty_comments_and_strings: function(input) {

        var i = 0;
        var output = '';
        var len = input.length;
        var is_in_single_line_comment = false;
        var is_in_multiline_comment = false;
        var matches, k;

        while (i < len) {

            if (is_in_single_line_comment) {
                if (input.charAt(i) === "\n") {
                    is_in_single_line_comment = false;
                    output += "\n";
                }
                else {
                    output += ' ';
                }
                i++;
            }
            else if (is_in_multiline_comment) {
                if (input.substr(i, 2) === '*/') {
                    is_in_multiline_comment = false;
                    output += '  ';
                    i += 2;
                }
                else {
                    if (input.charAt(i) === "\n") {
                        output += "\n";
                    }
                    else {
                        output += ' ';
                    }
                    i++;
                }
            }
            else if (input.substr(i, 2) === '//') {
                is_in_single_line_comment = true;
                output += '  ';
                i += 2;
            }
            else if (input.substr(i, 2) === '/*') {
                is_in_multiline_comment = true;
                output += '  ';
                i += 2;
            }
            else if (input.charAt(i) === '\'' || input.charAt(i) === '"') {
                REGEX_BEGINS_WITH_STRING.lastIndex = -1;
                if (matches = input.substring(i).match(REGEX_BEGINS_WITH_STRING)) {
                    var match_len = matches[0].length;
                    output += '"';
                    for (k = 0; k < match_len - 2; k++) {
                        output += ' ';
                    }
                    output += '"';
                    i += match_len;
                }
                else {
                        // Input finishes with non terminated string
                        // In that case, remove the partial string and put spaces
                    while (i < len) {
                        output += ' ';
                        i++;
                    }
                }
            }
            else {
                output += input.charAt(i);
                i++;
            }
        }

        return output;
    }

} //module.exports
<commit_msg>completion; don't strip Null<T>, print missing type parens<commit_after>
    // Match any single/double quoted string
var REGEX_BEGINS_WITH_STRING = new RegExp('^(?:"(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\']*(?:\'\'[^\']*)*)\')', '');

var REGEX_ENDS_WITH_BEFORE_CALL_CHAR = /[a-zA-Z0-9_\]\)]\s*$/;
var REGEX_ENDS_WITH_KEY = /([a-zA-Z0-9_]+)\s*\:$/;
var REGEX_ENDS_WITH_ALPHANUMERIC = /([a-zA-Z0-9_]+)$/;
var REGEX_BEGINS_WITH_KEY = /^([a-zA-Z0-9_]+)\s*\:/;

module.exports = {

        // Parse a composed haxe type (that can be a whole function signature)
        // and return an object with all the informations walkable recursively (json-friendly)
        // A function type will have an `args` value next to the `type` value
        // while a regular type will only have a `type` value.
        // In case the type is itself named inside another function signature, a `name` value
        // Will be added to it.
    parse_composed_type: function(raw_composed_type, ctx, delimiter) {

        var info = {};
        var len = raw_composed_type.length;

        var current_item = '';
        var items = [];
        var item_params = [];
        var c, item, sub_item, params;

        if (ctx == null) {
            ctx = {
                i: 0, // index
                stop: null // the character that stopped the last recursive call
            };
        }

            // Iterate over each characters and parse groups recursively
        while (ctx.i < len) {
            c = raw_composed_type.charAt(ctx.i);

            if (c === '(') {
                ctx.i++;
                if (current_item.length > 0 && current_item.charAt(current_item.length - 1) === ':') {
                        // New group, continue parsing in a sub call until the end of the group
                    item = {
                        name: current_item.slice(0, current_item.length-1),
                        type: this.parse_composed_type(raw_composed_type, ctx)
                    };
                    if (item.name.charAt(0) === '?') {
                        item.optional = true;
                        item.name = item.name.slice(1);
                    }
                    items.push(item);
                } else {
                    items.push(this.parse_composed_type(raw_composed_type, ctx));
                }
                current_item = '';
            }
            else if (c === '<') {
                ctx.i++;

                    // Add type parameters
                params = [];
                do {
                    params.push(this.parse_composed_type(raw_composed_type, ctx));
                }
                while (ctx.stop === ',');

                if (current_item.length > 0) {
                    item = this.parse_composed_type(current_item);

                    item.type = {
                        type: item.type,
                        params: params
                    };

                    items.push(item);
                }
                item_params.push([]);
                current_item = '';
            }
            else if (c === ')') {
                ctx.i++;
                ctx.stop = ')';
                break;
            }
            else if (c === '>') {
                ctx.i++;
                ctx.stop = '>';
                break;
            }
            else if (c === ',') {
                ctx.i++;
                ctx.stop = ',';
                break;
            }
            else if (c === '-' && raw_composed_type.charAt(ctx.i + 1) === '>') {
                if (current_item.length > 0) {
                        // Parse the current item as a composed type in case there are
                        // nested groups inside
                    items.push(this.parse_composed_type(current_item));
                }
                current_item = '';
                ctx.i += 2;
            }
            else if (c.trim() === '') {
                ctx.i++;
            }
            else {
                current_item += c;
                ctx.i++;
            }
        }

            // Stopped by end of string
        if (ctx.i >= len) {
            ctx.stop = null;
        }

        if (current_item.length > 0) {
            if (current_item.indexOf('->') != -1) {
                    // Parse the current item as a composed type as there as still
                    // nested groups inside
                items.push(this.parse_composed_type(current_item));
            }
            else {
                items.push(this.parse_type(current_item));
            }
        }

        if (items.length > 1) {
                // If multiple items were parsed, that means it is a function signature
                // Extract arguments and return type
            info.args = [].concat(items);
            info.type = info.args.pop();
            if (info.args.length === 1 && info.args[0].type === 'Void') {
                info.args = [];
            }
        }
        else if (items.length === 1) {
                // If only 1 item was parsed, this is a simple type
            info = items[0];
        }

        return info;
    },

        // Parse haxe type / haxe named argument
        // It will return an object with a `type` value or with both a `type` and `name` values
    parse_type: function(raw_type) {

        var parts = raw_type.split(':');
        var result = {};

        if (parts.length === 2) {
            result.type = parts[1];
            result.name = parts[0];

        } else {
            result.type = parts[0];
        }

            // Optional?
        if (result.name != null && result.name.charAt(0) === '?') {
            result.optional = true;
            result.name = result.name.slice(1);
        }

        return result;
    },

        // Get string from parsed haxe type
        // It may be useful to stringify a sub-type (group)
        // of a previously parsed type
    string_from_parsed_type: function(parsed_type) {
        if (parsed_type == null) {
            return '';
        }

        if (typeof(parsed_type) == 'object') {
            var result;

            if (parsed_type.args != null) {
                var str_args;
                if (parsed_type.args.length > 0) {
                    var arg_items = [];
                    var str_arg;
                    for (var i = 0; i < parsed_type.args.length; i++) {
                        str_arg = this.string_from_parsed_type(parsed_type.args[i]);
                        if (parsed_type.args[i].args != null && parsed_type.args[i].args.length == 1) {
                            str_arg = '(' + str_arg + ')';
                        }
                        arg_items.push(str_arg);
                    }
                    str_args = arg_items.join('->');
                }
                else {
                    str_args = 'Void';
                }

                if (parsed_type.type != null && parsed_type.type.args != null) {
                    result = str_args + '->(' + this.string_from_parsed_type(parsed_type.type) + ')';
                } else {
                    result = str_args + '->' + this.string_from_parsed_type(parsed_type.type)
                }
            }
            else {
                result = this.string_from_parsed_type(parsed_type.type);
            }

            if (parsed_type.params != null && parsed_type.params.length > 0) {
                var params = [];
                for (var i = 0; i < parsed_type.params.length; i++) {
                    params.push(this.string_from_parsed_type(parsed_type.params[i]));
                }

                result += '<' + params.join(',') + '>';
            }

            return result;
        }

        return String(parsed_type);
    },

        // Try to match a partial function call from the given
        // text and index position and return info if succeeded or null.
        // The provided info are:
        //  `signature_start`   the index of the opening parenthesis starting the function call signature
        //  `number_of_args`    the number of arguments between the signature start and the given index
        //  `key_path`          (optional) an array of keys, in case the index is inside an anonymous structure given as argument
        //  `partial_key`       (optional) a string of the key being written at the given index if inside an anonymous structure given as argument
    parse_partial_call: function(text, index) {
            // Cleanup text
        text = this.code_with_empty_comments_and_strings(text.slice(0, index));

        var i = index - 1;
        var number_of_args = 0;
        var number_of_parens = 0;
        var number_of_braces = 0;
        var number_of_unclosed_parens = 0;
        var number_of_unclosed_braces = 0;
        var signature_start = -1;
        var did_extract_used_keys = false;
        var c, arg, m;

            // A key path will be detected when giving
            // anonymous structure as argument. The key path will allow to
            // know exactly which key or value we are currently writing.
            // Coupled with typedefs, it can allow to compute suggestions for
            // anonymous structure keys and values
        var can_set_colon_index = true;
        var colon_index = -1;
        var key_path = [];
        var used_keys = [];
        var partial_key = null;

        while (i > 0) {
            c = text.charAt(i);

            if (c === '"' || c === '\'') {
                    // Continue until we reach the beginning of the string
                while (i >= 0) {
                    i--;
                    if (text.charAt(i) === c) {
                        i--;
                        break;
                    }
                }
            }
            else if (c === ',') {
                if (number_of_parens === 0 && number_of_braces === 0) {
                    can_set_colon_index = false;
                    if (number_of_args == 0) {
                        arg = text.slice(i + 1, index);
                        if (arg.length > 0) {
                            number_of_args++;
                        }
                    }
                    else {
                        number_of_args++;
                    }
                }
                i--;
            }
            else if (c === ')') {
                number_of_parens++;
                i--;
            }
            else if (c === '}') {
                number_of_braces++;
                i--;
            }
            else if (c === ':') {
                if (can_set_colon_index && number_of_braces === 0 && number_of_parens == 0) {
                    colon_index = i;
                    can_set_colon_index = false;
                }
                i--;
            }
            else if (c === '{') {
                if (number_of_braces === 0) {
                        // Reset number of arguments because we found that
                        // all the already parsed text is inside an unclosed brace token
                    number_of_args = 0;
                    number_of_unclosed_braces++;
                    can_set_colon_index = true;

                    if (!did_extract_used_keys) {
                            // Extract already used keys
                        used_keys = this.extract_used_keys_in_structure(text.slice(i+1));
                        did_extract_used_keys = true;
                    }

                        // Match key
                    if (colon_index != -1) {
                        REGEX_ENDS_WITH_KEY.lastIndex = -1;
                        if (m = text.slice(0, colon_index + 1).match(REGEX_ENDS_WITH_KEY)) {
                            key_path.unshift(m[1]);
                        }
                    }
                    else if (key_path.length === 0) {
                        REGEX_ENDS_WITH_ALPHANUMERIC.lastIndex = -1;
                        if (m = text.slice(0, index).match(REGEX_ENDS_WITH_ALPHANUMERIC)) {
                            partial_key = m[1];
                        } else {
                            partial_key = '';
                        }
                    }
                }
                else {
                    number_of_braces--;
                }
                i--;
            }
            else if (c === '(') {
                if (number_of_parens > 0) {
                    number_of_parens--;
                    i--;
                }
                else {
                    REGEX_ENDS_WITH_BEFORE_CALL_CHAR.lastIndex = -1;
                    if (REGEX_ENDS_WITH_BEFORE_CALL_CHAR.test(text.slice(0, i))) {
                        number_of_args++;
                        signature_start = i;
                        break;
                    }
                    else {
                            // Reset number of arguments because we found that
                            // all the already parsed text is inside an unclosed paren token
                        number_of_args = 0;

                            // Reset key path also if needed
                        can_set_colon_index = true;
                        colon_index = -1;

                        number_of_unclosed_parens++;
                        i--;
                    }
                }
            }
            else {
                i--;
            }
        }

        if (signature_start === -1) {
            return null;
        }

        var result = {
            signature_start: signature_start,
            number_of_args: number_of_args
        };

        if (number_of_unclosed_braces > 0) {
            result.key_path = key_path;
            result.partial_key = partial_key;
            result.used_keys = used_keys;
        }

        return result;
    },

        // Extract used keys in structure
        // For instance, when writing: `{a: {b: "c"}, d: |`, used_keys will contain 'a' and 'd'.
    extract_used_keys_in_structure: function(cleaned_text) {

        var i = 0, len = cleaned_text.length;
        var number_of_braces = 0;
        var number_of_parens = 0;
        var c;
        var used_keys = [];

        while (i < len) {
            c = cleaned_text.charAt(i);
            if (c === '{') {
                number_of_braces++;
                i++;
            }
            else if (c === '}') {
                number_of_braces--;
                i++;
            }
            else if (c === '(') {
                number_of_parens++;
                i++;
            }
            else if (c === ')') {
                number_of_parens--;
                i++;
            }
            else if (number_of_braces === 0 && number_of_parens === 0) {
                REGEX_BEGINS_WITH_KEY.lastIndex = -1;
                if (m = cleaned_text.slice(i).match(REGEX_BEGINS_WITH_KEY)) {
                    i += m[0].length;
                    used_keys.push(m[1]);
                }
                else {
                    i++;
                }
            } else {
                i++;
            }
        }

        return used_keys;
    },

        // Return the given code after replacing single-line/multiline comments
        // and string contents with white spaces
        // In other words, the output will be the same haxe code, with the same text length
        // but strings will be only composed of spaces and comments completely replaced with spaces
        // Use this method to simplify later parsing of the code and/or make it more efficient
        // where you don't need string and comment contents
    code_with_empty_comments_and_strings: function(input) {

        var i = 0;
        var output = '';
        var len = input.length;
        var is_in_single_line_comment = false;
        var is_in_multiline_comment = false;
        var matches, k;

        while (i < len) {

            if (is_in_single_line_comment) {
                if (input.charAt(i) === "\n") {
                    is_in_single_line_comment = false;
                    output += "\n";
                }
                else {
                    output += ' ';
                }
                i++;
            }
            else if (is_in_multiline_comment) {
                if (input.substr(i, 2) === '*/') {
                    is_in_multiline_comment = false;
                    output += '  ';
                    i += 2;
                }
                else {
                    if (input.charAt(i) === "\n") {
                        output += "\n";
                    }
                    else {
                        output += ' ';
                    }
                    i++;
                }
            }
            else if (input.substr(i, 2) === '//') {
                is_in_single_line_comment = true;
                output += '  ';
                i += 2;
            }
            else if (input.substr(i, 2) === '/*') {
                is_in_multiline_comment = true;
                output += '  ';
                i += 2;
            }
            else if (input.charAt(i) === '\'' || input.charAt(i) === '"') {
                REGEX_BEGINS_WITH_STRING.lastIndex = -1;
                if (matches = input.substring(i).match(REGEX_BEGINS_WITH_STRING)) {
                    var match_len = matches[0].length;
                    output += '"';
                    for (k = 0; k < match_len - 2; k++) {
                        output += ' ';
                    }
                    output += '"';
                    i += match_len;
                }
                else {
                        // Input finishes with non terminated string
                        // In that case, remove the partial string and put spaces
                    while (i < len) {
                        output += ' ';
                        i++;
                    }
                }
            }
            else {
                output += input.charAt(i);
                i++;
            }
        }

        return output;
    }

} //module.exports
<|endoftext|>