<commit_before>module.exports = config => {
  var TEXT_NODE = 'span';
  var c = config;
  var modelAttrStart = 'data-gjs-';

  return {
    compTypes: '',

    /**
     * Parse style string to object
     * @param {string} str
     * @return {Object}
     * @example
     * var stl = ParserHtml.parseStyle('color:black; width:100px; test:value;');
     * console.log(stl);
     * // {color: 'black', width: '100px', test: 'value'}
     */
    parseStyle(str) {
      var result = {};
      var decls = str.split(';');
      for (var i = 0, len = decls.length; i < len; i++) {
        var decl = decls[i].trim();
        if (!decl) continue;
        var prop = decl.split(':');
        result[prop[0].trim()] = prop
          .slice(1)
          .join(':')
          .trim();
      }
      return result;
    },

    /**
     * Parse class string to array
     * @param {string} str
     * @return {Array<string>}
     * @example
     * var res = ParserHtml.parseClass('test1 test2 test3');
     * console.log(res);
     * // ['test1', 'test2', 'test3']
     */
    parseClass(str) {
      var result = [];
      var cls = str.split(' ');
      for (var i = 0, len = cls.length; i < len; i++) {
        var cl = cls[i].trim();
        var reg = new RegExp('^' + c.pStylePrefix);
        if (!cl || reg.test(cl)) continue;
        result.push(cl);
      }
      return result;
    },

    /**
     * Get data from the node element
     * @param  {HTMLElement} el DOM element to traverse
     * @return {Array<Object>}
     */
    parseNode(el) {
      const result = [];
      const nodes = el.childNodes;

      for (var i = 0, len = nodes.length; i < len; i++) {
        const node = nodes[i];
        const attrs = node.attributes || [];
        const attrsLen = attrs.length;
        const nodePrev = result[result.length - 1];
        const nodeChild = node.childNodes.length;
        const ct = this.compTypes;
        let model = {};

        // Start with understanding what kind of component it is
        if (ct) {
          let obj = '';

          // Iterate over all available Component Types and
          // the first with a valid result will be that component
          for (let it = 0; it < ct.length; it++) {
            obj = ct[it].model.isComponent(node);
            if (obj) break;
          }

          model = obj;
        }

        // Set tag name if not yet done
        if (!model.tagName) {
          model.tagName = node.tagName ? node.tagName.toLowerCase() : '';
        }

        if (attrsLen) {
          model.attributes = {};
        }

        // Parse attributes
        for (let j = 0; j < attrsLen; j++) {
          const nodeName = attrs[j].nodeName;
          let nodeValue = attrs[j].nodeValue;

          // Isolate attributes
          if (nodeName == 'style') {
            model.style = this.parseStyle(nodeValue);
          } else if (nodeName == 'class') {
            model.classes = this.parseClass(nodeValue);
          } else if (nodeName == 'contenteditable') {
            continue;
          } else if (nodeName.indexOf(modelAttrStart) === 0) {
            const modelAttr = nodeName.replace(modelAttrStart, '');
            const valueLen = nodeValue.length;
            const firstChar = nodeValue && nodeValue.substr(0, 1);
            const lastChar = nodeValue && nodeValue.substr(valueLen - 1);
            nodeValue = nodeValue === 'true' ? true : nodeValue;
            nodeValue = nodeValue === 'false' ? false : nodeValue;

            // Try to parse JSON where it's possible
            // I can get false positive here (eg. a selector '[data-attr]')
            // so put it under try/catch and let fail silently
            try {
              nodeValue =
                (firstChar == '{' && lastChar == '}') ||
                (firstChar == '[' && lastChar == ']')
                  ? JSON.parse(nodeValue)
                  : nodeValue;
            } catch (e) {}

            model[modelAttr] = nodeValue;
          } else {
            model.attributes[nodeName] = nodeValue;
          }
        }

        // Check for nested elements but avoid it if already provided
        if (nodeChild && !model.components) {
          // Avoid infinite nested text nodes
          const firstChild = node.childNodes[0];

          // If there is only one child and it's a TEXTNODE
          // just make it content of the current node
          if (nodeChild === 1 && firstChild.nodeType === 3) {
            !model.type && (model.type = 'text');
            model.content = firstChild.nodeValue;
          } else {
            model.components = this.parseNode(node);
          }
        }

        // Check if it's a text node and if could be moved to the prevous model
        if (model.type == 'textnode') {
          if (nodePrev && nodePrev.type == 'textnode') {
            nodePrev.content += model.content;
            continue;
          }

          // Throw away empty nodes (keep spaces)
          if (!config.keepEmptyTextNodes) {
            const content = node.nodeValue;
            if (content != ' ' && !content.trim()) {
              continue;
            }
          }
        }

        // If all children are texts and there is some textnode the parent should
        // be text too otherwise I'm unable to edit texnodes
        const comps = model.components;
        if (!model.type && comps) {
          let allTxt = 1;
          let foundTextNode = 0;

          for (let ci = 0; ci < comps.length; ci++) {
            const comp = comps[ci];
            const cType = comp.type;

            if (
              ['text', 'textnode'].indexOf(cType) < 0 &&
              c.textTags.indexOf(comp.tagName) < 0
            ) {
              allTxt = 0;
              break;
            }

            if (cType == 'textnode') {
              foundTextNode = 1;
            }
          }

          if (allTxt && foundTextNode) {
            model.type = 'text';
          }
        }

        // If tagName is still empty and is not a textnode, do not push it
        if (!model.tagName && model.type != 'textnode') {
          continue;
        }

        result.push(model);
      }

      return result;
    },

    /**
     * Parse HTML string to a desired model object
     * @param  {string} str HTML string
     * @param  {ParserCss} parserCss In case there is style tags inside HTML
     * @return {Object}
     */
    parse(str, parserCss) {
      var config = (c.em && c.em.get('Config')) || {};
      var res = { html: '', css: '' };
      var el = document.createElement('div');
      el.innerHTML = str;
      var scripts = el.querySelectorAll('script');
      var i = scripts.length;

      // Remove all scripts
      if (!config.allowScripts) {
        while (i--) scripts[i].parentNode.removeChild(scripts[i]);
      }

      // Detach style tags and parse them
      if (parserCss) {
        var styleStr = '';
        var styles = el.querySelectorAll('style');
        var j = styles.length;

        while (j--) {
          styleStr = styles[j].innerHTML + styleStr;
          styles[j].parentNode.removeChild(styles[j]);
        }

        if (styleStr) res.css = parserCss.parse(styleStr);
      }

      var result = this.parseNode(el);

      if (result.length == 1) result = result[0];

      res.html = result;

      return res;
    }
  };
};
<commit_msg>Update the class parser in HTML<commit_after>module.exports = config => {
  var TEXT_NODE = 'span';
  var c = config;
  var modelAttrStart = 'data-gjs-';

  return {
    compTypes: '',

    /**
     * Parse style string to object
     * @param {string} str
     * @return {Object}
     * @example
     * var stl = ParserHtml.parseStyle('color:black; width:100px; test:value;');
     * console.log(stl);
     * // {color: 'black', width: '100px', test: 'value'}
     */
    parseStyle(str) {
      var result = {};
      var decls = str.split(';');
      for (var i = 0, len = decls.length; i < len; i++) {
        var decl = decls[i].trim();
        if (!decl) continue;
        var prop = decl.split(':');
        result[prop[0].trim()] = prop
          .slice(1)
          .join(':')
          .trim();
      }
      return result;
    },

    /**
     * Parse class string to array
     * @param {string} str
     * @return {Array<string>}
     * @example
     * var res = ParserHtml.parseClass('test1 test2 test3');
     * console.log(res);
     * // ['test1', 'test2', 'test3']
     */
    parseClass(str) {
      const result = [];
      const cls = str.split(' ');
      for (let i = 0, len = cls.length; i < len; i++) {
        const cl = cls[i].trim();
        if (!cl) continue;
        result.push(cl);
      }
      return result;
    },

    /**
     * Get data from the node element
     * @param  {HTMLElement} el DOM element to traverse
     * @return {Array<Object>}
     */
    parseNode(el) {
      const result = [];
      const nodes = el.childNodes;

      for (var i = 0, len = nodes.length; i < len; i++) {
        const node = nodes[i];
        const attrs = node.attributes || [];
        const attrsLen = attrs.length;
        const nodePrev = result[result.length - 1];
        const nodeChild = node.childNodes.length;
        const ct = this.compTypes;
        let model = {};

        // Start with understanding what kind of component it is
        if (ct) {
          let obj = '';

          // Iterate over all available Component Types and
          // the first with a valid result will be that component
          for (let it = 0; it < ct.length; it++) {
            obj = ct[it].model.isComponent(node);
            if (obj) break;
          }

          model = obj;
        }

        // Set tag name if not yet done
        if (!model.tagName) {
          model.tagName = node.tagName ? node.tagName.toLowerCase() : '';
        }

        if (attrsLen) {
          model.attributes = {};
        }

        // Parse attributes
        for (let j = 0; j < attrsLen; j++) {
          const nodeName = attrs[j].nodeName;
          let nodeValue = attrs[j].nodeValue;

          // Isolate attributes
          if (nodeName == 'style') {
            model.style = this.parseStyle(nodeValue);
          } else if (nodeName == 'class') {
            model.classes = this.parseClass(nodeValue);
          } else if (nodeName == 'contenteditable') {
            continue;
          } else if (nodeName.indexOf(modelAttrStart) === 0) {
            const modelAttr = nodeName.replace(modelAttrStart, '');
            const valueLen = nodeValue.length;
            const firstChar = nodeValue && nodeValue.substr(0, 1);
            const lastChar = nodeValue && nodeValue.substr(valueLen - 1);
            nodeValue = nodeValue === 'true' ? true : nodeValue;
            nodeValue = nodeValue === 'false' ? false : nodeValue;

            // Try to parse JSON where it's possible
            // I can get false positive here (eg. a selector '[data-attr]')
            // so put it under try/catch and let fail silently
            try {
              nodeValue =
                (firstChar == '{' && lastChar == '}') ||
                (firstChar == '[' && lastChar == ']')
                  ? JSON.parse(nodeValue)
                  : nodeValue;
            } catch (e) {}

            model[modelAttr] = nodeValue;
          } else {
            model.attributes[nodeName] = nodeValue;
          }
        }

        // Check for nested elements but avoid it if already provided
        if (nodeChild && !model.components) {
          // Avoid infinite nested text nodes
          const firstChild = node.childNodes[0];

          // If there is only one child and it's a TEXTNODE
          // just make it content of the current node
          if (nodeChild === 1 && firstChild.nodeType === 3) {
            !model.type && (model.type = 'text');
            model.content = firstChild.nodeValue;
          } else {
            model.components = this.parseNode(node);
          }
        }

        // Check if it's a text node and if could be moved to the prevous model
        if (model.type == 'textnode') {
          if (nodePrev && nodePrev.type == 'textnode') {
            nodePrev.content += model.content;
            continue;
          }

          // Throw away empty nodes (keep spaces)
          if (!config.keepEmptyTextNodes) {
            const content = node.nodeValue;
            if (content != ' ' && !content.trim()) {
              continue;
            }
          }
        }

        // If all children are texts and there is some textnode the parent should
        // be text too otherwise I'm unable to edit texnodes
        const comps = model.components;
        if (!model.type && comps) {
          let allTxt = 1;
          let foundTextNode = 0;

          for (let ci = 0; ci < comps.length; ci++) {
            const comp = comps[ci];
            const cType = comp.type;

            if (
              ['text', 'textnode'].indexOf(cType) < 0 &&
              c.textTags.indexOf(comp.tagName) < 0
            ) {
              allTxt = 0;
              break;
            }

            if (cType == 'textnode') {
              foundTextNode = 1;
            }
          }

          if (allTxt && foundTextNode) {
            model.type = 'text';
          }
        }

        // If tagName is still empty and is not a textnode, do not push it
        if (!model.tagName && model.type != 'textnode') {
          continue;
        }

        result.push(model);
      }

      return result;
    },

    /**
     * Parse HTML string to a desired model object
     * @param  {string} str HTML string
     * @param  {ParserCss} parserCss In case there is style tags inside HTML
     * @return {Object}
     */
    parse(str, parserCss) {
      var config = (c.em && c.em.get('Config')) || {};
      var res = { html: '', css: '' };
      var el = document.createElement('div');
      el.innerHTML = str;
      var scripts = el.querySelectorAll('script');
      var i = scripts.length;

      // Remove all scripts
      if (!config.allowScripts) {
        while (i--) scripts[i].parentNode.removeChild(scripts[i]);
      }

      // Detach style tags and parse them
      if (parserCss) {
        var styleStr = '';
        var styles = el.querySelectorAll('style');
        var j = styles.length;

        while (j--) {
          styleStr = styles[j].innerHTML + styleStr;
          styles[j].parentNode.removeChild(styles[j]);
        }

        if (styleStr) res.css = parserCss.parse(styleStr);
      }

      var result = this.parseNode(el);

      if (result.length == 1) result = result[0];

      res.html = result;

      return res;
    }
  };
};
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

