<commit_before>import PropTypes from 'prop-types';

export const commandsPropType = PropTypes.objectOf(PropTypes.oneOfType([
  PropTypes.func,
  PropTypes.shape({
    options: PropTypes.arrayOf(PropTypes.shape({
      name: PropTypes.string,
      description: PropTypes.string,
      defaultValue: PropTypes.any,
    })),
    method: PropTypes.func,
  }),
]));

export const descriptionsPropType = PropTypes.objectOf(PropTypes.oneOfType([
  PropTypes.string,
  PropTypes.bool,
]));

export const TerminalPropTypes = {
  startState: PropTypes.oneOf(['minimised', 'maximised', 'open', 'closed']),
  showActions: PropTypes.bool,
  hideTopBar: PropTypes.bool,
  allowTabs: PropTypes.bool,
  msg: PropTypes.string,
  openTitle: PropTypes.string,
  openMessage: PropTypes.string,
  color: PropTypes.string,
  style: PropTypes.object, // eslint-disable-line
  prompt: PropTypes.string,
  barColor: PropTypes.string,
  outputColor: PropTypes.string,
  backgroundColor: PropTypes.string,
  commands: commandsPropType,
  descriptions: descriptionsPropType,
  watchConsoleLogging: PropTypes.bool,
  commandPassThrough: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.bool,
  ]),
  promptSymbol: PropTypes.string,
  plugins: PropTypes.arrayOf(PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.shape({
      class: PropTypes.func,
      config: PropTypes.object,
    }),
  ])),
  shortcuts: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
  actionHandlers: PropTypes.shape({
    handleClose: PropTypes.func,
    handleMinimise: PropTypes.func,
    handleMaximise: PropTypes.func,
  }),
  afterChange: PropTypes.func,
  commandWasRun: PropTypes.func,
};

export const TerminalContextTypes = {
  barShowing: PropTypes.bool,
  tabsShowing: PropTypes.bool,
  activeTab: PropTypes.string,
  instances: PropTypes.array,
  show: PropTypes.bool,
  minimise: PropTypes.bool,
  maximise: PropTypes.bool,
  closeWindow: PropTypes.func,
  openWindow: PropTypes.func,
  minimiseWindow: PropTypes.func,
  unminimiseWindow: PropTypes.func,
  maximiseWindow: PropTypes.func,
  unmaximiseWindow: PropTypes.func,
  toggleShow: PropTypes.func,
  toggleMaximise: PropTypes.func,
  toggleMinimize: PropTypes.func,
};

export const TerminalDefaultProps = {
  startState: 'open',
  hideTopBar: false,
  allowTabs: true,
  showActions: true,
  msg: '',
  openTitle: 'OOPS! You closed the window.',
  openMessage: 'Click on the icon to reopen.',
  color: 'green',
  style: {},
  prompt: 'green',
  barColor: 'black',
  backgroundColor: 'black',
  commands: {},
  descriptions: {},
  watchConsoleLogging: true,
  commandPassThrough: false,
  promptSymbol: '>',
  plugins: [],
  shortcuts: {},
};
<commit_msg>Update types.js<commit_after>import PropTypes from 'prop-types';

export const commandsPropType = PropTypes.objectOf(PropTypes.oneOfType([
  PropTypes.func,
  PropTypes.shape({
    options: PropTypes.arrayOf(PropTypes.shape({
      name: PropTypes.string,
      description: PropTypes.string,
      defaultValue: PropTypes.any,
    })),
    method: PropTypes.func,
  }),
]));

export const descriptionsPropType = PropTypes.objectOf(PropTypes.oneOfType([
  PropTypes.string,
  PropTypes.bool,
]));

export const TerminalPropTypes = {
  startState: PropTypes.oneOf(['minimised', 'maximised', 'open', 'closed']),
  showActions: PropTypes.bool,
  hideTopBar: PropTypes.bool,
  allowTabs: PropTypes.bool,
  msg: PropTypes.string,
  closedTitle: PropTypes.string,
  closedMessage: PropTypes.string,
  color: PropTypes.string,
  style: PropTypes.object, // eslint-disable-line
  prompt: PropTypes.string,
  barColor: PropTypes.string,
  outputColor: PropTypes.string,
  backgroundColor: PropTypes.string,
  commands: commandsPropType,
  descriptions: descriptionsPropType,
  watchConsoleLogging: PropTypes.bool,
  commandPassThrough: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.bool,
  ]),
  promptSymbol: PropTypes.string,
  plugins: PropTypes.arrayOf(PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.shape({
      class: PropTypes.func,
      config: PropTypes.object,
    }),
  ])),
  shortcuts: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
  actionHandlers: PropTypes.shape({
    handleClose: PropTypes.func,
    handleMinimise: PropTypes.func,
    handleMaximise: PropTypes.func,
  }),
  afterChange: PropTypes.func,
  commandWasRun: PropTypes.func,
};

export const TerminalContextTypes = {
  barShowing: PropTypes.bool,
  tabsShowing: PropTypes.bool,
  activeTab: PropTypes.string,
  instances: PropTypes.array,
  show: PropTypes.bool,
  minimise: PropTypes.bool,
  maximise: PropTypes.bool,
  closeWindow: PropTypes.func,
  openWindow: PropTypes.func,
  minimiseWindow: PropTypes.func,
  unminimiseWindow: PropTypes.func,
  maximiseWindow: PropTypes.func,
  unmaximiseWindow: PropTypes.func,
  toggleShow: PropTypes.func,
  toggleMaximise: PropTypes.func,
  toggleMinimize: PropTypes.func,
};

export const TerminalDefaultProps = {
  startState: 'open',
  hideTopBar: false,
  allowTabs: true,
  showActions: true,
  msg: '',
  closedTitle: 'OOPS! You closed the window.',
  closedMessage: 'Click on the icon to reopen.',
  color: 'green',
  style: {},
  prompt: 'green',
  barColor: 'black',
  backgroundColor: 'black',
  commands: {},
  descriptions: {},
  watchConsoleLogging: true,
  commandPassThrough: false,
  promptSymbol: '>',
  plugins: [],
  shortcuts: {},
};
<|endoftext|>


<commit_before>import CoreFeature from '../CoreFeature.js';
import Helpers from './Helpers.js';

export default class Popup extends CoreFeature{
    constructor(table, element, parent){
        super(table);

        this.element = element;
        this.container = this._lookupContainer();

        this.parent = parent;

        this.reversedX = false;
        this.childPopup = null;
        this.blurable = false;
        this.blurCallback = null;
        this.renderedCallback = null;

        this.element.classList.add("tabulator-popup-container");

        this.blurEvent = this.hide.bind(this);
        this.escEvent = this._escapeCheck.bind(this);
    }

    _lookupContainer(){
        var container = this.table.options.popupContainer;

        if(typeof container === "string"){
            container = document.querySelector(container);

            if(!container){
                console.warn("Menu Error - no container element found matching selector:",  this.table.options.popupContainer , "(defaulting to document body)");
            }
        }else if (container === true){
            container = this.table.element;
        }

        if(container && !this._checkContainerIsParent(container)){
            container = false;
            console.warn("Menu Error - container element does not contain this table:",  this.table.options.popupContainer , "(defaulting to document body)");
        }

        if(!container){
            container = document.body;
        }

        return container;
    }

    _checkContainerIsParent(container, element = this.table.element){
        if(container === element){
            return true;
        }else{
            return element.parentNode ? this._checkContainerIsParent(container, element.parentNode) : false;
        }
    }

    renderCallback(callback){
        this.renderedCallback = callback;
    }

    containerEventCoords(e){
        var touch = !(e instanceof MouseEvent);

        var x = touch ? e.touches[0].pageX : e.pageX;
        var y = touch ? e.touches[0].pageY : e.pageY;

        if(this.container !== document.body){
            parentOffset = Helpers.elOffset(this.container);

            x -= parentOffset.left;
            y -= parentOffset.top;
        }

        return {x, y};
    }

    show(origin, originY){
        var x, y, parentEl, parentOffset, containerOffset, coords;

        if(origin instanceof HTMLElement){
            parentEl = origin;
            parentOffset = Helpers.elOffset(parentEl);

            if(this.container !== document.body){
                containerOffset = Helpers.elOffset(this.container);

                parentOffset.left -= containerOffset.left;
                parentOffset.top -= containerOffset.top;
            }

            x = parentOffset.left + parentEl.offsetWidth;
            y = parentOffset.top - 1;
        }else if(typeof origin === "number"){
            parentOffset = {top:0, left:0};
            x = origin;
            y = originY;
        }else{
            coords = this.containerEventCoords(origin);

            x = coords.x;
            y = coords.y;

            this.reversedX = false;
        }

        this.element.style.top = y + "px";
        this.element.style.left = x + "px";

        this.container.appendChild(this.element);

        if(typeof this.renderedCallback === "function"){
            this.renderedCallback();
        }

        this._fitToScreen(x, y, parentEl, parentOffset);

        return this;
    }

    _fitToScreen(x, y, parentEl, parentOffset){
        //move menu to start on right edge if it is too close to the edge of the screen
        if((x + this.element.offsetWidth) >= this.container.offsetWidth || this.reversedX){
            this.element.style.left = "";

            if(parentEl){
                this.element.style.right = (this.container.offsetWidth - parentOffset.left) + "px";
            }else{
                this.element.style.right = (this.container.offsetWidth - x) + "px";
            }

            this.reversedX = true;
        }

        //move menu to start on bottom edge if it is too close to the edge of the screen
        if((y + this.element.offsetHeight) > this.container.offsetHeight) {
            if(this.parent){
                this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight + parentEl.offsetHeight + 1) + "px";
            }else{
                this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight) + "px";
            }
        }
    }

    hideOnBlur(callback){
        this.blurable = true;

        setTimeout(() => {
            this.table.rowManager.element.addEventListener("scroll", this.blurEvent);
            this.subscribe("cell-editing", this.blurEvent);
            document.body.addEventListener("click", this.blurEvent);
            document.body.addEventListener("contextmenu", this.blurEvent);
            window.addEventListener("resize", this.blurEvent);
            document.body.addEventListener("keydown", this.escEvent);
        }, 100);

        this.blurCallback = callback;

        return this;
    }

    _escapeCheck(e){
        if(e.keyCode == 27){
            this.hide();
        }
    }

    hide(){
        if(this.blurable){
            document.body.removeEventListener("keydown", this.escEvent);
            document.body.removeEventListener("click", this.blurEvent);
            document.body.removeEventListener("contextmenu", this.blurEvent);
            window.removeEventListener("resize", this.blurEvent);
            this.table.rowManager.element.removeEventListener("scroll", this.blurEvent);
            this.unsubscribe("cell-editing", this.blurEvent);
        }

        if(this.childPopup){
            this.childPopup.hide();
        }

        if(this.parent){
            this.parent.childPopup = null;
        }

        if(this.element.parentNode){
            this.element.parentNode.removeChild(this.element);
        }

        if(this.blurCallback){
            this.blurCallback();
        }

        return this;
    }

    child(element){
        if(this.childPopup){
            this.childPopup.hide();
        }

        this.childPopup = new Popup(this.table, element, this);

        return this.childPopup;
    }
}<commit_msg>add ability to position popup relative to element<commit_after>import CoreFeature from '../CoreFeature.js';
import Helpers from './Helpers.js';