// 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.

'use strict';

var parallel = require('run-parallel');
var TypedError = require('error/typed');
var util = require('util');

var EndpointAlreadyDefinedError = TypedError({
    type: 'endpoint-already-defined',
    message: 'endpoint {endpoint} already defined on service {service}',
    service: null,
    endpoint: null,
    oldHandler: null,
    newHandler:  null
});

var InvalidHandlerError = TypedError({
    type: 'invalid-handler',
    message: 'invalid handler function'
});

function TChannelEndpointHandler(serviceName) {
    if (!(this instanceof TChannelEndpointHandler)) {
        return new TChannelEndpointHandler(serviceName);
    }
    var self = this;
    self.serviceName = serviceName;
    self.endpoints = Object.create(null);
    self.type = null;
}

TChannelEndpointHandler.prototype.register = function register(name, handler) {
    var self = this;
    if (self.endpoints[name] !== undefined) {
        throw EndpointAlreadyDefinedError({
            service: self.serviceName,
            endpoint: name,
            oldHandler: self.endpoints[name],
            newHandler: handler
        });
    }
    if (typeof handler !== 'function') {
        throw InvalidHandlerError();
    }
    self.endpoints[name] = handler;
    return handler;
};

TChannelEndpointHandler.prototype.handleRequest = function handleRequest(req, res) {
    var self = this;
    // TODO: waterfall
    req.arg1.onValueReady(function arg1Ready(err, arg1) {
        if (err) throw err; // TODO: protocol error, respond with error frame
        handleArg1(arg1);
    });

    function handleArg1(arg1) {
        var name = String(arg1);
        var handler = self.endpoints[name];
        if (!handler) {
            sendError('BadRequest', util.format(
                'no such endpoint service=%j endpoint=%j',
                req.service, name));
        } else if (handler.canStream) {
            handler(req, res);
        } else {
            parallel({
                arg2: req.arg2.onValueReady,
                arg3: req.arg3.onValueReady
            }, argsDone);
        }

        function argsDone(err, args) {
            if (err) {
                throw err; // TODO: protocol error, respond with error frame
            } else {
                compatHandle(handler, args);
            }
        }

        function compatHandle(handler, args) {
            handler(req, res, args.arg2, args.arg3);
        }
    }

    function sendError(code, mess) {
        res.sendError(code, mess);
    }
};

module.exports = TChannelEndpointHandler;
<commit_msg>endpoint-handler: send UnexpectedError frame rather than throwing<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

// 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.

'use strict';

var parallel = require('run-parallel');
var TypedError = require('error/typed');
var util = require('util');

var EndpointAlreadyDefinedError = TypedError({
    type: 'endpoint-already-defined',
    message: 'endpoint {endpoint} already defined on service {service}',
    service: null,
    endpoint: null,
    oldHandler: null,
    newHandler:  null
});

var InvalidHandlerError = TypedError({
    type: 'invalid-handler',
    message: 'invalid handler function'
});

function TChannelEndpointHandler(serviceName) {
    if (!(this instanceof TChannelEndpointHandler)) {
        return new TChannelEndpointHandler(serviceName);
    }
    var self = this;
    self.serviceName = serviceName;
    self.endpoints = Object.create(null);
    self.type = null;
}

TChannelEndpointHandler.prototype.register = function register(name, handler) {
    var self = this;
    if (self.endpoints[name] !== undefined) {
        throw EndpointAlreadyDefinedError({
            service: self.serviceName,
            endpoint: name,
            oldHandler: self.endpoints[name],
            newHandler: handler
        });
    }
    if (typeof handler !== 'function') {
        throw InvalidHandlerError();
    }
    self.endpoints[name] = handler;
    return handler;
};

TChannelEndpointHandler.prototype.handleRequest = function handleRequest(req, res) {
    var self = this;
    // TODO: waterfall
    req.arg1.onValueReady(function arg1Ready(err, arg1) {
        if (err) {
            // TODO: log error
            sendError('UnexpectedError', util.format(
                'error accumulating arg1: %s: %s',
                err.constructor.name, err.message));
        } else {
            handleArg1(arg1);
        }
    });

    function handleArg1(arg1) {
        var name = String(arg1);
        var handler = self.endpoints[name];
        if (!handler) {
            sendError('BadRequest', util.format(
                'no such endpoint service=%j endpoint=%j',
                req.service, name));
        } else if (handler.canStream) {
            handler(req, res);
        } else {
            parallel({
                arg2: req.arg2.onValueReady,
                arg3: req.arg3.onValueReady
            }, argsDone);
        }

        function argsDone(err, args) {
            if (err) {
                // TODO: log error
                sendError('UnexpectedError', util.format(
                    'error accumulating arg2/arg3: %s: %s',
                    err.constructor.name, err.message));
            } else {
                compatHandle(handler, args);
            }
        }

        function compatHandle(handler, args) {
            handler(req, res, args.arg2, args.arg3);
        }
    }

    function sendError(code, mess) {
        res.sendError(code, mess);
    }
};

module.exports = TChannelEndpointHandler;
<|endoftext|>


<commit_before>//Module wrapper for camera related classes.
define([
  "pex/behaviors/Orbiter"
  ], function(Orbiter) {
    return {
      Orbiter : Orbiter
    }
  }
);
<commit_msg>Updated Behaviors module description<commit_after>//Module wrapper for behaviors (controllers) classes.
define([
  "pex/behaviors/Orbiter"
  ], function(Orbiter) {
    return {
      Orbiter : Orbiter
    }
  }
);
<|endoftext|>


<commit_before>/* eslint-disable */
/* global module */
/* not yet approved by skill group core team */
module.exports = {
	rules: {

		// Overwrite require-jsdoc rule
		'require-jsdoc': 0,

		// disallow importing from the same path more than once
		// http://eslint.org/docs/rules/no-duplicate-imports
		'no-duplicate-imports': 0,

		// disallow duplicate imports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-duplicates.md
		'import/no-duplicates': 0,

		// Prevent missing props validation in a React component definition
		// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md
		'react/prop-types': 0,

		// enforces a particular style for boolean type annotations. This rule takes one argument.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-boolean-style
		'flowtype/boolean-style': [2, 'boolean'],

		// marks Flow type identifiers as defined. Used to suppress [`no-undef`](https://github.com/gajus/eslint-plugin-flowtypedocs/rules/no-undef) reporting of type identifiers.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-define-flow-type
		'flowtype/define-flow-type': 2,

		// enforces consistent use of trailing commas in Object and Tuple annotations.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-delimiter-dangle
		'flowtype/delimiter-dangle': [2, 'always-multiline'],
		// [20.09.2016] always-multiline -> https://github.com/namics/eslint-config-namics/issues/1

		// enforces consistent spacing within generic type annotation parameters.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-generic-spacing
		'flowtype/generic-spacing': [2, 'never'],

		// disallows use of primitive constructors as types, such as `Boolean`, `Number` and `String`. [See more](https://flowtype.org/docs/builtins.html).
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-no-primitive-constructor-types
		'flowtype/no-primitive-constructor-types': 2,

		// Warns against weak type annotations *any*, *Object* and *Function*. These types can cause flow to silently skip over portions of your code, which would have otherwise caused type errors.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-no-weak-types
		'flowtype/no-weak-types': 0,
		// [10.03.2013] disabled because of third party libraries

		// enforces consistent separators between properties in Flow object types.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-object-type-delimiter
		'flowtype/object-type-delimiter': [2, 'comma'],

		// requires that all function parameters have type annotations.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-require-parameter-type
		'flowtype/require-parameter-type': 2,

		// requires that functions have return type annotation.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-require-return-type
		'flowtype/require-return-type': [2, 'always',
			{
				'annotateUndefined': 'never',
			},
		],

		// this rule validates Flow file annotations.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-require-valid-file-annotation
		'flowtype/require-valid-file-annotation': 2,

		// enforces consistent use of semicolons after type aliases.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-semi
		'flowtype/semi': [2, 'always'],

		// enforces consistent spacing after the type annotation colon.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-space-after-type-colon
		'flowtype/space-after-type-colon': [2, 'always'],

		// Enforces consistent spacing before the opening < of generic type annotation parameters.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-space-before-generic-bracket
		'flowtype/space-before-generic-bracket': [2, 'never'],

		// Enforces consistent spacing before the type annotation colon.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-space-before-type-colon
		'flowtype/space-before-type-colon': [2, 'never'],

		// Enforces a consistent naming pattern for type aliases.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-type-id-match
		'flowtype/type-id-match': [2, '^([A-Z][a-z0-9]+)+Type$'],

		// Enforces consistent spacing around union and intersection type separators (| and &).
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-union-intersection-spacing
		'flowtype/union-intersection-spacing': [2, 'always'],

		// Marks Flow type alias declarations as used. Used to suppress no-unused-vars errors that are triggered by type aliases.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-use-flow-type
		'flowtype/use-flow-type': 2,

		// Checks for simple Flow syntax errors.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-valid-syntax
		'flowtype/valid-syntax': 2,
	},
};
<commit_msg>[change] disable flowtype/require-return-type because it's better to use flow coverage<commit_after>/* eslint-disable */
/* global module */
/* not yet approved by skill group core team */
module.exports = {
	rules: {
		// Overwrite require-jsdoc rule
		'require-jsdoc': 0,

		// disallow importing from the same path more than once
		// http://eslint.org/docs/rules/no-duplicate-imports
		'no-duplicate-imports': 0,

		// disallow duplicate imports
		// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-duplicates.md
		'import/no-duplicates': 0,

		// Prevent missing props validation in a React component definition
		// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md
		'react/prop-types': 0,

		// enforces a particular style for boolean type annotations. This rule takes one argument.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-boolean-style
		'flowtype/boolean-style': [2, 'boolean'],

		// marks Flow type identifiers as defined. Used to suppress [`no-undef`](https://github.com/gajus/eslint-plugin-flowtypedocs/rules/no-undef) reporting of type identifiers.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-define-flow-type
		'flowtype/define-flow-type': 2,

		// enforces consistent use of trailing commas in Object and Tuple annotations.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-delimiter-dangle
		'flowtype/delimiter-dangle': [2, 'always-multiline'],
		// [20.09.2016] always-multiline -> https://github.com/namics/eslint-config-namics/issues/1

		// enforces consistent spacing within generic type annotation parameters.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-generic-spacing
		'flowtype/generic-spacing': [2, 'never'],

		// disallows use of primitive constructors as types, such as `Boolean`, `Number` and `String`. [See more](https://flowtype.org/docs/builtins.html).
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-no-primitive-constructor-types
		'flowtype/no-primitive-constructor-types': 2,

		// Warns against weak type annotations *any*, *Object* and *Function*. These types can cause flow to silently skip over portions of your code, which would have otherwise caused type errors.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-no-weak-types
		'flowtype/no-weak-types': 0,
		// [10.03.2013] disabled because of third party libraries

		// enforces consistent separators between properties in Flow object types.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-object-type-delimiter
		'flowtype/object-type-delimiter': [2, 'comma'],

		// requires that all function parameters have type annotations.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-require-parameter-type
		'flowtype/require-parameter-type': 2,

		// requires that functions have return type annotation.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-require-return-type
		'flowtype/require-return-type': 0,

		// this rule validates Flow file annotations.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-require-valid-file-annotation
		'flowtype/require-valid-file-annotation': 2,

		// enforces consistent use of semicolons after type aliases.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-semi
		'flowtype/semi': [2, 'always'],

		// enforces consistent spacing after the type annotation colon.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-space-after-type-colon
		'flowtype/space-after-type-colon': [2, 'always'],

		// Enforces consistent spacing before the opening < of generic type annotation parameters.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-space-before-generic-bracket
		'flowtype/space-before-generic-bracket': [2, 'never'],

		// Enforces consistent spacing before the type annotation colon.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-space-before-type-colon
		'flowtype/space-before-type-colon': [2, 'never'],

		// Enforces a consistent naming pattern for type aliases.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-type-id-match
		'flowtype/type-id-match': [2, '^([A-Z][a-z0-9]+)+Type$'],

		// Enforces consistent spacing around union and intersection type separators (| and &).
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-union-intersection-spacing
		'flowtype/union-intersection-spacing': [2, 'always'],

		// Marks Flow type alias declarations as used. Used to suppress no-unused-vars errors that are triggered by type aliases.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-use-flow-type
		'flowtype/use-flow-type': 2,

		// Checks for simple Flow syntax errors.
		// https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype-rules-valid-syntax
		'flowtype/valid-syntax': 2,
	},
};
<|endoftext|>