export default class Popup extends CoreFeature{
    constructor(table, element, parent){
        super(table);

        this.element = element;
        this.container = this._lookupContainer();

        this.parent = parent;

        this.reversedX = false;
        this.childPopup = null;
        this.blurable = false;
        this.blurCallback = null;
        this.renderedCallback = null;

        this.element.classList.add("tabulator-popup-container");

        this.blurEvent = this.hide.bind(this);
        this.escEvent = this._escapeCheck.bind(this);
    }

    _lookupContainer(){
        var container = this.table.options.popupContainer;

        if(typeof container === "string"){
            container = document.querySelector(container);

            if(!container){
                console.warn("Menu Error - no container element found matching selector:",  this.table.options.popupContainer , "(defaulting to document body)");
            }
        }else if (container === true){
            container = this.table.element;
        }

        if(container && !this._checkContainerIsParent(container)){
            container = false;
            console.warn("Menu Error - container element does not contain this table:",  this.table.options.popupContainer , "(defaulting to document body)");
        }

        if(!container){
            container = document.body;
        }

        return container;
    }

    _checkContainerIsParent(container, element = this.table.element){
        if(container === element){
            return true;
        }else{
            return element.parentNode ? this._checkContainerIsParent(container, element.parentNode) : false;
        }
    }

    renderCallback(callback){
        this.renderedCallback = callback;
    }

    containerEventCoords(e){
        var touch = !(e instanceof MouseEvent);

        var x = touch ? e.touches[0].pageX : e.pageX;
        var y = touch ? e.touches[0].pageY : e.pageY;

        if(this.container !== document.body){
            parentOffset = Helpers.elOffset(this.container);

            x -= parentOffset.left;
            y -= parentOffset.top;
        }

        return {x, y};
    }

    elementPositionCoords(element, position = "right"){
        var offset = Helpers.elOffset(element),
        containerOffset, x, y;

        if(this.container !== document.body){
            containerOffset = Helpers.elOffset(this.container);

            offset.left -= containerOffset.left;
            offset.top -= containerOffset.top;
        }

        switch(position){
            case "right":
                x = offset.left + element.offsetWidth;
                y = offset.top - 1;
            break;

            case "bottom":
                x = offset.left;
                y = offset.top + element.offsetHeight;
            break;
        }

        return {x, y, offset};
    }

    show(origin, position){
        var x, y, parentEl, parentOffset, containerOffset, coords;

        if(origin instanceof HTMLElement){
            parentEl = origin;
            coords = this.elementPositionCoords(origin, position);

            parentOffset = coords.offset;
            x = coords.x;
            y = coords.y;

        }else if(typeof origin === "number"){
            parentOffset = {top:0, left:0};
            x = origin;
            y = position;
        }else{
            coords = this.containerEventCoords(origin);

            x = coords.x;
            y = coords.y;

            this.reversedX = false;
        }

        this.element.style.top = y + "px";
        this.element.style.left = x + "px";

        this.container.appendChild(this.element);

        if(typeof this.renderedCallback === "function"){
            this.renderedCallback();
        }

        this._fitToScreen(x, y, parentEl, parentOffset);

        return this;
    }

    _fitToScreen(x, y, parentEl, parentOffset){
        //move menu to start on right edge if it is too close to the edge of the screen
        if((x + this.element.offsetWidth) >= this.container.offsetWidth || this.reversedX){
            this.element.style.left = "";

            if(parentEl){
                this.element.style.right = (this.container.offsetWidth - parentOffset.left) + "px";
            }else{
                this.element.style.right = (this.container.offsetWidth - x) + "px";
            }

            this.reversedX = true;
        }

        //move menu to start on bottom edge if it is too close to the edge of the screen
        if((y + this.element.offsetHeight) > this.container.offsetHeight) {
            if(this.parent){
                this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight + parentEl.offsetHeight + 1) + "px";
            }else{
                this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight) + "px";
            }
        }
    }

    hideOnBlur(callback){
        this.blurable = true;

        setTimeout(() => {
            this.table.rowManager.element.addEventListener("scroll", this.blurEvent);
            this.subscribe("cell-editing", this.blurEvent);
            document.body.addEventListener("click", this.blurEvent);
            document.body.addEventListener("contextmenu", this.blurEvent);
            window.addEventListener("resize", this.blurEvent);
            document.body.addEventListener("keydown", this.escEvent);
        }, 100);

        this.blurCallback = callback;

        return this;
    }

    _escapeCheck(e){
        if(e.keyCode == 27){
            this.hide();
        }
    }

    hide(){
        if(this.blurable){
            document.body.removeEventListener("keydown", this.escEvent);
            document.body.removeEventListener("click", this.blurEvent);
            document.body.removeEventListener("contextmenu", this.blurEvent);
            window.removeEventListener("resize", this.blurEvent);
            this.table.rowManager.element.removeEventListener("scroll", this.blurEvent);
            this.unsubscribe("cell-editing", this.blurEvent);
        }

        if(this.childPopup){
            this.childPopup.hide();
        }

        if(this.parent){
            this.parent.childPopup = null;
        }

        if(this.element.parentNode){
            this.element.parentNode.removeChild(this.element);
        }

        if(this.blurCallback){
            this.blurCallback();
        }

        return this;
    }

    child(element){
        if(this.childPopup){
            this.childPopup.hide();
        }

        this.childPopup = new Popup(this.table, element, this);

        return this.childPopup;
    }
}<|endoftext|>


<commit_before>/*
 * https://github.com/starzou
 *
 * Copyright (c) 2014 StarZou
 * Licensed under the MIT license.
 */

/**
 *@class app
 *@description
 *@time 2014-11-04 22:45
 *@author StarZou
 **/
'use strict';

/**
 * 定义模块
 */
var App = angular.module('App', ['ngRoute']);

/**
 * App.config
 */
App.config(['$routeProvider', function ($routeProvider) {

    /**
     * 路由服务配置
     */
    $routeProvider
        .when('/users', {templateUrl: 'users.html', controller: 'AppCtrl'})
        .when('/users/:name', {templateUrl: 'users.html', controller: 'AppCtrl'})
        .when('/hi', {templateUrl: 'hiTemplate.html', controller: 'AppCtrl'})
        .otherwise({redirectTo: '/users'});

    console.log('App.config');
}]);

/**
 * App.run
 */
App.run(['$rootScope', '$interval', function ($rootScope, $interval) {
    $rootScope.app = {
        name: 'Angular Tutorial',
        version: '1.0.0 alpha',
        date: new Date()
    };

    $interval(function () {
        $rootScope.app.date = new Date();
    }, 100, 20);

    /**
     * 监听路由服务改变, 例:可以统计用户打开页面的情况
     */
    $rootScope.$on('$routeChangeStart', function (event, next, current) {
        console.log('$routeChangeStart : ', arguments);
    });

    console.log('App.run');
}]);

/**
 * 注册controller
 */
App.controller('AppCtrl', ['$scope', '$filter', '$location', '$route', '$routeParams', function ($scope, $filter, $location, $route, $routeParams) {
    var users = $scope.users || [
            {id: 1, name: 'star1'},
            {id: 2, name: 'star2'},
            {id: 3, name: 'star3'}
        ];

    $scope.word = 'hello ';

    $scope.date = new Date();

    $scope.title = 'Hi Guys!';

    /**
     * 使用$filter 服务
     */
    $scope.name = $filter('lowercase')('StarZou');

    if (Object.keys($routeParams).length) {
        $scope.users = $filter('filter')(users, $routeParams);
    } else {
        $scope.users = users;
    }

    $scope.addUser = function (name) {
        $scope.users.push({id: Date.now(), name: name});
        $scope.name = undefined;
    };

    $scope.removeUser = function (index) {
        $scope.users.splice(index, 1);
    };

    $scope.routeTo = function (path) {
        $location.path(path);
    };

    console.log($location, $route);
}]);

/**
 * 注册自定义过滤器
 */
App.filter('capitalize', function () {
    return function (input) {
        if (input) {
            return input[0].toUpperCase() + input.slice(1);
        }
    };
});

App.directive('validateForm', function () {
    return {
        require: '?ngModel',
        link: function (scope, element, attrs, ngModel) {
            var form = scope[attrs.name];
            console.log('validateForm', arguments, form);
        }
    };
});

App.directive('validateField', function () {
    var VALIDATE = {
        required: '必填！',
        number: "必须为数字！",
        minlength: '太短！',
        maxlength: '太长！',
        min: '太小！',
        max: '太大！',
        more: '太多！',
        email: 'Email无效！',
        username: '有效字符为汉字、字母、数字、下划线，以汉字或小写字母开头！',
        minname: '长度应大于5字节，一个汉字3字节！',
        maxname: '长度应小于15字节，一个汉字3字节！',
        repasswd: '密码不一致！',
        url: 'URL无效！',
        tag: '标签错误，不能包含“,”、“，”和“、”'
    };
    return {
        require: '?ngModel',
        link: function (scope, element, attrs, ngModel) {
            console.log('validateField', arguments);

            var validateFn = function () {
                angular.forEach(ngModel.$error, function (value, key) {
                    console.log(VALIDATE[key]);
                });
            };

            /**
             * ngModel 值改变,进行验证
             */
//            scope.$watch(attrs.ngModel, validateFn);

            element.bind('keyup', validateFn);
        }
    };
});

/**
 * 内部指令 测试控制器
 */
App.controller('InternalDirectiveController', ['$scope', '$timeout', function ($scope, $timeout) {
    $scope.appUrl = 'app.js';

    $scope.tplUrl = 'hiTemplate.html';

    $scope.loadImage = function () {
        $timeout(function () {
            $scope.imgUrl = '/images/1.jpg';
            console.log($scope);
            $scope.app.date = '加载完成';
        }, 2000);
    };
}]);

App.directive('sayHello', [function () {
    return {
        restrict: 'A',
        replace: true,
        scope: {
            title: '=',
            text: '@'
        },
        template: '

{{text}}
title:<input type="text" ng-model="title" />
'
    };
}]);

App.directive('one', [function () {
    return {
        restrict: 'A',
        replace: true,
        priority: -1,
        template: '

'
    };
}]);

App.directive('two', [function () {
    return {
        restrict: 'A',
        replace: true,
        scope: true,
        terminal: true,
        template: '

'
    };
}]);

App.directive('userList', [function () {
    return {
        scope: {
            users: '=',
            remove: '&'
        },
        templateUrl: 'users.html',
        link: function (scope, element, attrs) {
            console.log(arguments);
            element.on('mouseover', function () {
                element.css('backgroundColor', 'green');
            });
            element.on('mouseout', function () {
                element.css('backgroundColor', 'white');
            });
        }
    };
}]);

/**
 * Service 使用案例
 */
App.controller('ServiceController', ['$scope', '$timeout', 'AppService', 'GitHubService', 'MyService', 'MyService2', 'MyService3', 'MyService5', 'MyService6', 'MyService7', function ($scope, $timeout, AppService, GitHubService, MyService, MyService2, MyService3, MyService5, MyService6, MyService7) {
    console.log('AppService : ', AppService);
    console.log('GitHubService : ', GitHubService);
    console.log('MyService : ', MyService);
    console.log('MyService2 : ', MyService2);
    console.log('MyService3 : ', MyService3);
    console.log('MyService5 : ', MyService5);
    console.log('MyService6 : ', MyService6);
    console.log('MyService7 : ', MyService7);

    $scope.userName = 'starzou';
    $scope.queryEvents = function (userName) {
        GitHubService.events(userName).success(function (response) {
            $scope.events = response.data;
        });
    };

    var timeout, pending = false, enable = false;
    if (enable) {
        $scope.$watch('userName', function (newUserName) {
            if (newUserName) {
                if (!timeout && !pending) {
                    pending = true;
                    timeout = $timeout(function () {
                        GitHubService.events(newUserName).success(function (response) {
                            $scope.events = response.data;
                        });
                        pending = false;
                        timeout = null;
                    }, 500);
                }
            }
        });
    }
}]);

/**
 * factory : 返回对象作为一个服务
 */
App.factory('AppService', [function () {
    var version = {
        full: "1.0.0",
        major: 1,
        dot: 0,
        minor: 0
    };
    return {
        version: version,
        date: Date.now()
    };
}]);

App.factory('GitHubService', ['$http', function ($http) {
    var Resources = {
        events: {
            url: 'https://api.github.com/users/:user/events?callback=JSON_CALLBACK',
            method: function (userName) {
                return $http({method: 'JSONP', url: this.url.replace(':user', userName)});
            }
        },
        repos: {
            url: 'https://api.github.com/users/:user/repos?callback=JSON_CALLBACK',
            method: function (userName) {
                return $http({method: 'JSONP', url: this.url.replace(':user', userName)});
            }
        }
    };

    return {
        events: function (userName) {
            return Resources.events.method(userName);
        },

        repos: function (userName) {
            return Resources.repos.method(userName);
        }
    };
}]);

/**
 * provider, 可配置的服务.
 * provider : 为参数
 * service实例 : provider.$get 方法返回的对象
 */
App.provider('MyService', {
    $get: ['AppService', function () {
        return this;
    }]
});

/**
 * 推荐.
 * provider : 为参数 函数的实例化对象
 * service实例 : provider.$get 方法返回的对象
 */
App.provider('MyService2', function () {
    var obj = {
        date: Date.now()
    };

    this.setName = function (name) {
        obj.name = name;
    };

    this.$get = ['AppService', function () {
        return obj;
    }];
});

/**
 * provider : 为参数 函数的返回值
 * service实例 : provider.$get 方法返回的对象
 */
App.provider('MyService3', ['$httpProvider', function () {
    var service = {
        date: Date.now()
    };

    return {
        $get: ['AppService', function () {
            return service;
        }],
        setName: function (name) {
            service.name = name;
        }
    };
}]);

App.config(['MyServiceProvider', function (MyServiceProvider) {
    MyServiceProvider.name = 'MyService';
}]);

App.config(['MyService2Provider', function (MyService2Provider) {
    MyService2Provider.setName('MyService2');
}]);

App.config(['MyService3Provider', function (MyService3Provider) {
    MyService3Provider.setName('MyService3');
}]);

/**
 * constant
 * service实例 : 为参数
 */
App.constant('MyService5', {date: Date.now()});

/**
 * value
 * service实例 : 为参数
 */
App.value('MyService6', {date: Date.now()});

/**
 * service
 * service实例 : 为参数 函数的实例化对象
 */
App.service('MyService7', ['AppService', function (AppService) {
    this.AppService = AppService;
    this.name = 'MyService7';
}]);<commit_msg>添加 service 示例<commit_after>/*
 * https://github.com/starzou
 *
 * Copyright (c) 2014 StarZou
 * Licensed under the MIT license.
 */

/**
 *@class app
 *@description
 *@time 2014-11-04 22:45
 *@author StarZou
 **/
'use strict';

/**
 * 定义模块
 */
var App = angular.module('App', ['ngRoute']);

/**
 * App.config 配置块
 * 配置服务
 */
App.config(['$routeProvider', function ($routeProvider) {

    /**
     * 路由服务配置
     */
    $routeProvider
        .when('/users', {templateUrl: 'users.html', controller: 'AppCtrl'})
        .when('/users/:name', {templateUrl: 'users.html', controller: 'AppCtrl'})
        .when('/hi', {templateUrl: 'hiTemplate.html', controller: 'AppCtrl'})
        .otherwise({redirectTo: '/users'});

    console.log('App.config');
}]);

/**
 * App.run 运行块
 * 初始化设置
 */
App.run(['$rootScope', '$interval', function ($rootScope, $interval) {
    $rootScope.app = {
        name: 'Angular Tutorial',
        version: '1.0.0 alpha',
        date: new Date()
    };

    $interval(function () {
        $rootScope.app.date = new Date();
    }, 100, 20);

    /**
     * 监听路由服务改变, 例:可以统计用户打开页面的情况
     */
    $rootScope.$on('$routeChangeStart', function (event, next, current) {
        console.log('$routeChangeStart : ', arguments);
    });

    console.log('App.run');
}]);

/**
 * 注册controller
 */
App.controller('AppCtrl', ['$scope', '$filter', '$location', '$route', '$routeParams', function ($scope, $filter, $location, $route, $routeParams) {
    var users = $scope.users || [
            {id: 1, name: 'star1'},
            {id: 2, name: 'star2'},
            {id: 3, name: 'star3'}
        ];

    $scope.word = 'hello ';

    $scope.date = new Date();

    $scope.title = 'Hi Guys!';

    /**
     * 使用$filter 服务
     */
    $scope.name = $filter('lowercase')('StarZou');

    if (Object.keys($routeParams).length) {
        $scope.users = $filter('filter')(users, $routeParams);
    } else {
        $scope.users = users;
    }

    $scope.addUser = function (name) {
        $scope.users.push({id: Date.now(), name: name});
        $scope.name = undefined;
    };

    $scope.removeUser = function (index) {
        $scope.users.splice(index, 1);
    };

    $scope.routeTo = function (path) {
        $location.path(path);
    };

    console.log($location, $route);
}]);

/**
 * 注册自定义过滤器
 */
App.filter('capitalize', function () {
    return function (input) {
        if (input) {
            return input[0].toUpperCase() + input.slice(1);
        }
    };
});

/**
 * 指令注册
 */
App.directive('validateForm', function () {
    return {
        require: '?ngModel',
        link: function (scope, element, attrs, ngModel) {
            var form = scope[attrs.name];
            console.log('validateForm', arguments, form);
        }
    };
});

App.directive('validateField', function () {
    var VALIDATE = {
        required: '必填！',
        number: "必须为数字！",
        minlength: '太短！',
        maxlength: '太长！',
        min: '太小！',
        max: '太大！',
        more: '太多！',
        email: 'Email无效！',
        username: '有效字符为汉字、字母、数字、下划线，以汉字或小写字母开头！',
        minname: '长度应大于5字节，一个汉字3字节！',
        maxname: '长度应小于15字节，一个汉字3字节！',
        repasswd: '密码不一致！',
        url: 'URL无效！',
        tag: '标签错误，不能包含“,”、“，”和“、”'
    };
    return {
        require: '?ngModel',
        link: function (scope, element, attrs, ngModel) {
            console.log('validateField', arguments);

            var validateFn = function () {
                angular.forEach(ngModel.$error, function (value, key) {
                    console.log(VALIDATE[key]);
                });
            };

            /**
             * ngModel 值改变,进行验证
             */
//            scope.$watch(attrs.ngModel, validateFn);

            element.bind('keyup', validateFn);
        }
    };
});

/**
 * 内部指令 测试控制器
 */
App.controller('InternalDirectiveController', ['$scope', '$timeout', function ($scope, $timeout) {
    $scope.appUrl = 'app.js';

    $scope.tplUrl = 'hiTemplate.html';

    $scope.loadImage = function () {
        $timeout(function () {
            $scope.imgUrl = '/images/1.jpg';
            console.log($scope);
            $scope.app.date = '加载完成';
        }, 2000);
    };
}]);

App.directive('sayHello', [function () {
    return {
        restrict: 'A',
        replace: true,
        scope: {
            title: '=',
            text: '@'
        },
        template: '

{{text}}
title:<input type="text" ng-model="title" />
'
    };
}]);

App.directive('one', [function () {
    return {
        restrict: 'A',
        replace: true,
        priority: -1,
        template: '

'
    };
}]);

App.directive('two', [function () {
    return {
        restrict: 'A',
        replace: true,
        scope: true,
        terminal: true,
        template: '

'
    };
}]);