<commit_before>'use strict';

const test = require('ava');
const mockery = require('mockery');

const configHelperModule = './utils/config-helper';
const scopeModule = '../lib/scope';

test.beforeEach(() => {
  mockery.enable({ useCleanCache: true });
  mockery.registerAllowable(scopeModule, true);
  mockery.registerAllowables(['object-merge', 'object-foreach', 'clone-function']);
});

test.afterEach(() => {
  mockery.deregisterAll();
  mockery.resetCache();
  mockery.disable();
});

test.serial('it should return the currently set scope', (t) => {
  const expectedScope = 'a';
  const configHelperMock = {
    read: () => Promise.resolve({cdn: {scope: expectedScope}})
  };

  mockery.registerMock(configHelperModule, configHelperMock);

  const scope = require(scopeModule);

  return scope.read().then((s) => t.is(s.scope, expectedScope));
});

test.serial('it should handle an unitinitalised config file', (t) => {
  const expectedScope = undefined;
  const configHelperMock = {
    read: () => Promise.resolve({})
  };

  mockery.registerMock(configHelperModule, configHelperMock);

  const scope = require(scopeModule);
  return scope.read().then((s) => t.is(s.scope, expectedScope));
});

test.serial('it should reject if no scope is set', (t) => {
  const configHelperMock = {
    write: (fn) => Promise.reject({})
  };

  mockery.registerMock(configHelperModule, configHelperMock);

  const scope = require(scopeModule);
  let p = scope.write();
  t.throws(p, 'Options.scope was not defined.');
});

test.serial('it should merge new scope with the current config', (t) => {
  const originalConfig = {
    bmp: {
      scope: 'blah'
    },
    cdn: {
      scope: 'old',
      extra: 'existing'
    }
  };

  const configHelperMock = {
    write: (fn) => Promise.resolve(fn(originalConfig))
  };

  mockery.registerMock(configHelperModule, configHelperMock);

  const scope = require(scopeModule);
  scope.write({scope: 'c'}).then((config) => {
    t.not(config.cdn.scope, 'old');
    t.is(config.cdn.scope, 'c');
    t.is(config.cdn.extra, 'existing');
  });
});
<commit_msg>Fixed eslint errors<commit_after>'use strict';

const test = require('ava');
const mockery = require('mockery');

const configHelperModule = './utils/config-helper';
const scopeModule = '../lib/scope';

test.beforeEach(() => {
  mockery.enable({ useCleanCache: true });
  mockery.registerAllowable(scopeModule, true);
  mockery.registerAllowables(['object-merge', 'object-foreach', 'clone-function']);
});

test.afterEach(() => {
  mockery.deregisterAll();
  mockery.resetCache();
  mockery.disable();
});

test.serial('it should return the currently set scope', (t) => {
  const expectedScope = 'a';
  const configHelperMock = {
    read: () => Promise.resolve({cdn: {scope: expectedScope}})
  };

  mockery.registerMock(configHelperModule, configHelperMock);

  const scope = require(scopeModule);

  return scope.read().then((s) => t.is(s.scope, expectedScope));
});

test.serial('it should handle an unitinitalised config file', (t) => {
  const expectedScope = undefined;
  const configHelperMock = {
    read: () => Promise.resolve({})
  };

  mockery.registerMock(configHelperModule, configHelperMock);

  const scope = require(scopeModule);
  return scope.read().then((s) => t.is(s.scope, expectedScope));
});

test.serial('it should reject if no scope is set', (t) => {
  const configHelperMock = {
    write: (fn) => Promise.reject(new Error())
  };

  mockery.registerMock(configHelperModule, configHelperMock);

  const scope = require(scopeModule);
  let p = scope.write();
  t.throws(p, 'Options.scope was not defined.');
});

test.serial('it should merge new scope with the current config', (t) => {
  const originalConfig = {
    bmp: {
      scope: 'blah'
    },
    cdn: {
      scope: 'old',
      extra: 'existing'
    }
  };

  const configHelperMock = {
    write: (fn) => Promise.resolve(fn(originalConfig))
  };

  mockery.registerMock(configHelperModule, configHelperMock);

  const scope = require(scopeModule);
  scope.write({scope: 'c'}).then((config) => {
    t.not(config.cdn.scope, 'old');
    t.is(config.cdn.scope, 'c');
    t.is(config.cdn.extra, 'existing');
  });
});
<|endoftext|>