App.directive('userList', [function () {
    return {
        scope: {
            users: '=',
            remove: '&'
        },
        templateUrl: 'users.html',
        link: function (scope, element, attrs) {
            console.log(arguments);
            element.on('mouseover', function () {
                element.css('backgroundColor', 'green');
            });
            element.on('mouseout', function () {
                element.css('backgroundColor', 'white');
            });
        }
    };
}]);

/**
 * Service 使用案例
 */
App.controller('ServiceController', ['$scope', '$timeout', 'AppService', 'GitHubService', 'MyService', 'MyService2', 'MyService3', 'MyService5', 'MyService6', 'MyService7', function ($scope, $timeout, AppService, GitHubService, MyService, MyService2, MyService3, MyService5, MyService6, MyService7) {
    console.log('AppService : ', AppService);
    console.log('GitHubService : ', GitHubService);
    console.log('MyService : ', MyService);
    console.log('MyService2 : ', MyService2);
    console.log('MyService3 : ', MyService3);
    console.log('MyService5 : ', MyService5);
    console.log('MyService6 : ', MyService6);
    console.log('MyService7 : ', MyService7);

    $scope.userName = 'starzou';
    $scope.queryEvents = function (userName) {
        GitHubService.events(userName).success(function (response) {
            $scope.events = response.data;
        });
    };

    var timeout, pending = false, enable = false;
    if (enable) {
        $scope.$watch('userName', function (newUserName) {
            if (newUserName) {
                if (!timeout && !pending) {
                    pending = true;
                    timeout = $timeout(function () {
                        GitHubService.events(newUserName).success(function (response) {
                            $scope.events = response.data;
                        });
                        pending = false;
                        timeout = null;
                    }, 500);
                }
            }
        });
    }
}]);

/**
 * factory : 返回对象作为一个服务
 */
App.factory('AppService', [function () {
    var version = {
        full: "1.0.0",
        major: 1,
        dot: 0,
        minor: 0
    };
    return {
        version: version,
        date: Date.now()
    };
}]);

App.factory('GitHubService', ['$http', function ($http) {
    var Resources = {
        events: {
            url: 'https://api.github.com/users/:user/events?callback=JSON_CALLBACK',
            method: function (userName) {
                return $http({method: 'JSONP', url: this.url.replace(':user', userName)});
            }
        },
        repos: {
            url: 'https://api.github.com/users/:user/repos?callback=JSON_CALLBACK',
            method: function (userName) {
                return $http({method: 'JSONP', url: this.url.replace(':user', userName)});
            }
        }
    };

    return {
        events: function (userName) {
            return Resources.events.method(userName);
        },

        repos: function (userName) {
            return Resources.repos.method(userName);
        }
    };
}]);

/**
 * provider, 可配置的服务.
 * provider : 为参数
 * service实例 : provider.$get 方法返回的对象
 */
App.provider('MyService', {
    $get: ['AppService', function () {
        return this;
    }]
});

/**
 * 推荐.
 * provider : 为参数 函数的实例化对象
 * service实例 : provider.$get 方法返回的对象
 */
App.provider('MyService2', function () {
    var obj = {
        date: Date.now()
    };

    this.setName = function (name) {
        obj.name = name;
    };

    this.$get = ['AppService', function () {
        return obj;
    }];
});

/**
 * provider : 为参数 函数的返回值
 * service实例 : provider.$get 方法返回的对象
 */
App.provider('MyService3', ['$httpProvider', function () {
    var service = {
        date: Date.now()
    };

    return {
        $get: ['AppService', function () {
            return service;
        }],
        setName: function (name) {
            service.name = name;
        }
    };
}]);

App.config(['MyServiceProvider', function (MyServiceProvider) {
    MyServiceProvider.name = 'MyService';
}]);

App.config(['MyService2Provider', function (MyService2Provider) {
    MyService2Provider.setName('MyService2');
}]);

App.config(['MyService3Provider', function (MyService3Provider) {
    MyService3Provider.setName('MyService3');
}]);

/**
 * constant
 * service实例 : 为参数
 */
App.constant('MyService5', {date: Date.now()});

/**
 * value
 * service实例 : 为参数
 */
App.value('MyService6', {date: Date.now()});

/**
 * service
 * service实例 : 为参数 函数的实例化对象
 */
App.service('MyService7', ['AppService', function (AppService) {
    this.AppService = AppService;
    this.name = 'MyService7';
}]);<|endoftext|>


<commit_before>const game = {};
const _ = require('underscore');
const preloader = require('./preloader.js');
const allowedTypes = ['jpg', 'png', 'jpeg'];
let setID = 0;
let countID = 0;
let readingComplete = false;
let loadingComplete = false;
let sets = {};

game.create = function () {
  game.prepSet();
  document.getElementById('getval').addEventListener('change', this.readURL, true);
  this.game.load.onLoadComplete.add(game.loadComplete, this);
};

game.prepSet = function () {
  setID++;
  sets['set' + String(setID)] = [];
}

game.readURL = function(){
  console.log('Loading...');
  const input = document.getElementById("getval");
  let reader = new FileReader();
  let fileArr = []
  let file;

  for(let item of input.files){
    fileArr.push(item);
  }

  let readFiles = function() {
    if(fileArr.length > 0){
      file = fileArr.shift()
      let extension = file.name.split('.').pop();
      if(allowedTypes.indexOf(extension) >= 0){
        reader.readAsDataURL(file);
      }
      else {
        console.log('Ignoring file:', file.name);
        readFiles();
      }
    }
    else
    {
      readingComplete = true;
    }
  }

  reader.onloadend = function(e){
    let result = reader.result;
    game.loadResult(result);
    readFiles();
  }
  readFiles();
}

game.loadResult = function (result) {
  countID++;
  let imgID = "set" + String(setID) + '_' + String(countID)
  this.game.load.image(imgID, result);
  this.game.load.start();
  sets['set' + String(setID)].push(imgID);
}

game.loadComplete = function () {
  if(readingComplete === true){
    loadingComplete = true;
    game.genNewPair();
  }
}

game.genNewPair = function () {
  const newPics = _.sample(sets['set' + String(setID)], 2)
  let img1 = this.addPic(0, newPics[0]);
  let img2 = this.addPic(1, newPics[1]);
}

game.addPic = function (pos, imgID) {
  const posArr = [[256, this.game.world.centerY], [768, this.game.world.centerY]];
  const pic = this.game.add.sprite(posArr[pos][0], posArr[pos][1], imgID);
  const maxX = this.game.world.width / 2;
  const maxY = this.game.world.height;
  console.log(maxX, maxY);
  const scaleX = maxX / pic.width;
  const scaleY = maxY / pic.height;
  pic.anchor.setTo(0.5, 0.5);
  pic.scale.x = pic.scale.y = Math.min(scaleX, scaleY);
  return pic
}

module.exports = game;
<commit_msg>Up to first round loading.<commit_after>const WIN   = 'win'
const DRAW  = 'draw'
const PURGE = 'purge'
const BYE   = 'bye'

const game = {};
const _ = require('underscore');
const preloader = require('./preloader.js');
const allowedTypes = ['jpg', 'png', 'jpeg'];
let setID = 0;
let countID = 0;
let matchID = 0
let readingComplete = false;
let loadingComplete = false;
let sets = {};
let teams = []
let draw = []
let teamsToLoad = []
let loadedTeams = []
let reader = new FileReader();

game.create = function () {
  game.prepSet();
  document.getElementById('getval').addEventListener('change', this.readURL, true);
  this.game.load.onLoadComplete.add(game.loadComplete, this);
};

game.prepSet = function () {
  setID++;
  sets['set' + String(setID)] = [];
}

game.readURL = function(){
  console.log('Loading...');
  const input = document.getElementById("getval");
  // Generate the teams
  let generateTeams = function(input){
    let i = 0

    let validateItem = function(item){
      let extension = item.name.split('.').pop();
      if(allowedTypes.indexOf(extension) >= 0){
        return true
      }
      console.log('Ignoring file:', item.name);
      return false
    }

    let newTeam = function(item, i){
      return {
        file: item,
        id: i,
        purged: false,
        win: 0,
        draw: 0,
        loss: 0,
        points: 0
      }
    }
    // Make each team
    for(let item of input.files){
      if(validateItem(item)){
        teams.push(newTeam(item, i))
        i++
      }
    }
    // Push a bye if odd teams
    if(teams.length % 2 != 0){
      teams.push(newTeam(BYE, i))
    }
    return teams
  }
  // Generate the draw
  let generateDraw = function(teams){
    console.log('generateDraw', teams.length, 'teams')
    let arr = []
    for(let i = 0; i < teams.length; i++){
      for(let j = 0; j < teams.length; j++){
        if(i != j){
          arr.push({
            home: teams[i],
            away: teams[j]
          })
        }
      }
    }
    arr = _.shuffle(arr)
    return arr
  }

  teams = generateTeams(input)
  draw = generateDraw(teams)
  console.log('Ready to play', draw.length, 'matches')

  game.generateMatch()
}

game.generateMatch = function(){
  let matchInfo = draw.pop()
  this.loadTeams(matchInfo.home, matchInfo.away)
}

game.endMatch = function(winner, loser, draw = false, purge = false){
  if(draw){
    winner.draw++
    loser.draw++
  }else{
    winner.win++
    loser.loss++
  }
  if(purge){
    loser.purged = true
  }
}

game.loadTeams = function(home, away){
  let readFiles = function(){
    const team = teamsToLoad.shift()
    if(teamsToLoad.length <= 0) readingComplete = true
    reader.readAsDataURL(team.file);
  }

  reader.onloadend = function(e){
    const result = reader.result;
    game.loadResult(result);
    if(teamsToLoad.length > 0) readFiles();
  }

  if(home.file === BYE){
    this.endMatch(away, home)
  }else if(away.file === BYE){
    this.endMatch(home, away)
  }else{
    teamsToLoad = [home, away]
    readFiles()
    /*for(team of [home, away]){
      reader.readAsDataURL(team.file);
    }*/
  }

    /*file = teams.shift().file
    let extension = file.name.split('.').pop();
    if(allowedTypes.indexOf(extension) >= 0){
      reader.readAsDataURL(file);
    }
    else {
      console.log('Ignoring file:', file.name);
      readFiles();
    }*/

}

game.loadResult = function (result) {
  console.log("loading image")
  countID++;
  let imgID = "set" + String(setID) + '_' + String(countID)
  this.game.load.image(imgID, result);
  this.game.load.start();
  sets['set' + String(setID)].push(imgID);
}

game.loadComplete = function () {
  console.log('loadComplete')
  if(readingComplete === true){
    loadingComplete = true;
    game.genNewPair();
  }
}

game.genNewPair = function () {
  console.log('genNewPair')
  const newPics = _.sample(sets['set' + String(setID)], 2)
  let img1 = this.addPic(0, newPics[0]);
  let img2 = this.addPic(1, newPics[1]);
}

game.addPic = function (pos, imgID) {
  console.log('addPic')
  const posArr = [[256, this.game.world.centerY], [768, this.game.world.centerY]];
  const pic = this.game.add.sprite(posArr[pos][0], posArr[pos][1], imgID);
  const maxX = this.game.world.width / 2;
  const maxY = this.game.world.height;
  console.log(maxX, maxY);
  const scaleX = maxX / pic.width;
  const scaleY = maxY / pic.height;
  pic.anchor.setTo(0.5, 0.5);
  pic.scale.x = pic.scale.y = Math.min(scaleX, scaleY);
  return pic
}

module.exports = game;
<|endoftext|>


<commit_before>"use strict";

var CV = CV || {};

CV.ProgressDial = function ( container ) {

	var stdWidth  = CV.HudObject.stdWidth;
	var stdMargin = CV.HudObject.stdMargin;

	var geometry = new THREE.RingGeometry( stdWidth * 0.9, stdWidth, 50 );

	THREE.Mesh.call( this, geometry, new THREE.MeshBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ) );

	this.name = "CV.ProgressDial";
	this.domObjects = [];

	var offset = stdWidth + stdMargin;

	this.translateX( -offset * 5 );
	this.translateY(  offset );

	this.rotateOnAxis( new THREE.Vector3( 0, 0, 1 ), Math.PI / 2 );

	this.visible  = false;
	this.isVisible = true;

	this.addEventListener( "removed", this.removeDomObjects );

	return this;

}

CV.ProgressDial.prototype = Object.create( THREE.Mesh.prototype );

Object.assign( CV.ProgressDial.prototype, CV.HudObject.prototype );

CV.ProgressDial.prototype.contructor = CV.ProgressDial;

CV.ProgressDial.prototype.set = function ( progress ) {

	this.progress = progress;

	var l = Math.min( 100, Math.round( progress ) );
	var faces = this.geometry.faces;

	for ( var i = 0; i < l; i++ ) {

		faces[ 99 - i ].color.set( 0x00ff00 );

	}

	this.geometry.colorsNeedUpdate = true;

}

CV.ProgressDial.prototype.add = function ( progress ) {

	this.set( this.progress + progress );

}

CV.ProgressDial.prototype.start = function () {

	var faces = this.geometry.faces;

	for ( var i = 0; i < 100; i++ ) {

		faces[i].color.set( 0x333333 );

	}

	this.geometry.colorsNeedUpdate = true;
	this.progress = 0;
	this.visible = this.isVisible;

}

CV.ProgressDial.prototype.end = function () {

	var self = this;

	setTimeout( function () { self.visible = false; }, 500 );

}

CV.ProgressDial.prototype.setVisibility = function ( visibility ) {

	this.isVisible = visibility;
	this.visible = ( this.visible && visibility );

}

// EOF<commit_msg>make faces coloured a multiple of 2 to ensure 'square' ends of progress bar.<commit_after>"use strict";

var CV = CV || {};

CV.ProgressDial = function ( container ) {

	var stdWidth  = CV.HudObject.stdWidth;
	var stdMargin = CV.HudObject.stdMargin;

	var geometry = new THREE.RingGeometry( stdWidth * 0.9, stdWidth, 50 );

	THREE.Mesh.call( this, geometry, new THREE.MeshBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ) );

	this.name = "CV.ProgressDial";
	this.domObjects = [];

	var offset = stdWidth + stdMargin;

	this.translateX( -offset * 5 );
	this.translateY(  offset );

	this.rotateOnAxis( CV.upAxis, Math.PI / 2 );

	this.visible  = false;
	this.isVisible = true;

	this.addEventListener( "removed", this.removeDomObjects );

	return this;

}

CV.ProgressDial.prototype = Object.create( THREE.Mesh.prototype );

Object.assign( CV.ProgressDial.prototype, CV.HudObject.prototype );

CV.ProgressDial.prototype.contructor = CV.ProgressDial;

CV.ProgressDial.prototype.set = function ( progress ) {

	this.progress = progress;

	var l = Math.floor( Math.min( 100, Math.round( progress ) ) / 2 ) * 2;
	var faces = this.geometry.faces;

	for ( var i = 0; i < l; i++ ) {

		faces[ 99 - i ].color.set( 0x00ff00 );

	}

	this.geometry.colorsNeedUpdate = true;

}

CV.ProgressDial.prototype.add = function ( progress ) {

	this.set( this.progress + progress );

}

CV.ProgressDial.prototype.start = function () {

	var faces = this.geometry.faces;

	for ( var i = 0; i < 100; i++ ) {

		faces[i].color.set( 0x333333 );

	}

	this.geometry.colorsNeedUpdate = true;
	this.progress = 0;
	this.visible = this.isVisible;

}

CV.ProgressDial.prototype.end = function () {

	var self = this;

	setTimeout( function () { self.visible = false; }, 500 );

}

CV.ProgressDial.prototype.setVisibility = function ( visibility ) {

	this.isVisible = visibility;
	this.visible = ( this.visible && visibility );

}

// EOF<|endoftext|>


<commit_before>import * as R from 'ramda';
import CodeMirror from 'codemirror';
import 'codemirror/addon/mode/simple';
import 'codemirror/addon/mode/overlay';
import 'codemirror/mode/clike/clike';

import 'codemirror/addon/edit/closebrackets';
import 'codemirror/addon/edit/trailingspace';
import 'codemirror/addon/comment/comment';
import 'codemirror/addon/scroll/simplescrollbars';

/* eslint-disable max-len */
const XOD_TYPE_NAMES = /(Number|NodeId|Context|DirtyFlags|TimeMs|u?int\d{1,2}_t|size_t|XString|State|List|Iterator)\b/gm;
const XOD_BUILTIN_NAMES = /(getValue|emitValue|isInputDirty|transactionTime|setTimeout|clearTimeout|isTimedOut|evaluate|getState)\b/gm;
const ARDUINO_BUILTIN_NAMES = /((digital|analog)(Read|Write)|pinMode|analogReference)\b/gm;
const XOD_TAG_NAMES = /(((input|output)_[A-Za-z0-9_]+)|GENERATED_CODE)\b/gm;
/* eslint-enable max-len */

// Function that creates a new RegExp to prevent highlighting
// a variable part that matches with things above.
// E.G. `myNumber`, `newState`, `agetValue` and etc
// :: [RegExp] -> RegExp
const createCMHack = R.compose(
  r => new RegExp(`[A-Za-z0-9_]+(${r})`, 'gm'),
  R.join('|'),
  R.map(R.compose(R.replace('\\b', ''), R.prop('source')))
);