<commit_before>var createContext = require('./util/create-context')
var createREGL = require('../../regl')
var tape = require('tape')

tape('scope', function (t) {
  var regl = createREGL(createContext(5, 5))

  function checkPixmap (expected, remark) {
    var pixels = regl.read()
    var actual = Array(25)
    for (var i = 0; i < 25; ++i) {
      actual[i] = Math.min(1, pixels[4 * i])
    }
    t.same(actual, expected, remark)
  }

  var scope = regl({
    frag: [
      'precision mediump float;',
      'void main() {',
      'gl_FragColor = vec4(1, 1, 1, 1);',
      '}'
    ].join('\n'),

    vert: [
      'precision mediump float;',
      'attribute vec2 position;',
      'varying vec4 fragColor;',
      'void main() {',
      'gl_Position=vec4((position - 2.0) / 2.1, 0, 1);',
      '}'
    ].join('\n'),

    attributes: {
      position: regl.prop('position')
    },
    primitive: regl.prop('primitive'),
    offset: regl.prop('offset'),
    count: regl.prop('count'),

    depth: {enable: false, mask: false}
  })

  // properties:
  //  attributes
  //  uniforms
  //  draw state
  //  elements
  //  glstate
  //  shaders

  // sequences to test
  // scope (draw - batch - draw) sequence
  // scope (batch - batch) sequence
  // scope (draw - scope ( draw ) - draw)

  // reentrant scope with attributes

  regl.clear({
    color: [0, 0, 0, 0]
  })
  scope({
    position: regl.buffer([0, 0, 4, 0, 4, 4, 0, 4]),
    count: 4,
    offset: 0,
    primitive: 'point'
  }, function () {
    scope({
      position: regl.buffer([2, 2]),
      count: 1,
      offset: 0,
      primitive: 'point'
    })
    checkPixmap([
      0, 0, 0, 0, 0,
      0, 0, 0, 0, 0,
      0, 0, 1, 0, 0,
      0, 0, 0, 0, 0,
      0, 0, 0, 0, 0
    ], 'scope (draw - batch - draw) : first draw')

    scope([{
      position: regl.buffer([3, 3]),
      count: 1,
      offset: 0,
      primitive: 'point'
    }])
    checkPixmap([
      0, 0, 0, 0, 0,
      0, 0, 0, 0, 0,
      0, 0, 1, 0, 0,
      0, 0, 0, 1, 0,
      0, 0, 0, 0, 0
    ], 'scope (draw - batch - draw) : second draw')

    regl.draw()
  })
  checkPixmap([
    1, 0, 0, 0, 1,
    0, 0, 0, 0, 0,
    0, 0, 1, 0, 0,
    0, 0, 0, 1, 0,
    1, 0, 0, 0, 1
  ], 'scope (draw - batch - draw) : result')

  // test setting uniforms with scope

  regl.destroy()
  t.end()
})
<commit_msg>test reentrant scopes<commit_after>var createContext = require('./util/create-context')
var createREGL = require('../../regl')
var tape = require('tape')

tape('scope', function (t) {
  var regl = createREGL(createContext(5, 5))

  function checkPixmap (expected, remark) {
    var pixels = regl.read()
    var actual = Array(25)
    for (var i = 0; i < 25; ++i) {
      actual[i] = Math.min(1, pixels[4 * i])
    }
    t.same(actual, expected, remark)
  }

  var scope = regl({
    frag: [
      'precision mediump float;',
      'void main() {',
      'gl_FragColor = vec4(1, 1, 1, 1);',
      '}'
    ].join('\n'),

    vert: [
      'precision mediump float;',
      'attribute vec2 position;',
      'varying vec4 fragColor;',
      'void main() {',
      'gl_Position=vec4((position - 2.0) / 2.1, 0, 1);',
      '}'
    ].join('\n'),

    attributes: {
      position: regl.prop('position')
    },
    primitive: regl.prop('primitive'),
    offset: regl.prop('offset'),
    count: regl.prop('count'),

    depth: {enable: false, mask: false}
  })

  // properties:
  //  attributes
  //  uniforms
  //  draw state
  //  elements
  //  glstate
  //  shaders

  // sequences to test
  // scope (draw - batch - draw) sequence
  // scope (batch - batch) sequence
  // scope (draw - scope ( draw ) - draw)

  // reentrant scope with attributes

  regl.clear({
    color: [0, 0, 0, 0]
  })
  scope({
    position: regl.buffer([0, 0, 4, 0, 4, 4, 0, 4]),
    count: 4,
    offset: 0,
    primitive: 'points'
  }, function () {
    scope({
      position: regl.buffer([2, 2]),
      count: 1,
      offset: 0,
      primitive: 'points'
    })
    checkPixmap([
      0, 0, 0, 0, 0,
      0, 0, 0, 0, 0,
      0, 0, 1, 0, 0,
      0, 0, 0, 0, 0,
      0, 0, 0, 0, 0
    ], 'scope (draw - batch - draw) : first draw')

    scope([{
      position: regl.buffer([3, 3]),
      count: 1,
      offset: 0,
      primitive: 'points'
    }])
    checkPixmap([
      0, 0, 0, 0, 0,
      0, 0, 0, 0, 0,
      0, 0, 1, 0, 0,
      0, 0, 0, 1, 0,
      0, 0, 0, 0, 0
    ], 'scope (draw - batch - draw) : second draw')

    regl.draw()
  })
  checkPixmap([
    1, 0, 0, 0, 1,
    0, 0, 0, 0, 0,
    0, 0, 1, 0, 0,
    0, 0, 0, 1, 0,
    1, 0, 0, 0, 1
  ], 'scope (draw - batch - draw) : result')

  // test setting uniforms with scope

  regl.destroy()
  t.end()
})
<|endoftext|>


<commit_before>const $ = require('jquery');
const { BagItProfile } = require('../../bagit/bagit_profile');
const { BaseController } = require('./base_controller');
const { Context } = require('../../core/context');
const FileSystemReader = require('../../plugins/formats/read/file_system_reader');
const fs = require('fs');
const { Job } = require('../../core/job');
const { JobTagsForm } = require('../forms/job_tags_form');
const Templates = require('../common/templates');
const { Util } = require('../../core/util');

/**
 * The JobFilesController displays the Job files page, where
 * users can drag and drop the files that a Job will package
 * and/or uploade.
 *
 * @param {URLSearchParams} params - The URL search params parsed
 * from the URL used to reach this page. This should contain at
 * least the Job Id.
 *
 * @param {string} params.id - The id of the Job being worked
 * on. Job.id is a UUID string.
 */
class JobFilesController extends BaseController {

    constructor(params) {
        super(params, 'Jobs');
        this.model = Job;
        this.job = Job.find(this.params.get('id'));
    }

    /**
     * This displays the Job files UI, where the user can drag
     * and drop files.
     */
    show() {
        let data = {
            alertMessage: this.alertMessage,
            job: this.job
        }
        this.alertMessage = null;
        let html = Templates.jobFiles(data);
        return this.containerContent(html);
    }

    /**
     * This attaches required events to the Job files UI and
     * adds the list of files and folders to be packaged to
     * the UI.
     */
    postRenderCallback(fnName) {
        this.attachDragAndDropEvents();
        this.attachDeleteEvents();
        this.addItemsToUI();
    }