(() => {
  let xodOverlay;
  CodeMirror.defineMode('xodCpp', (config, parserConfig) => {
    xodOverlay = CodeMirror.simpleMode(config, {
      mode: { spec: CodeMirror.modes.clike },
      start: [
        {
          regex: XOD_TAG_NAMES,
          token: 'tag',
        },
        {
          regex: XOD_TYPE_NAMES,
          token: 'type',
        },
        {
          regex: XOD_BUILTIN_NAMES,
          token: 'builtin',
        },
        {
          regex: ARDUINO_BUILTIN_NAMES,
          token: 'builtin',
        },
        {
          regex: createCMHack([
            XOD_TYPE_NAMES,
            XOD_BUILTIN_NAMES,
            ARDUINO_BUILTIN_NAMES,
            XOD_TAG_NAMES,
          ]),
          token: 'variable',
        },
        { regex: /\/\/.*/, token: 'comment' },
        { regex: /\/\*/, token: 'comment', next: 'comment' },
      ],
      comment: [
        { regex: /.*?\*\//, token: 'comment', next: 'start' },
        { regex: /.*/, token: 'comment' },
      ],
    });
    return CodeMirror.overlayMode(
      CodeMirror.getMode(config, parserConfig.backdrop || 'text/x-c++src'),
      xodOverlay
    );
  });
  CodeMirror.defineMIME('text/x-c++xod', 'xodCpp');

  const maybeClearLines = (cm, event) => {
    if (event.keyCode !== 13) return;
    const cursor = cm.getCursor();
    if (cursor.line <= 0) return;

    for (
      let ln = cursor.line;
      !/\S/.test(cm.getLine(ln));
      ln-- // eslint-disable-line no-plusplus
    ) {
      cm.replaceRange(
        '',
        new CodeMirror.Pos(ln, 0),
        new CodeMirror.Pos(ln, cm.getLine(ln).length)
      );
    }
  };

  CodeMirror.defineOption('autoClearEmptyLines', false, (cm, value) => {
    /* eslint-disable no-param-reassign */
    if (value && !cm.state.clearEmptyLines) {
      cm.state.clearEmptyLines = { prev: cm.getCursor() };
      cm.on('keydown', maybeClearLines);
    } else if (!value && cm.state.clearEmptyLines) {
      cm.off('keydown', maybeClearLines);
      cm.state.clearEmptyLines = null;
    }
    /* eslint-enable no-param-reassign */
  });
})();
<commit_msg>tweak(xod-client): highlight a few more core functions in C++ editor<commit_after>import * as R from 'ramda';
import CodeMirror from 'codemirror';
import 'codemirror/addon/mode/simple';
import 'codemirror/addon/mode/overlay';
import 'codemirror/mode/clike/clike';

import 'codemirror/addon/edit/closebrackets';
import 'codemirror/addon/edit/trailingspace';
import 'codemirror/addon/comment/comment';
import 'codemirror/addon/scroll/simplescrollbars';

/* eslint-disable max-len */
const XOD_TYPE_NAMES = /(Number|NodeId|Context|DirtyFlags|TimeMs|u?int\d{1,2}_t|size_t|XString|State|List|Iterator)\b/gm;
const XOD_BUILTIN_NAMES = /(getValue|emitValue|isInputDirty|transactionTime|setTimeout|clearTimeout|isTimedOut|evaluate|getState|raiseError|isSettingUp|getError)\b/gm;
const ARDUINO_BUILTIN_NAMES = /((digital|analog)(Read|Write)|pinMode|analogReference)\b/gm;
const XOD_TAG_NAMES = /(((input|output)_[A-Za-z0-9_]+)|GENERATED_CODE)\b/gm;
/* eslint-enable max-len */

// Function that creates a new RegExp to prevent highlighting
// a variable part that matches with things above.
// E.G. `myNumber`, `newState`, `agetValue` and etc
// :: [RegExp] -> RegExp
const createCMHack = R.compose(
  r => new RegExp(`[A-Za-z0-9_]+(${r})`, 'gm'),
  R.join('|'),
  R.map(R.compose(R.replace('\\b', ''), R.prop('source')))
);

(() => {
  let xodOverlay;
  CodeMirror.defineMode('xodCpp', (config, parserConfig) => {
    xodOverlay = CodeMirror.simpleMode(config, {
      mode: { spec: CodeMirror.modes.clike },
      start: [
        {
          regex: XOD_TAG_NAMES,
          token: 'tag',
        },
        {
          regex: XOD_TYPE_NAMES,
          token: 'type',
        },
        {
          regex: XOD_BUILTIN_NAMES,
          token: 'builtin',
        },
        {
          regex: ARDUINO_BUILTIN_NAMES,
          token: 'builtin',
        },
        {
          regex: createCMHack([
            XOD_TYPE_NAMES,
            XOD_BUILTIN_NAMES,
            ARDUINO_BUILTIN_NAMES,
            XOD_TAG_NAMES,
          ]),
          token: 'variable',
        },
        { regex: /\/\/.*/, token: 'comment' },
        { regex: /\/\*/, token: 'comment', next: 'comment' },
      ],
      comment: [
        { regex: /.*?\*\//, token: 'comment', next: 'start' },
        { regex: /.*/, token: 'comment' },
      ],
    });
    return CodeMirror.overlayMode(
      CodeMirror.getMode(config, parserConfig.backdrop || 'text/x-c++src'),
      xodOverlay
    );
  });
  CodeMirror.defineMIME('text/x-c++xod', 'xodCpp');

  const maybeClearLines = (cm, event) => {
    if (event.keyCode !== 13) return;
    const cursor = cm.getCursor();
    if (cursor.line <= 0) return;

    for (
      let ln = cursor.line;
      !/\S/.test(cm.getLine(ln));
      ln-- // eslint-disable-line no-plusplus
    ) {
      cm.replaceRange(
        '',
        new CodeMirror.Pos(ln, 0),
        new CodeMirror.Pos(ln, cm.getLine(ln).length)
      );
    }
  };

  CodeMirror.defineOption('autoClearEmptyLines', false, (cm, value) => {
    /* eslint-disable no-param-reassign */
    if (value && !cm.state.clearEmptyLines) {
      cm.state.clearEmptyLines = { prev: cm.getCursor() };
      cm.on('keydown', maybeClearLines);
    } else if (!value && cm.state.clearEmptyLines) {
      cm.off('keydown', maybeClearLines);
      cm.state.clearEmptyLines = null;
    }
    /* eslint-enable no-param-reassign */
  });
})();
<|endoftext|>


<commit_before>class MegaMenu {
  constructor(element) {
    this.element = element;
    this.addListeners = this.addListeners.bind(this);
    this.toggleMenu = this.toggleMenu.bind(this);
    this.toggleSubMenu = this.toggleSubMenu.bind(this);
    this.closeAll = this.closeAll.bind(this);
    this.closeMenu = this.closeMenu.bind(this);
    this.openMenu = this.openMenu.bind(this);
    this.getMenu = this.getMenu.bind(this);
    this.isWideScreen = this.isWideScreen.bind(this);
    this.addListeners();
  }

  addListeners() {
    const menus = Array.from(this.element.querySelectorAll('.vetnav-level1'));
    const submenus = Array.from(this.element.querySelectorAll('.vetnav-level2'));
    const backs = Array.from(this.element.querySelectorAll('.vetnav-back'));

    menus.forEach((menu) => {
      menu.addEventListener('click', this.toggleMenu);
    });

    submenus.forEach((submenu) => {
      submenu.addEventListener('click', this.toggleSubMenu);
    });

    backs.forEach((back) => {
      back.addEventListener('click', this.closeMenu);
    });
  }

  closeAll() {
    const menus = this.element.querySelectorAll('[aria-expanded=true]');
    Array.from(menus).forEach((m) => {
      const whichMenu = this.getMenu(m.getAttribute('aria-controls'));
      whichMenu.setAttribute('hidden','hidden');
      m.setAttribute('aria-expanded', false);
    });
  }

  closeMenu(event) {
    const target = event.target;
    const menu = target.getAttribute('aria-controls');

    target.setAttribute('aria-expanded', false);
    this.getMenu(target.getAttribute('aria-controls')).setAttribute('hidden','hidden');
  }

  getMenu(idName) {
    const selector = `#${idName}`;
    return this.element.querySelector(selector);
  }

  isWideScreen() {
    return matchMedia('(min-width: 768px)').matches;
  }

  openMenu(event) {
    const target = event.target;
    const menu = target.getAttribute('aria-controls');

    target.setAttribute('aria-expanded', true);
    this.getMenu(menu).removeAttribute('hidden','hidden');
  }

  toggleMenu(event) {
    const eTarget = event.target;
    const whichMenu = this.getMenu(eTarget.getAttribute('aria-controls'));

    if(eTarget.getAttribute('aria-expanded') == 'true') {
      this.closeMenu(event);
    } else {
      this.closeAll();
      this.openMenu(event);

      /*
      Open the first sub-menu and expand first trigger
      when the breakpoint > 768
      */
      if(matchMedia('(min-width: 768px)').matches && whichMenu.querySelector('.vetnav-panel--submenu')){
        whichMenu.querySelector('.vetnav-trigger').setAttribute('aria-expanded', true);
        whichMenu.querySelector('.vetnav-panel--submenu').removeAttribute('hidden');
      }
    }
  }

  toggleSubMenu(event) {
    const submenus = Array.from(this.element.querySelectorAll('.vetnav-panel--submenu'));
    const triggers = Array.from(this.element.querySelectorAll('.vetnav-level2'));

    submenus.forEach((sm) => {
      sm.setAttribute('hidden','hidden');
    });

    triggers.forEach((sm) => {
      sm.setAttribute('aria-expanded', false);
    });

    const showCurrent = submenus.find((sm) => {
      return sm.id == event.target.getAttribute('aria-controls');
    });

    showCurrent.removeAttribute('hidden');
    event.target.setAttribute('aria-expanded', true);
  }
}

export default MegaMenu;

function toggleMobileMenu(event) {
  const toggleNav = event.currentTarget.querySelectorAll('button');
  const menu = document.querySelector('#vetnav');

  Array.from(toggleNav).forEach((tn) => {
    if( tn.hidden ) {
      tn.removeAttribute('hidden');
    } else {
      tn.setAttribute('hidden','hidden');
    }
  });

  if( menu.hidden ) {
    menu.removeAttribute('hidden');
  } else {
    menu.setAttribute('hidden','hidden');
  }

  document.body.classList.toggle('vetnav-isopen');
}

document.addEventListener('DOMContentLoaded', () => {
  const mm = new MegaMenu(document.querySelector('#vetnav-menu'));

  if(mm.isWideScreen()) {
    document.querySelector('#vetnav').removeAttribute('hidden');
  } else {
    document.querySelector('#vetnav-controls').addEventListener('click', toggleMobileMenu);
  }
});

<commit_msg>Re-initialize menu when the window is resized.<commit_after>class MegaMenu {
  constructor(element) {
    this.element = element;
    this.addListeners = this.addListeners.bind(this);
    this.toggleMenu = this.toggleMenu.bind(this);
    this.toggleSubMenu = this.toggleSubMenu.bind(this);
    this.closeAll = this.closeAll.bind(this);
    this.closeMenu = this.closeMenu.bind(this);
    this.openMenu = this.openMenu.bind(this);
    this.getMenu = this.getMenu.bind(this);
    this.isWideScreen = this.isWideScreen.bind(this);
    this.addListeners();
  }

  addListeners() {
    const menus = Array.from(this.element.querySelectorAll('.vetnav-level1'));
    const submenus = Array.from(this.element.querySelectorAll('.vetnav-level2'));
    const backs = Array.from(this.element.querySelectorAll('.vetnav-back'));

    menus.forEach((menu) => {
      menu.addEventListener('click', this.toggleMenu);
    });

    submenus.forEach((submenu) => {
      submenu.addEventListener('click', this.toggleSubMenu);
    });

    backs.forEach((back) => {
      back.addEventListener('click', this.closeMenu);
    });
  }

  closeAll() {
    const menus = this.element.querySelectorAll('[aria-expanded=true]');
    Array.from(menus).forEach((m) => {
      const whichMenu = this.getMenu(m.getAttribute('aria-controls'));
      whichMenu.setAttribute('hidden','hidden');
      m.setAttribute('aria-expanded', false);
    });
  }

  closeMenu(event) {
    const target = event.target;
    const menu = target.getAttribute('aria-controls');

    target.setAttribute('aria-expanded', false);
    this.getMenu(target.getAttribute('aria-controls')).setAttribute('hidden','hidden');
  }

  getMenu(idName) {
    const selector = `#${idName}`;
    return this.element.querySelector(selector);
  }

  isWideScreen() {
    return matchMedia('(min-width: 768px)').matches;
  }

  openMenu(event) {
    const target = event.target;
    const menu = target.getAttribute('aria-controls');

    target.setAttribute('aria-expanded', true);
    this.getMenu(menu).removeAttribute('hidden','hidden');
  }

  toggleMenu(event) {
    const eTarget = event.target;
    const whichMenu = this.getMenu(eTarget.getAttribute('aria-controls'));

    if(eTarget.getAttribute('aria-expanded') === 'true') {
      this.closeMenu(event);
    } else {
      this.closeAll();
      this.openMenu(event);

      /*
      Open the first sub-menu and expand first trigger
      when the breakpoint > 768
      */
      if(this.isWideScreen() && whichMenu.querySelector('.vetnav-panel--submenu')){
        whichMenu.querySelector('.vetnav-trigger').setAttribute('aria-expanded', true);
        whichMenu.querySelector('.vetnav-panel--submenu').removeAttribute('hidden');
      }
    }
  }

  toggleSubMenu(event) {
    const submenus = Array.from(this.element.querySelectorAll('.vetnav-panel--submenu'));
    const triggers = Array.from(this.element.querySelectorAll('.vetnav-level2'));

    submenus.forEach((sm) => {
      sm.setAttribute('hidden','hidden');
    });

    triggers.forEach((sm) => {
      sm.setAttribute('aria-expanded', false);
    });

    const showCurrent = submenus.find((sm) => {
      return sm.id == event.target.getAttribute('aria-controls');
    });

    showCurrent.removeAttribute('hidden');
    event.target.setAttribute('aria-expanded', true);
  }
}

export default MegaMenu;

function toggleMobileMenu(event) {
  const toggleNav = event.currentTarget.querySelectorAll('button');
  const menu = document.querySelector('#vetnav');

  Array.from(toggleNav).forEach((tn) => {
    if( tn.hidden ) {
      tn.removeAttribute('hidden');
    } else {
      tn.setAttribute('hidden','hidden');
    }
  });

  if( menu.hidden ) {
    menu.removeAttribute('hidden');
  } else {
    menu.setAttribute('hidden','hidden');
  }

  document.body.classList.toggle('vetnav-isopen');
}
function reInitMenu() {
  const mm = new MegaMenu(document.querySelector('#vetnav-menu'));
  const vetnav = document.querySelector('#vetnav');

  if(mm.isWideScreen()) {
    vetnav.removeAttribute('hidden');
    document.body.classList.remove('vetnav-isopen');
  } else {
    vetnav.setAttribute('hidden', 'hidden');
    document.querySelector('#vetnav-controls').addEventListener('click', toggleMobileMenu);
  }
}

(function() {
  document.addEventListener('DOMContentLoaded', reInitMenu);
  window.addEventListener('resize', reInitMenu);
}())
<|endoftext|>


<commit_before>// Builds the different HTML parts as needed.

// Gets the link/style data.
function getData() {

    var links = [];
    var cssArr = [];

    // To avoid mismatches we're running sychonously.
    // TODO: find async way to do this so it doesn't block browser.
    $.ajaxSetup({async:false});

    // Find all stylesheet <link>s and


<commit_before>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

define(function (require, exports, module) {
    "use strict";

    var _ = require("lodash");

    /**
     * Given a raw array of layers, will construct the layer tree
     * and create the Layer objects
     *
     * @constructor
     * @param {object} rawDocument
     * @param {Array.<Layer>} layerArray Array of the layers in document
     */
    var LayerTree = function (rawDocument, layerArray) {
        this._layerArray = layerArray;

        this._layerSet = layerArray.reduce(function (layerMap, layer) {
            layerMap[layer.id] = layer;
            return layerMap;
        }, {});

        // puts the layers in index order
        this._layerArray.reverse();

        // Since PS starts indices by 1 for layers, we're adding an undefined layer at the start
        // Only time a layer index is 0 is when we're referencing TO the background layer in an image
        // Document.targetLayers will always be 0 indexed, and are layer agnostic
        this._layerArray.unshift(null);
        delete this._layerArray[0];

        this._buildTree();
        this._updateSelection(rawDocument.targetLayers);
        this._hasBackgroundLayer = rawDocument.hasBackgroundLayer;
        this._numberOfLayers = rawDocument.numberOfLayers;
    };

    Object.defineProperties(LayerTree.prototype, {
        "topLayers": {
            get: function () { return this._topLayers; }
        },
        "layerSet": {
            get: function () { return this._layerSet; }
        },
        "layerArray": {
            get: function () {return this._layerArray; }
        },
        "hasBackgroundLayer": {
            get: function () {return this._hasBackgroundLayer; }
        },
        "numberOfLayers": {
            get: function () {return this._numberOfLayers; }
        }
    });

    /**
     * @private
     * @type {Array.<Layer>}
     */
    LayerTree.prototype._topLayers = null;

    /**
     * @private
     * @type {Array.<Layer>}
     */
    LayerTree.prototype._layerArray = null;

    /**
     * @private
     * @type {Object.<number>} ID look up table for layers in the tree
     */
    LayerTree.prototype._layerSet = null;

    /**
     * @private
     * @type {number} Number of layers in the tree
     */
    LayerTree.prototype._numberOfLayers = null;

    /**
     * @private
     * @type {boolean} Whether or not there is a background layer in the layer tree
     */
    LayerTree.prototype._hasBackgroundLayer = null;

    /**
     * Photoshop gives us layers in a flat array with hidden endGroup layers
     * This function parses the layer array into a tree where layer's children
     * are in a children object, and each layer also have a parent object pointing at their parent
     *
     * @private
     */
    LayerTree.prototype._buildTree = function () {
        var root = [],
            currentParent = null,
            depth = 0;

        _.forEachRight(this._layerArray, function (layer) {
            if (!layer) {
                return;
            }
            layer._children = [];
            layer._parent = currentParent;
            layer._depth = depth;

            if (currentParent) {
                currentParent._children.push(layer);
            } else {
                root.push(layer);
            }

            // If we're encountering a groupend layer, we go up a level
            if (layer.kind === layer.layerKinds.GROUPEND) {
                // TODO: Assert to see if currentParent is null here, it should never be
                currentParent = currentParent.parent;
                depth--;
            } else if (layer.kind === layer.layerKinds.GROUP) {
                currentParent = layer;
                depth++;
            }
        }.bind(this));

        this._topLayers = root;

    };

    /**
     * Given the targetLayers property of a document
     * Will mark the selected layers in the tree
     *
     * @private
     * @param {Array.<object>} targetLayers
     */
    LayerTree.prototype._updateSelection = function (targetLayers) {
        // update the selection property of selected layers
        var selectedIndices = targetLayers || [];
        selectedIndices.forEach(function (obj) {
            this._layerArray[obj.index + 1]._selected = true;
        }, this);
    };

    /**
     * Given the new layer ID array after a reorder
     * sorts the layer array, and rebuilds the layer tree
     * This function does not check to see if layer IDs are valid
     * buildTree might fail if a group end layer is encountered unexpectedly
     *
     * @param {Array.<number>} layerIDs Layer IDs in the document order
     */
    LayerTree.prototype.updateLayerOrder = function (layerIDs) {
        this._layerArray = layerIDs.map(function (id, index) {
            var layer = this._layerSet[id];
            layer._index = layerIDs.length - index;
            return layer;
        }, this);

        // puts the layers in index order
        this._layerArray.reverse();

        // Since PS starts indices by 1 for layers, we're adding an undefined layer at the start
        // Only time a layer index is 0 is when we're referencing TO the background layer in an image
        // Document.targetLayers will always be 0 indexed, and are layer agnostic
        this._layerArray.unshift(null);
        delete this._layerArray[0];

        this._buildTree();
    };

    /**
     * Wrapper for Array.prototype.forEach on layerArray,
     * Later on we can replace this with a traversal function
     *
     * @param {Function} callback Function to execute for each element
     * @param {Object} thisArg Value to use as this when executing callback
     */
    LayerTree.prototype.forEach = function (callback, thisArg) {
        return this._layerArray.forEach(callback, thisArg);
    };

    /**
     * Wrapper for Array.prototype.map on layerArray,
     * Later on we can replace this with a traversal function
     *
     * @param {Function} callback Function to execute for each element
     * @param {Object} thisArg Value to use as this when executing callback
     */
    LayerTree.prototype.map = function (callback, thisArg) {
        return this._layerArray.map(callback, thisArg);
    };

    /**
     * Returns the layer with the given ID from the tree
     */
    LayerTree.prototype.getLayerByID = function (id) {
        return this._layerSet[id];
    };

    module.exports = LayerTree;
});
<commit_msg>Fix getLayerByID jsdoc<commit_after>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

define(function (require, exports, module) {
    "use strict";

    var _ = require("lodash");

    /**
     * Given a raw array of layers, will construct the layer tree
     * and create the Layer objects
     *
     * @constructor
     * @param {object} rawDocument
     * @param {Array.<Layer>} layerArray Array of the layers in document
     */
    var LayerTree = function (rawDocument, layerArray) {
        this._layerArray = layerArray;

        this._layerSet = layerArray.reduce(function (layerMap, layer) {
            layerMap[layer.id] = layer;
            return layerMap;
        }, {});

        // puts the layers in index order
        this._layerArray.reverse();

        // Since PS starts indices by 1 for layers, we're adding an undefined layer at the start
        // Only time a layer index is 0 is when we're referencing TO the background layer in an image
        // Document.targetLayers will always be 0 indexed, and are layer agnostic
        this._layerArray.unshift(null);
        delete this._layerArray[0];

        this._buildTree();
        this._updateSelection(rawDocument.targetLayers);
        this._hasBackgroundLayer = rawDocument.hasBackgroundLayer;
        this._numberOfLayers = rawDocument.numberOfLayers;
    };

    Object.defineProperties(LayerTree.prototype, {
        "topLayers": {
            get: function () { return this._topLayers; }
        },
        "layerSet": {
            get: function () { return this._layerSet; }
        },
        "layerArray": {
            get: function () {return this._layerArray; }
        },
        "hasBackgroundLayer": {
            get: function () {return this._hasBackgroundLayer; }
        },
        "numberOfLayers": {
            get: function () {return this._numberOfLayers; }
        }
    });

    /**
     * @private
     * @type {Array.<Layer>}
     */
    LayerTree.prototype._topLayers = null;

    /**
     * @private
     * @type {Array.<Layer>}
     */
    LayerTree.prototype._layerArray = null;

    /**
     * @private
     * @type {Object.<number>} ID look up table for layers in the tree
     */
    LayerTree.prototype._layerSet = null;

    /**
     * @private
     * @type {number} Number of layers in the tree
     */
    LayerTree.prototype._numberOfLayers = null;

    /**
     * @private
     * @type {boolean} Whether or not there is a background layer in the layer tree
     */
    LayerTree.prototype._hasBackgroundLayer = null;

    /**
     * Photoshop gives us layers in a flat array with hidden endGroup layers
     * This function parses the layer array into a tree where layer's children
     * are in a children object, and each layer also have a parent object pointing at their parent
     *
     * @private
     */
    LayerTree.prototype._buildTree = function () {
        var root = [],
            currentParent = null,
            depth = 0;

        _.forEachRight(this._layerArray, function (layer) {
            if (!layer) {
                return;
            }
            layer._children = [];
            layer._parent = currentParent;
            layer._depth = depth;

            if (currentParent) {
                currentParent._children.push(layer);
            } else {
                root.push(layer);
            }

            // If we're encountering a groupend layer, we go up a level
            if (layer.kind === layer.layerKinds.GROUPEND) {
                // TODO: Assert to see if currentParent is null here, it should never be
                currentParent = currentParent.parent;
                depth--;
            } else if (layer.kind === layer.layerKinds.GROUP) {
                currentParent = layer;
                depth++;
            }
        }.bind(this));

        this._topLayers = root;

    };

    /**
     * Given the targetLayers property of a document
     * Will mark the selected layers in the tree
     *
     * @private
     * @param {Array.<object>} targetLayers
     */
    LayerTree.prototype._updateSelection = function (targetLayers) {
        // update the selection property of selected layers
        var selectedIndices = targetLayers || [];
        selectedIndices.forEach(function (obj) {
            this._layerArray[obj.index + 1]._selected = true;
        }, this);
    };

    /**
     * Given the new layer ID array after a reorder
     * sorts the layer array, and rebuilds the layer tree
     * This function does not check to see if layer IDs are valid
     * buildTree might fail if a group end layer is encountered unexpectedly
     *
     * @param {Array.<number>} layerIDs Layer IDs in the document order
     */
    LayerTree.prototype.updateLayerOrder = function (layerIDs) {
        this._layerArray = layerIDs.map(function (id, index) {
            var layer = this._layerSet[id];
            layer._index = layerIDs.length - index;
            return layer;
        }, this);

        // puts the layers in index order
        this._layerArray.reverse();

        // Since PS starts indices by 1 for layers, we're adding an undefined layer at the start
        // Only time a layer index is 0 is when we're referencing TO the background layer in an image
        // Document.targetLayers will always be 0 indexed, and are layer agnostic
        this._layerArray.unshift(null);
        delete this._layerArray[0];

        this._buildTree();
    };

    /**
     * Wrapper for Array.prototype.forEach on layerArray,
     * Later on we can replace this with a traversal function
     *
     * @param {Function} callback Function to execute for each element
     * @param {Object} thisArg Value to use as this when executing callback
     */
    LayerTree.prototype.forEach = function (callback, thisArg) {
        return this._layerArray.forEach(callback, thisArg);
    };

    /**
     * Wrapper for Array.prototype.map on layerArray,
     * Later on we can replace this with a traversal function
     *
     * @param {Function} callback Function to execute for each element
     * @param {Object} thisArg Value to use as this when executing callback
     */
    LayerTree.prototype.map = function (callback, thisArg) {
        return this._layerArray.map(callback, thisArg);
    };

    /**
     * Returns the layer with the given ID from the tree
     * @param {number} id ID of the layer
     * @return {Layer}
     */
    LayerTree.prototype.getLayerByID = function (id) {
        return this._layerSet[id];
    };

    module.exports = LayerTree;
});
<|endoftext|>


<commit_before>/**
 * @fileoverview 控制台UI
 * @authors liangdong2 <liangdong2@staff.sina.com.cn> <pillar0514@gmail.com>
 */
define('mods/view/config',function(require,exports,module){

	var $ = require('lib');
	var $limit = require('lib/kit/num/limit');
	var $view = require('lib/mvc/view');
	var $configModel = require('mods/model/config');
	var $channel = require('mods/channel/global');

	//设置面板
	var Config = $view.extend({
		defaults : {
			node : '#config-panel',
			events : {
				'[data-role="count"] input' : 'limitLotterys',
				'[data-role="reset"] click' : 'confirmReset',
				'[data-role="ok"] click' : 'renderLotteryBox',
				'[data-role="cancel"] click' : 'cancelReset'
			}
		},
		build : function(){
			var conf = this.conf;
			this.root = this.role('root');
			this.elDrawCount = $('#draw-count');
			this.role('reset').blur();

			this.renderDrawCount();
			this.renderLotteryCount();
		},
		setEvents : function(action){
			this.delegate(action);
			var proxy = this.proxy();
			$configModel.on('change:drawCount', proxy('renderDrawCount'));
			$configModel.on('change:lotteryCount', proxy('renderLotteryCount'));
		},
		//渲染可抽取数量
		renderDrawCount : function(){
			var count = $configModel.get('drawCount');
			this.elDrawCount.html(count);
		},
		//渲染待抽奖奖票数量
		renderLotteryCount : function(){
			var count = $configModel.get('lotteryCount');
			this.role('count').val(count);
		},
		showSettings : function(){
			this.root.addClass('open');
		},
		hideSettings : function(){
			this.root.removeClass('open');
		},
		limitLotterys : function(){
			var input = this.role('count');
			var val = parseInt(input.val(), 10) || 0;
			var min = parseInt(input.attr('min'), 10) || 0;
			var max = parseInt(input.attr('max'), 10) || 10000;
			val = $limit(val, min, max);
			input.val(val);
		},
		confirmReset : function(){
			this.role('reset').hide();
			this.role('confirm').show();
		},
		renderLotteryBox : function(){
			var that = this;
			var count = this.role('count').val();
			count = parseInt(count, 10) || $configModel.get('lotteryCount');
			$configModel.set('lotteryCount', count);

			var csvInput = this.role('file').get(0);
			var blob = csvInput.files[0];
			if(blob){
				var reader = new FileReader();
				reader.readAsText(blob, 'GBK');
				reader.onload = function(){
					var arr = reader.result.split(/[\r\n]+/);
					arr = arr.filter(function(str){
						return !!str.toString().trim();
					}).map(function(str){
						var item = {};
						var itemArr = str.split(/[,;\t]+/);
						item.id = itemArr[0].toString().trim();
						item.name = itemArr[1].toString().trim();
						return item;
					});
					$configModel.set('lotteryData', arr);
					that.triggerReset();
				};
			}else{
				$configModel.set('lotteryData', null);
				this.triggerReset();
			}
		},
		//触发奖票数据重置
		triggerReset : function(){
			$channel.trigger('reset');
			this.role('confirm').hide();
			this.role('reset').show();
			this.showTip('已重置奖项，所有抽奖数据清空。');
		},
		cancelReset : function(){
			this.role('count').val(
				$configModel.get('lotteryCount')
			);
			this.role('confirm').hide();
			this.role('reset').show();
			this.showTip('已恢复为上一次抽奖后的状态。');
		},
		showTip : function(str){
			$channel.trigger('tip', str);
		}
	});

	module.exports = Config;

});
<commit_msg>避免号码重复现象。<commit_after>/**
 * @fileoverview 控制台UI
 * @authors liangdong2 <liangdong2@staff.sina.com.cn> <pillar0514@gmail.com>
 */
define('mods/view/config',function(require,exports,module){

	var $ = require('lib');
	var $limit = require('lib/kit/num/limit');
	var $view = require('lib/mvc/view');
	var $configModel = require('mods/model/config');
	var $channel = require('mods/channel/global');

	//设置面板
	var Config = $view.extend({
		defaults : {
			node : '#config-panel',
			events : {
				'[data-role="count"] input' : 'limitLotterys',
				'[data-role="reset"] click' : 'confirmReset',
				'[data-role="ok"] click' : 'renderLotteryBox',
				'[data-role="cancel"] click' : 'cancelReset'
			}
		},
		build : function(){
			var conf = this.conf;
			this.root = this.role('root');
			this.elDrawCount = $('#draw-count');
			this.role('reset').blur();

			this.renderDrawCount();
			this.renderLotteryCount();
		},
		setEvents : function(action){
			this.delegate(action);
			var proxy = this.proxy();
			$configModel.on('change:drawCount', proxy('renderDrawCount'));
			$configModel.on('change:lotteryCount', proxy('renderLotteryCount'));
		},
		//渲染可抽取数量
		renderDrawCount : function(){
			var count = $configModel.get('drawCount');
			this.elDrawCount.html(count);
		},
		//渲染待抽奖奖票数量
		renderLotteryCount : function(){
			var count = $configModel.get('lotteryCount');
			this.role('count').val(count);
		},
		showSettings : function(){
			this.root.addClass('open');
		},
		hideSettings : function(){
			this.root.removeClass('open');
		},
		limitLotterys : function(){
			var input = this.role('count');
			var val = parseInt(input.val(), 10) || 0;
			var min = parseInt(input.attr('min'), 10) || 0;
			var max = parseInt(input.attr('max'), 10) || 10000;
			val = $limit(val, min, max);
			input.val(val);
		},
		confirmReset : function(){
			this.role('reset').hide();
			this.role('confirm').show();
		},
		renderLotteryBox : function(){
			var that = this;
			var count = this.role('count').val();
			count = parseInt(count, 10) || $configModel.get('lotteryCount');
			$configModel.set('lotteryCount', count);

			var csvInput = this.role('file').get(0);
			var blob = csvInput.files[0];
			if(blob){
				var reader = new FileReader();
				reader.readAsText(blob, 'GBK');
				reader.onload = function(){
					var arr = reader.result.split(/[\r\n]+/);
					arr = arr.filter(function(str){
						return !!str.toString().trim();
					}).map(function(str){
						var item = {};
						var itemArr = str.split(/[,;\t]+/);
						item.id = itemArr[0].toString().trim();
						item.name = itemArr[1].toString().trim();
						return item;
					});

					//避免数据有误，出现号码重复的现象
					var hash = {};
					var data = [];
					arr.forEach(function(item){
						if(!hash[item.id]){
							hash[item.id] = true;
							data.push(item);
						}
					});

					$configModel.set('lotteryData', data);
					that.triggerReset();
				};
			}else{
				$configModel.set('lotteryData', null);
				this.triggerReset();
			}
		},
		//触发奖票数据重置
		triggerReset : function(){
			$channel.trigger('reset');
			this.role('confirm').hide();
			this.role('reset').show();
			this.showTip('已重置奖项，所有抽奖数据清空。');
		},
		cancelReset : function(){
			this.role('count').val(
				$configModel.get('lotteryCount')
			);
			this.role('confirm').hide();
			this.role('reset').show();
			this.showTip('已恢复为上一次抽奖后的状态。');
		},
		showTip : function(str){
			$channel.trigger('tip', str);
		}
	});

	module.exports = Config;

});
<|endoftext|>