    /**
     * This attaches drag and drop events, so users can add files
     * and folders to the job by dragging them into the application
     * window.
     */
    attachDragAndDropEvents() {
        let controller = this;
        $('#dropZone').on('drop', function(e) {
            e.preventDefault();
            e.stopPropagation();
            // When drag event is attached to document, use
            // e.dataTransfer.files instead of what's below.
            for (let f of e.originalEvent.dataTransfer.files) {
                let containingItem = controller.findContainingItem(f.path);
                if (containingItem) {
                    let msg = Context.y18n.__(
                        '%s has already been added to this package as part of %s',
                        `${f.path}\n\n`,
                        `\n\n${containingItem}`
                    );
                    alert(msg);
                    continue;
                }
                controller.addFileToPackageSources(f.path);
                controller.addItemToUI(f.path);
                controller.job.save();
            }
            $(e.currentTarget).removeClass('drop-zone-over');
            return false;
        });
        $('#dropZone').on('dragover', function(e) {
            e.preventDefault();
            e.stopPropagation();
            $(e.currentTarget).addClass('drop-zone-over');
            return false;
        });
        $('#dropZone').on('dragleave', function(e) {
            e.preventDefault();
            e.stopPropagation();
            $(e.currentTarget).removeClass('drop-zone-over');
            return false;
        });
        $('#dropZone').on('dragend', function(e) {
            e.preventDefault();
            e.stopPropagation();
            $(e.currentTarget).removeClass('drop-zone-over');
            return false;
        });
    }

    /**
     * This attaches the deletion handler to the red X beside
     * each file and folder. Clicking the red delete icon causes
     * the filepath to be removed from the UI and from this Job's
     * list of sourceFiles in {@link PackageOperation}.
     */
    attachDeleteEvents() {
        let controller = this;
        $('#filesTable').on('click', 'td.delete-file', function(e) {
            let filepath = $(e.currentTarget).data('filepath');
            controller.removeItemFromUI(filepath);
            Util.deleteFromArray(controller.job.packageOp.sourceFiles, filepath);
            controller.job.save();
            return false;
        });
    }

    /**
     * This adds files and folders to the display.
     */
    addItemsToUI() {
        // Do not try to process any files or directories that
        // the user may have deleted.
        this.job.packageOp.pruneSourceFilesUnlessJobCompleted();
        var files = this.job.packageOp.sourceFiles.slice();
        for(var filepath of files) {
            this.addItemToUI(filepath);
        }
    }

    /**
     * This adds a single file or folder to the UI.
     */
    addItemToUI(filepath) {
        let controller = this;
        let stats = fs.statSync(filepath);
        if (stats.isFile()) {
            this.addRow(filepath, 'file', 1, 0, stats.size);
        } else if (stats.isDirectory()) {
            let fsReader = new FileSystemReader(filepath);
            fsReader.on('end', function() {
                controller.addRow(filepath, 'directory', fsReader.fileCount,
                              fsReader.dirCount, fsReader.byteCount);
            });
            fsReader.list();
        }
    }

    /**
     * This adds one row to the table that lists which files and
     * folders this job will package.
     *
     * @param {string} filepath - The absolute path to a file or folder
     * that is to be packaged as part of this job.
     *
     * @param {string} type - The type of item that filepath represents.
     * This should be either "file" or "directory".
     *
     * @param {number} fileCount - The number of files contained by the
     * item in param filepath. For files, this will be 1. For directories,
     * it will be the total number of files in the directory and all of
     * its subdirectories. (This info is readily available from the
     * {@link FileSystemReader} plugin.)
     *
     * @param {number} dirCount - The number of directories contained by
     * the item in param filepath. For files, this will be 0. For directories,
     * this will be 1 (for the directory itself) plus the total count of
     * all directories within that directory and all of its subdirectories.
     * (This info is readily available from the {@link FileSystemReader}
     * plugin.)
     *
     * @param {number} byteCount - The total number of bytes contained by
     * the item at filepath. For files, this will be the filesize. For
     * directories, this will be the sum of bytes contained by all the files
     * within the directory and all its subdirectories. (This info is readily
     * available from the {@link FileSystemReader} plugin.)
     *
     */
    addRow(filepath, type, fileCount, dirCount, byteCount) {
        $('#filesPanel').show();
        let row = this.getTableRow(filepath, type, fileCount, dirCount, byteCount);
        $(row).insertBefore('#fileTotals');
        this.updateTotals(fileCount, dirCount, byteCount);
    }

    /**
     * Updates the total number of files, folders, and bytes to be
     * packaged.
     *
     * @param {number} fileCount - The number of files contained by the
     * item in param filepath. For files, this will be 1. For directories,
     * it will be the total number of files in the directory and all of
     * its subdirectories. (This info is readily available from the
     * {@link FileSystemReader} plugin.) This number will be added to
     * the total file count.
     *
     * @param {number} dirCount - The number of directories contained by
     * the item in param filepath. For files, this will be 0. For directories,
     * this will be 1 (for the directory itself) plus the total count of
     * all directories within that directory and all of its subdirectories.
     * (This info is readily available from the {@link FileSystemReader}
     * plugin.) This number will be added to the total directory count.
     *
     * @param {number} byteCount - The total number of bytes contained by
     * the item at filepath. For files, this will be the filesize. For
     * directories, this will be the sum of bytes contained by all the files
     * within the directory and all its subdirectories. (This info is readily
     * available from the {@link FileSystemReader} plugin.) This number will
     * be added to the total byte count.
     *
     */
    updateTotals(fileCount, dirCount, byteCount) {
        let filesTotal = this.updateTotal('#totalFileCount', fileCount);
        let dirTotal = this.updateTotal('#totalDirCount', dirCount);
        let byteTotal = this.updateTotal('#totalByteCount', byteCount);
        this.job.fileCount = filesTotal;
        this.job.dirCount = dirTotal;
        this.job.byteCount = byteTotal;
    }

    /**
     * Updates one of the total fields at the bottom of the list of
     * files and folders, and returns the calculated total.
     *
     * @param {string} elementId - The id (css selector) of the element
     * whose text should be updated.
     *
     * @param {number} amountToAdd - The number to add to the existing
     * total already displayed in the cell. This will be negative in
     * cases where you're removing files or folders.
     *
     * @returns {number} The total value displayed in the field.
     */
    updateTotal(elementId, amountToAdd) {
        let element = $(elementId);
        let newTotal = parseInt(element.data('total'), 10) + amountToAdd;
        element.data('total', newTotal);
        if (elementId == '#totalByteCount') {
            element.text(Util.toHumanSize(newTotal));
        } else {
            element.text(newTotal);
        }
        return newTotal;
    }

    /**
     * Returns the HTML for a single table row in the files/folders
     * display.
     *
     * @param {string} filepath - The absolute path to a file or folder
     * that is to be packaged as part of this job.
     *
     * @param {string} type - The type of item that filepath represents.
     * This should be either "file" or "directory".
     *
     * @param {number} fileCount - The number of files contained by the
     * item in param filepath. For files, this will be 1. For directories,
     * it will be the total number of files in the directory and all of
     * its subdirectories. (This info is readily available from the
     * {@link FileSystemReader} plugin.)
     *
     * @param {number} dirCount - The number of directories contained by
     * the item in param filepath. For files, this will be 0. For directories,
     * this will be 1 (for the directory itself) plus the total count of
     * all directories within that directory and all of its subdirectories.
     * (This info is readily available from the {@link FileSystemReader}
     * plugin.)
     *
     * @param {number} byteCount - The total number of bytes contained by
     * the item at filepath. For files, this will be the filesize. For
     * directories, this will be the sum of bytes contained by all the files
     * within the directory and all its subdirectories. (This info is readily
     * available from the {@link FileSystemReader} plugin.)
     *
     * @returns {string} A string of HTML representing a table row.
     */
    getTableRow(filepath, type, fileCount, dirCount, byteCount) {
        let iconType = (type == 'file' ? 'file' : 'folder-closed');
        let data = {
            iconType: iconType,
            filepath: Util.escapeBackslashes(filepath),
            dirCount: dirCount,
            fileCount: fileCount,
            byteCount: byteCount,
            size: Util.toHumanSize(byteCount)
        }
        return Templates.jobFileRow(data);
    }

    /**
     * This adds a file or folder to the list of items that will be
     * packaged in this job. The item is added to the sourceFiles
     * list of the Job's {@link PackageOperation} attribute.
     *
     * @param {string} filepath - The absolute path to a file or folder
     * that is to be packaged as part of this job.
     *
     */
    addFileToPackageSources(filepath) {
        if (!Array.isArray(this.job.packageOp.sourceFiles)) {
            this.job.packageOp.sourceFiles = [];
        }
        this.job.packageOp.sourceFiles.push(filepath);
    }

    /**
     * This checks the list of source files and folders to see if
     * any of the items already set to be packaged contains the item
     * that the user just dragged into the window. We make this check
     * to avoid adding duplicate files or folders to the list of
     * sources.
     *
     * @param {string} filepath - The absolute path to a file or folder
     * that is to be packaged as part of this job.
     *
     * @return {string} - The path the already-added folder that contains
     * filepath, or null if filepath's containing folder has not already
     * been added to the list of source files.
     *
     */
    findContainingItem(filepath) {
        if (Array.isArray(this.job.packageOp.sourceFiles)) {
            for (let item of this.job.packageOp.sourceFiles) {
                if (filepath.startsWith(item)) {
                    return item;
                }
            }
        }
        return null;
    }

    /**
     * This deletes a file or folder from the list of files and folders
     * in the UI (but does not delete the item from the underlying
     * {@link PackageOperation}).
     *
     * @param {string} filepath - The absolute path to a file or folder
     * to be removed from the UI.
     *
     */
    removeItemFromUI(filepath) {
        let row = $(`tr[data-filepath="${filepath}"]`)
        let dirCount = -1 * parseInt(row.find('td.dirCount').text(), 10);
        let fileCount = -1 * parseInt(row.find('td.fileCount').text(), 10);
        let fileSize = -1 * parseInt(row.find('td.fileSize').data('bytes'), 10);
        row.remove();
        this.updateTotals(fileCount, dirCount, fileSize)
        if ($('tr.filepath').length == 0) {
            $('#filesPanel').hide();
        }
    }

}

module.exports.JobFilesController = JobFilesController;
<commit_msg>Still working on Windows fix<commit_after>const $ = require('jquery');
const { BagItProfile } = require('../../bagit/bagit_profile');
const { BaseController } = require('./base_controller');
const { Context } = require('../../core/context');
const FileSystemReader = require('../../plugins/formats/read/file_system_reader');
const fs = require('fs');
const { Job } = require('../../core/job');
const { JobTagsForm } = require('../forms/job_tags_form');
const Templates = require('../common/templates');
const { Util } = require('../../core/util');

/**
 * The JobFilesController displays the Job files page, where
 * users can drag and drop the files that a Job will package
 * and/or uploade.
 *
 * @param {URLSearchParams} params - The URL search params parsed
 * from the URL used to reach this page. This should contain at
 * least the Job Id.
 *
 * @param {string} params.id - The id of the Job being worked
 * on. Job.id is a UUID string.
 */
class JobFilesController extends BaseController {

    constructor(params) {
        super(params, 'Jobs');
        this.model = Job;
        this.job = Job.find(this.params.get('id'));
    }

    /**
     * This displays the Job files UI, where the user can drag
     * and drop files.
     */
    show() {
        let data = {
            alertMessage: this.alertMessage,
            job: this.job
        }
        this.alertMessage = null;
        let html = Templates.jobFiles(data);
        return this.containerContent(html);
    }

    /**
     * This attaches required events to the Job files UI and
     * adds the list of files and folders to be packaged to
     * the UI.
     */
    postRenderCallback(fnName) {
        this.attachDragAndDropEvents();
        this.attachDeleteEvents();
        this.addItemsToUI();
    }

    /**
     * This attaches drag and drop events, so users can add files
     * and folders to the job by dragging them into the application
     * window.
     */
    attachDragAndDropEvents() {
        let controller = this;
        $('#dropZone').on('drop', function(e) {
            e.preventDefault();
            e.stopPropagation();
            // When drag event is attached to document, use
            // e.dataTransfer.files instead of what's below.
            for (let f of e.originalEvent.dataTransfer.files) {
                let containingItem = controller.findContainingItem(f.path);
                if (containingItem) {
                    let msg = Context.y18n.__(
                        '%s has already been added to this package as part of %s',
                        `${f.path}\n\n`,
                        `\n\n${containingItem}`
                    );
                    alert(msg);
                    continue;
                }
                controller.addFileToPackageSources(f.path);
                controller.addItemToUI(f.path);
                controller.job.save();
            }
            $(e.currentTarget).removeClass('drop-zone-over');
            return false;
        });
        $('#dropZone').on('dragover', function(e) {
            e.preventDefault();
            e.stopPropagation();
            $(e.currentTarget).addClass('drop-zone-over');
            return false;
        });
        $('#dropZone').on('dragleave', function(e) {
            e.preventDefault();
            e.stopPropagation();
            $(e.currentTarget).removeClass('drop-zone-over');
            return false;
        });
        $('#dropZone').on('dragend', function(e) {
            e.preventDefault();
            e.stopPropagation();
            $(e.currentTarget).removeClass('drop-zone-over');
            return false;
        });
    }

    /**
     * This attaches the deletion handler to the red X beside
     * each file and folder. Clicking the red delete icon causes
     * the filepath to be removed from the UI and from this Job's
     * list of sourceFiles in {@link PackageOperation}.
     */
    attachDeleteEvents() {
        let controller = this;
        $('#filesTable').on('click', 'td.delete-file', function(e) {
            let filepath = $(e.currentTarget).data('filepath');
            controller.removeItemFromUI(filepath);
            Util.deleteFromArray(controller.job.packageOp.sourceFiles, filepath);
            controller.job.save();
            return false;
        });
    }

    /**
     * This adds files and folders to the display.
     */
    addItemsToUI() {
        // Do not try to process any files or directories that
        // the user may have deleted.
        this.job.packageOp.pruneSourceFilesUnlessJobCompleted();
        var files = this.job.packageOp.sourceFiles.slice();
        for(var filepath of files) {
            this.addItemToUI(filepath);
        }
    }

    /**
     * This adds a single file or folder to the UI.
     */
    addItemToUI(filepath) {
        let controller = this;
        let stats = fs.statSync(filepath);
        if (stats.isFile()) {
            this.addRow(filepath, 'file', 1, 0, stats.size);
        } else if (stats.isDirectory()) {
            let fsReader = new FileSystemReader(filepath);
            fsReader.on('end', function() {
                controller.addRow(filepath, 'directory', fsReader.fileCount,
                              fsReader.dirCount, fsReader.byteCount);
            });
            fsReader.list();
        }
    }

    /**
     * This adds one row to the table that lists which files and
     * folders this job will package.
     *
     * @param {string} filepath - The absolute path to a file or folder
     * that is to be packaged as part of this job.
     *
     * @param {string} type - The type of item that filepath represents.
     * This should be either "file" or "directory".
     *
     * @param {number} fileCount - The number of files contained by the
     * item in param filepath. For files, this will be 1. For directories,
     * it will be the total number of files in the directory and all of
     * its subdirectories. (This info is readily available from the
     * {@link FileSystemReader} plugin.)
     *
     * @param {number} dirCount - The number of directories contained by
     * the item in param filepath. For files, this will be 0. For directories,
     * this will be 1 (for the directory itself) plus the total count of
     * all directories within that directory and all of its subdirectories.
     * (This info is readily available from the {@link FileSystemReader}
     * plugin.)
     *
     * @param {number} byteCount - The total number of bytes contained by
     * the item at filepath. For files, this will be the filesize. For
     * directories, this will be the sum of bytes contained by all the files
     * within the directory and all its subdirectories. (This info is readily
     * available from the {@link FileSystemReader} plugin.)
     *
     */
    addRow(filepath, type, fileCount, dirCount, byteCount) {
        $('#filesPanel').show();
        let row = this.getTableRow(filepath, type, fileCount, dirCount, byteCount);
        $(row).insertBefore('#fileTotals');
        this.updateTotals(fileCount, dirCount, byteCount);
    }

    /**
     * Updates the total number of files, folders, and bytes to be
     * packaged.
     *
     * @param {number} fileCount - The number of files contained by the
     * item in param filepath. For files, this will be 1. For directories,
     * it will be the total number of files in the directory and all of
     * its subdirectories. (This info is readily available from the
     * {@link FileSystemReader} plugin.) This number will be added to
     * the total file count.
     *
     * @param {number} dirCount - The number of directories contained by
     * the item in param filepath. For files, this will be 0. For directories,
     * this will be 1 (for the directory itself) plus the total count of
     * all directories within that directory and all of its subdirectories.
     * (This info is readily available from the {@link FileSystemReader}
     * plugin.) This number will be added to the total directory count.
     *
     * @param {number} byteCount - The total number of bytes contained by
     * the item at filepath. For files, this will be the filesize. For
     * directories, this will be the sum of bytes contained by all the files
     * within the directory and all its subdirectories. (This info is readily
     * available from the {@link FileSystemReader} plugin.) This number will
     * be added to the total byte count.
     *
     */
    updateTotals(fileCount, dirCount, byteCount) {
        let filesTotal = this.updateTotal('#totalFileCount', fileCount);
        let dirTotal = this.updateTotal('#totalDirCount', dirCount);
        let byteTotal = this.updateTotal('#totalByteCount', byteCount);
        this.job.fileCount = filesTotal;
        this.job.dirCount = dirTotal;
        this.job.byteCount = byteTotal;
    }

    /**
     * Updates one of the total fields at the bottom of the list of
     * files and folders, and returns the calculated total.
     *
     * @param {string} elementId - The id (css selector) of the element
     * whose text should be updated.
     *
     * @param {number} amountToAdd - The number to add to the existing
     * total already displayed in the cell. This will be negative in
     * cases where you're removing files or folders.
     *
     * @returns {number} The total value displayed in the field.
     */
    updateTotal(elementId, amountToAdd) {
        let element = $(elementId);
        let newTotal = parseInt(element.data('total'), 10) + amountToAdd;
        element.data('total', newTotal);
        if (elementId == '#totalByteCount') {
            element.text(Util.toHumanSize(newTotal));
        } else {
            element.text(newTotal);
        }
        return newTotal;
    }

    /**
     * Returns the HTML for a single table row in the files/folders
     * display.
     *
     * @param {string} filepath - The absolute path to a file or folder
     * that is to be packaged as part of this job.
     *
     * @param {string} type - The type of item that filepath represents.
     * This should be either "file" or "directory".
     *
     * @param {number} fileCount - The number of files contained by the
     * item in param filepath. For files, this will be 1. For directories,
     * it will be the total number of files in the directory and all of
     * its subdirectories. (This info is readily available from the
     * {@link FileSystemReader} plugin.)
     *
     * @param {number} dirCount - The number of directories contained by
     * the item in param filepath. For files, this will be 0. For directories,
     * this will be 1 (for the directory itself) plus the total count of
     * all directories within that directory and all of its subdirectories.
     * (This info is readily available from the {@link FileSystemReader}
     * plugin.)
     *
     * @param {number} byteCount - The total number of bytes contained by
     * the item at filepath. For files, this will be the filesize. For
     * directories, this will be the sum of bytes contained by all the files
     * within the directory and all its subdirectories. (This info is readily
     * available from the {@link FileSystemReader} plugin.)
     *
     * @returns {string} A string of HTML representing a table row.
     */
    getTableRow(filepath, type, fileCount, dirCount, byteCount) {
        let iconType = (type == 'file' ? 'file' : 'folder-closed');
        let data = {
            iconType: iconType,
            filepath: Util.escapeBackslashes(filepath),
            dirCount: dirCount,
            fileCount: fileCount,
            byteCount: byteCount,
            size: Util.toHumanSize(byteCount)
        }
        return Templates.jobFileRow(data);
    }

    /**
     * This adds a file or folder to the list of items that will be
     * packaged in this job. The item is added to the sourceFiles
     * list of the Job's {@link PackageOperation} attribute.
     *
     * @param {string} filepath - The absolute path to a file or folder
     * that is to be packaged as part of this job.
     *
     */
    addFileToPackageSources(filepath) {
        if (!Array.isArray(this.job.packageOp.sourceFiles)) {
            this.job.packageOp.sourceFiles = [];
        }
        this.job.packageOp.sourceFiles.push(filepath);
    }

    /**
     * This checks the list of source files and folders to see if
     * any of the items already set to be packaged contains the item
     * that the user just dragged into the window. We make this check
     * to avoid adding duplicate files or folders to the list of
     * sources.
     *
     * @param {string} filepath - The absolute path to a file or folder
     * that is to be packaged as part of this job.
     *
     * @return {string} - The path the already-added folder that contains
     * filepath, or null if filepath's containing folder has not already
     * been added to the list of source files.
     *
     */
    findContainingItem(filepath) {
        if (Array.isArray(this.job.packageOp.sourceFiles)) {
            for (let item of this.job.packageOp.sourceFiles) {
                if (filepath.startsWith(item)) {
                    return item;
                }
            }
        }
        return null;
    }

    /**
     * This deletes a file or folder from the list of files and folders
     * in the UI (but does not delete the item from the underlying
     * {@link PackageOperation}).
     *
     * @param {string} filepath - The absolute path to a file or folder
     * to be removed from the UI.
     *
     */
    removeItemFromUI(filepath) {
        let row = $(`tr[data-filepath="${Util.escapeBackslashes(filepath)}"]`)
        let dirCount = -1 * parseInt(row.find('td.dirCount').text(), 10);
        let fileCount = -1 * parseInt(row.find('td.fileCount').text(), 10);
        let fileSize = -1 * parseInt(row.find('td.fileSize').data('bytes'), 10);
        row.remove();
        this.updateTotals(fileCount, dirCount, fileSize)
        if ($('tr.filepath').length == 0) {
            $('#filesPanel').hide();
        }
    }

}

module.exports.JobFilesController = JobFilesController;
<|endoftext|>


<commit_before>import { jsdom } from 'jsdom';
import 'css-modules-require-hook';

global.document = jsdom('<!doctype html>');
global.window = document.defaultView;
global.navigator = global.window.navigator;
<commit_msg>Add generateScopedName<commit_after>import { jsdom } from 'jsdom';
import hook from 'css-modules-require-hook';

hook({
  generateScopedName: '[name]__[local]___[hash:base64:5]'
});

global.document = jsdom('<!doctype html>');
global.window = document.defaultView;
global.navigator = global.window.navigator;
<|endoftext|>