<commit_before>// https://github.com/sanniassin/react-input-mask

import React from 'react';

class InputElement extends React.Component {
  defaultCharsRules = {
    '9': '[0-9]',
    'a': '[A-Za-z]',
    '*': '[A-Za-z0-9]'
  }
  defaultMaskChar = '_'
  lastCursorPos = null

  constructor(props) {
    super(props);

    this.hasValue = props.value != null;
    this.charsRules = props.formatChars != null
      ? props.formatChars
      : this.defaultCharsRules;

    var mask = this.parseMask(props.mask);
    var defaultValue = props.defaultValue != null
      ? props.defaultValue
      : '';
    var value = props.value != null
      ? props.value
      : defaultValue;

    value = this.getStringValue(value);

    this.mask = mask.mask;
    this.permanents = mask.permanents;
    this.lastEditablePos = mask.lastEditablePos;
    this.maskChar = 'maskChar' in props
      ? props.maskChar
      : this.defaultMaskChar;

    if (this.mask && (props.alwaysShowMask || value)) {
      value = this.formatValue(value);
    }

    this.state = { value };
  }

  isAndroidBrowser = () => {
    var windows = new RegExp('windows', 'i');
    var firefox = new RegExp('firefox', 'i');
    var android = new RegExp('android', 'i');
    var ua = navigator.userAgent;
    return !windows.test(ua)
           &&
           !firefox.test(ua)
           &&
           android.test(ua);
  }

  isWindowsPhoneBrowser = () => {
    var windows = new RegExp('windows', 'i');
    var phone = new RegExp('phone', 'i');
    var ua = navigator.userAgent;
    return windows.test(ua) && phone.test(ua);
  }

  isAndroidFirefox = () => {
    var windows = new RegExp('windows', 'i');
    var firefox = new RegExp('firefox', 'i');
    var android = new RegExp('android', 'i');
    var ua = navigator.userAgent;
    return !windows.test(ua)
           &&
           firefox.test(ua)
           &&
           android.test(ua);
  }

  isDOMElement = (element) => {
    return typeof HTMLElement === 'object'
         ? element instanceof HTMLElement // DOM2
         : element.nodeType === 1 && typeof element.nodeName === 'string';
  }

  getInputDOMNode = () => {
    var input = this.input;

    if (!input) {
      return null;
    }

    if (this.isDOMElement(input)) {
      return input;
    }

    // React 0.13
    return React.findDOMNode(input);
  }

  enableValueAccessors = () => {
    if (this.canUseAccessors) {
      var input = this.getInputDOMNode();
      this.valueDescriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(input), 'value');
      Object.defineProperty(input, 'value', {
        configurable: true,
        enumerable: true,
        get: () => this.value,
        set: (val) => {
          this.value = val;
          this.valueDescriptor.set.call(input, val);
        }
      });
    }
  }

  disableValueAccessors = () => {
    var { valueDescriptor } = this;
    if (!valueDescriptor) {
      return;
    }
    this.valueDescriptor = null;
    var input = this.getInputDOMNode();
    Object.defineProperty(input, 'value', valueDescriptor);
  }

  getInputValue = () => {
    var input = this.getInputDOMNode();
    var { valueDescriptor } = this;

    var value;
    if (valueDescriptor) {
      value = valueDescriptor.get.call(input);
    } else {
      value = input.value;
    }

    return value;
  }

  setInputValue = (val) => {
    var input = this.getInputDOMNode();
    this.value = val;
    input.value = val;
  }

  getPrefix = () => {
    var prefix = '';
    var { mask } = this;
    for (var i = 0; i < mask.length && this.isPermanentChar(i); ++i) {
      prefix += mask[i];
    }
    return prefix;
  }

  getFilledLength = (value = this.state.value) => {
    var i;
    var { maskChar } = this;

    if (!maskChar) {
      return value.length;
    }

    for (i = value.length - 1; i >= 0; --i) {
      var character = value[i];
      if (!this.isPermanentChar(i) && this.isAllowedChar(character, i)) {
        break;
      }
    }

    return ++i || this.getPrefix().length;
  }

  getLeftEditablePos = (pos) => {
    for (var i = pos; i >= 0; --i) {
      if (!this.isPermanentChar(i)) {
        return i;
      }
    }
    return null;
  }

  getRightEditablePos = (pos) => {
    var { mask } = this;
    for (var i = pos; i < mask.length; ++i) {
      if (!this.isPermanentChar(i)) {
        return i;
      }
    }
    return null;
  }

  isEmpty = (value = this.state.value) => {
    return !value.split('').some((character, i) =>
      !this.isPermanentChar(i) && this.isAllowedChar(character, i)
    );
  }

  isFilled = (value = this.state.value) => {
    return this.getFilledLength(value) === this.mask.length;
  }

  createFilledArray = (length, val) => {
    var array = [];
    for (var i = 0; i < length; i++) {
      array[i] = val;
    }
    return array;
  }

  formatValue = (value) => {
    var { maskChar, mask } = this;

    if (!maskChar) {
      var prefix = this.getPrefix();
      var prefixLen = prefix.length;
      value = this.insertRawSubstr('', value, 0);
      while (value.length > prefixLen && this.isPermanentChar(value.length - 1)) {
        value = value.slice(0, value.length - 1);
      }

      if (value.length < prefixLen) {
        value = prefix;
      }

      return value;
    }

    if (value) {
      var emptyValue = this.formatValue('');
      return this.insertRawSubstr(emptyValue, value, 0);
    }

    return value.split('')
                .concat(this.createFilledArray(mask.length - value.length, null))
                .map((character, pos) => {
                  if (this.isAllowedChar(character, pos)) {
                    return character;
                  } else if (this.isPermanentChar(pos)) {
                    return mask[pos];
                  }
                  return maskChar;
                })
                .join('');
  }

  clearRange = (value, start, len) => {
    var end = start + len;
    var { maskChar, mask } = this;

    if (!maskChar) {
      var prefixLen = this.getPrefix().length;
      value = value.split('')
                   .filter((character, i) => i < prefixLen || i < start || i >= end)
                   .join('');

      return this.formatValue(value);
    }

    return value
      .split('')
      .map((character, i) => {
        if (i < start || i >= end) {
          return character;
        }
        if (this.isPermanentChar(i)) {
          return mask[i];
        }
        return maskChar;
      })
      .join('');
  }

  replaceSubstr = (value, newSubstr, pos) => {
    return value.slice(0, pos) + newSubstr + value.slice(pos + newSubstr.length);
  }

  insertRawSubstr = (value, substr, pos) => {
    var { mask, maskChar } = this;
    var isFilled = this.isFilled(value);
    var prefixLen = this.getPrefix().length;
    substr = substr.split('');

    if (!maskChar && pos > value.length) {
      value += mask.slice(value.length, pos);
    }

    for (var i = pos; i < mask.length && substr.length;) {
      var isPermanent = this.isPermanentChar(i);
      if (!isPermanent || mask[i] === substr[0]) {
        var character = substr.shift();
        if (this.isAllowedChar(character, i, true)) {
          if (i < value.length) {
            if (maskChar || isFilled || i < prefixLen) {
              value = this.replaceSubstr(value, character, i);
            } else {
              value = this.formatValue(value.substr(0, i) + character + value.substr(i));
            }
          } else if (!maskChar) {
            value += character;
          }
          ++i;
        }
      } else {
        if (!maskChar && i >= value.length) {
          value += mask[i];
        } else if (maskChar && isPermanent && substr[0] === maskChar) {
          substr.shift();
        }
        ++i;
      }
    }
    return value;
  }

  getRawSubstrLength = (value, substr, pos) => {
    var { mask } = this;
    var i = pos;
    substr = substr.split('');
    while (i < mask.length && substr.length) {
      if (!this.isPermanentChar(i) || mask[i] === substr[0]) {
        var character = substr.shift();
        if (this.isAllowedChar(character, i, true)) {
          ++i;
        }
      } else {
        ++i;
      }
    }
    return i - pos;
  }

  isAllowedChar = (character, pos, allowMaskChar = false) => {
    var { mask, maskChar } = this;

    if (this.isPermanentChar(pos)) {
      return mask[pos] === character;
    }

    var ruleChar = mask[pos];
    var charRule = this.charsRules[ruleChar];

    return (new RegExp(charRule)).test(character || '')
           ||
           (allowMaskChar && character === maskChar);
  }

  isPermanentChar = (pos) => {
    return this.permanents.indexOf(pos) !== -1;
  }

  setCursorToEnd = () => {
    var filledLen = this.getFilledLength();
    var pos = this.getRightEditablePos(filledLen);
    if (pos !== null) {
      this.setCursorPos(pos);
    }
  }

  setSelection = (start, len = 0) => {
    var input = this.getInputDOMNode();
    if (!input) {
      return;
    }

    var end = start + len;
    if ('selectionStart' in input && 'selectionEnd' in input) {
      input.selectionStart = start;
      input.selectionEnd = end;
    } else {
      var range = input.createTextRange();
      range.collapse(true);
      range.moveStart('character', start);
      range.moveEnd('character', end - start);
      range.select();
    }
  }

  getSelection = () => {
    var input = this.getInputDOMNode();
    var start = 0;
    var end = 0;

    if ('selectionStart' in input && 'selectionEnd' in input) {
      start = input.selectionStart;
      end = input.selectionEnd;
    } else {
      var range = document.selection.createRange();
      if (range.parentElement() === input) {
        start = -range.moveStart('character', -input.value.length);
        end = -range.moveEnd('character', -input.value.length);
      }
    }

    return {
      start: start,
      end: end,
      length: end - start
    };
  }

  getCursorPos = () => {
    return this.getSelection().start;
  }

  setCursorPos = (pos) => {
    var raf = window.requestAnimationFrame
        ||
        window.webkitRequestAnimationFrame
        ||
        window.mozRequestAnimationFrame
        ||
        ((fn) => setTimeout(fn, 0));

    var setPos = this.setSelection.bind(this, pos, 0);

    setPos();
    raf(setPos);

    this.lastCursorPos = pos;
  }

  isFocused = () => {
    return document.activeElement === this.getInputDOMNode();
  }

  parseMask = (mask) => {
    if (!mask || typeof mask !== 'string') {
      return {
        mask: null,
        lastEditablePos: null,
        permanents: []
      };
    }
    var str = '';
    var permanents = [];
    var isPermanent = false;
    var lastEditablePos = null;

    mask.split('')
        .forEach((character) => {
          if (!isPermanent && character === '\\') {
            isPermanent = true;
          } else {
            if (isPermanent || !this.charsRules[character]) {
              permanents.push(str.length);
            } else {
              lastEditablePos = str.length + 1;
            }
            str += character;
            isPermanent = false;
          }
        });

    return {
      mask: str,
      lastEditablePos: lastEditablePos,
      permanents: permanents
    };
  }

  getStringValue = (value) => {
    return !value && value !== 0 ? '' : value + '';
  }

  componentWillMount = () => {
    var { mask } = this;
    var { value } = this.state;
    if (mask && value) {
      this.setState({ value });
    }
  }

  componentWillReceiveProps = (nextProps) => {
    this.hasValue = this.props.value != null;
    this.charsRules = nextProps.formatChars != null
      ? nextProps.formatChars
      : this.defaultCharsRules;

    var oldMask = this.mask;
    var mask = this.parseMask(nextProps.mask);
    var isMaskChanged = mask.mask && mask.mask !== this.mask;

    this.mask = mask.mask;
    this.permanents = mask.permanents;
    this.lastEditablePos = mask.lastEditablePos;
    this.maskChar = 'maskChar' in nextProps
      ? nextProps.maskChar
      : this.defaultMaskChar;

    if (!this.mask) {
      this.lastCursorPos = null;
      return;
    }

    var newValue = nextProps.value != null
      ? this.getStringValue(nextProps.value)
      : this.state.value;

    if (!oldMask && nextProps.value == null) {
      newValue = this.getInputDOMNode().value;
    }

    var showEmpty = nextProps.alwaysShowMask || this.isFocused();
    if (isMaskChanged || (mask.mask && (newValue || showEmpty))) {
      newValue = this.formatValue(newValue);

      if (isMaskChanged) {
        var pos = this.lastCursorPos;
        var filledLen = this.getFilledLength(newValue);
        if (pos === null || filledLen < pos) {
          if (this.isFilled(newValue)) {
            pos = filledLen;
          } else {
            pos = this.getRightEditablePos(filledLen);
          }
          this.setCursorPos(pos);
        }
      }
    }
    if (mask.mask && this.isEmpty(newValue) && !showEmpty && (!this.hasValue || !nextProps.value)) {
      newValue = '';
    }
    this.value = newValue;
    if (this.state.value !== newValue) {
      this.setState({ value: newValue });
    }
  }

  componentDidUpdate = (prevProps) => {
    if ((this.mask || prevProps.mask) && this.props.value == null) {
      this.updateUncontrolledInput();
    }
    if (this.valueDescriptor && this.getInputValue() !== this.state.value) {
      this.setInputValue(this.state.value);
    }
  }

  updateUncontrolledInput = () => {
    if (this.getInputValue() !== this.state.value) {
      this.setInputValue(this.state.value);
    }
  }

  onKeyDown = (event) => {
    var hasHandler = typeof this.props.onKeyDown === 'function';
    if (event.ctrlKey || event.metaKey) {
      if (hasHandler) {
        this.props.onKeyDown(event);
      }
      return;
    }

    var cursorPos = this.getCursorPos();
    var value = this.state.value;
    var key = event.key;
    var preventDefault = false;
    switch (key) {
      case 'Backspace':
      case 'Delete':
        var prefixLen = this.getPrefix().length;
        var deleteFromRight = key === 'Delete';
        var selectionRange = this.getSelection();
        if (selectionRange.length) {
          value = this.clearRange(value, selectionRange.start, selectionRange.length);
        } else if (cursorPos < prefixLen || (!deleteFromRight && cursorPos === prefixLen)) {
          cursorPos = prefixLen;
        } else {
          var editablePos = deleteFromRight
            ? this.getRightEditablePos(cursorPos)
            : this.getLeftEditablePos(cursorPos - 1);

          if (editablePos !== null) {
            value = this.clearRange(value, editablePos, 1);
            cursorPos = editablePos;
          }
        }
        preventDefault = true;
        break;
      default:
        break;
    }

    if (hasHandler) {
      this.props.onKeyDown(event);
    }

    if (value !== this.state.value) {
      this.setInputValue(value);
      this.setState({
        value: this.hasValue ? this.state.value : value
      });
      preventDefault = true;
      if (typeof this.props.onChange === 'function') {
        this.props.onChange(event);
      }
    }
    if (preventDefault) {
      event.preventDefault();
      this.setCursorPos(cursorPos);
    }
  }

  onKeyPress = (event) => {
    var key = event.key;
    var hasHandler = typeof this.props.onKeyPress === 'function';
    if (key === 'Enter' || event.ctrlKey || event.metaKey) {
      if (hasHandler) {
        this.props.onKeyPress(event);
      }
      return;
    }

    if (this.isWindowsPhoneBrowser) {
      return;
    }

    var cursorPos = this.getCursorPos();
    var selection = this.getSelection();
    var { value } = this.state;
    var { mask, lastEditablePos } = this;
    var prefixLen = this.getPrefix().length;

    if (this.isPermanentChar(cursorPos) && mask[cursorPos] === key) {
      value = this.insertRawSubstr(value, key, cursorPos);
      ++cursorPos;
    } else {
      var editablePos = this.getRightEditablePos(cursorPos);
      if (editablePos !== null && this.isAllowedChar(key, editablePos)) {
        value = this.clearRange(value, selection.start, selection.length);
        value = this.insertRawSubstr(value, key, editablePos);
        cursorPos = editablePos + 1;
      }
    }

    if (value !== this.state.value) {
      this.setInputValue(value);
      this.setState({
        value: this.hasValue ? this.state.value : value
      });
      if (typeof this.props.onChange === 'function') {
        this.props.onChange(event);
      }
    }
    event.preventDefault();
    if (cursorPos < lastEditablePos && cursorPos > prefixLen) {
      cursorPos = this.getRightEditablePos(cursorPos);
    }
    this.setCursorPos(cursorPos);
  }

  onChange = (event) => {
    var { pasteSelection, mask, maskChar, lastEditablePos } = this;
    var value = this.getInputValue();
    if (!value && this.preventEmptyChange) {
      this.disableValueAccessors();
      this.preventEmptyChange = false;
      this.setInputValue(this.state.value);
      return;
    }
    var oldValue = this.state.value;
    if (pasteSelection) {
      this.pasteSelection = null;
      this.pasteText(oldValue, value, pasteSelection, event);
      return;
    }
    var selection = this.getSelection();
    var cursorPos = selection.end;
    var maskLen = mask.length;
    var valueLen = value.length;
    var oldValueLen = oldValue.length;
    var prefixLen = this.getPrefix().length;
    var clearedValue;

    if (valueLen > oldValueLen) {
      var substrLen = valueLen - oldValueLen;
      var startPos = selection.end - substrLen;
      var enteredSubstr = value.substr(startPos, substrLen);

      if (startPos < lastEditablePos && (substrLen !== 1 || enteredSubstr !== mask[startPos])) {
        cursorPos = this.getRightEditablePos(startPos);
      } else {
        cursorPos = startPos;
      }

      value = value.substr(0, startPos) + value.substr(startPos + substrLen);

      clearedValue = this.clearRange(value, startPos, maskLen - startPos);
      clearedValue = this.insertRawSubstr(clearedValue, enteredSubstr, cursorPos);

      value = this.insertRawSubstr(oldValue, enteredSubstr, cursorPos);

      if (substrLen !== 1 || (cursorPos >= prefixLen && cursorPos < lastEditablePos)) {
        cursorPos = this.getFilledLength(clearedValue);
      } else if (cursorPos < lastEditablePos) {
        cursorPos++;
      }
    } else if (valueLen < oldValueLen) {
      var removedLen = maskLen - valueLen;
      clearedValue = this.clearRange(oldValue, selection.end, removedLen);
      var substr = value.substr(0, selection.end);
      var clearOnly = substr === oldValue.substr(0, selection.end);

      if (maskChar) {
        value = this.insertRawSubstr(clearedValue, substr, 0);
      }

      clearedValue = this.clearRange(clearedValue, selection.end, maskLen - selection.end);
      clearedValue = this.insertRawSubstr(clearedValue, substr, 0);

      if (!clearOnly) {
        cursorPos = this.getFilledLength(clearedValue);
      } else if (cursorPos < prefixLen) {
        cursorPos = prefixLen;
      }
    }
    value = this.formatValue(value);

    if (this.isWindowsPhoneBrowser) {
      event.persist();
      setTimeout(() => {
        this.setInputValue(value);

        if (!this.hasValue) {
          this.setState({
            value: value
          });
        }

        if (typeof this.props.onChange === 'function') {
          this.props.onChange(event);
        }

        this.setCursorPos(cursorPos);
      }, 0);
    } else {
      // prevent android autocomplete insertion on backspace
      if (!this.canUseAccessors || (!this.isAndroidBrowser)) {
        this.setInputValue(value);
      }

      if (this.canUseAccessors && ((this.isAndroidFirefox && value && !this.getInputValue()) || this.isAndroidBrowser)) {
        this.value = value;
        this.enableValueAccessors();
        if (this.isAndroidFirefox) {
          this.preventEmptyChange = true;
        }
        setTimeout(() => {
          this.preventEmptyChange = false;
          this.disableValueAccessors();
        }, 0);
      }

      this.setState({
        value: this.hasValue ? this.state.value : value
      });

      if (typeof this.props.onChange === 'function') {
        this.props.onChange(event);
      }

      this.setCursorPos(cursorPos);
    }
  }

  onFocus = (event) => {
    if (!this.state.value) {
      var prefix = this.getPrefix();
      var value = this.formatValue(prefix);
      var inputValue = this.formatValue(value);

      // do not use this.getInputValue and this.setInputValue as this.input
      // can be undefined at this moment if autoFocus attribute is set
      var isInputValueChanged = inputValue !== event.target.value;

      if (isInputValueChanged) {
        event.target.value = inputValue;
      }

      this.setState({
        value: this.hasValue ? this.state.value : inputValue
      }, this.setCursorToEnd);

      if (isInputValueChanged && typeof this.props.onChange === 'function') {
        this.props.onChange(event);
      }
    } else if (this.getFilledLength() < this.mask.length) {
      this.setCursorToEnd();
    }

    if (typeof this.props.onFocus === 'function') {
      this.props.onFocus(event);
    }
  }

  onBlur = (event) => {
    if (!this.props.alwaysShowMask && this.isEmpty(this.state.value)) {
      var inputValue = '';
      var isInputValueChanged = inputValue !== this.getInputValue();
      if (isInputValueChanged) {
        this.setInputValue(inputValue);
      }
      this.setState({
        value: this.hasValue ? this.state.value : ''
      });
      if (isInputValueChanged && typeof this.props.onChange === 'function') {
        this.props.onChange(event);
      }
    }

    if (typeof this.props.onBlur === 'function') {
      this.props.onBlur(event);
    }
  }

  onPaste = (event) => {
    if (this.isAndroidBrowser) {
      this.pasteSelection = this.getSelection();
      this.setInputValue('');
      return;
    }
    var text;
    if (window.clipboardData && window.clipboardData.getData) { // IE
      text = window.clipboardData.getData('Text');
    } else if (event.clipboardData && event.clipboardData.getData) {
      text = event.clipboardData.getData('text/plain');
    }
    if (text) {
      var value = this.state.value;
      var selection = this.getSelection();
      this.pasteText(value, text, selection, event);
    }
    event.preventDefault();
  }

  pasteText = (value, text, selection, event) => {
    var cursorPos = selection.start;
    if (selection.length) {
      value = this.clearRange(value, cursorPos, selection.length);
    }
    var textLen = this.getRawSubstrLength(value, text, cursorPos);
    value = this.insertRawSubstr(value, text, cursorPos);
    cursorPos += textLen;
    cursorPos = this.getRightEditablePos(cursorPos) || cursorPos;
    if (value !== this.getInputValue()) {
      if (event) {
        this.setInputValue(value);
      }
      this.setState({
        value: this.hasValue ? this.state.value : value
      });
      if (event && typeof this.props.onChange === 'function') {
        this.props.onChange(event);
      }
    }
    this.setCursorPos(cursorPos);
  }

  componentDidMount = () => {
    this.isAndroidBrowser = this.isAndroidBrowser();
    this.isWindowsPhoneBrowser = this.isWindowsPhoneBrowser();
    this.isAndroidFirefox = this.isAndroidFirefox();

    var input = this.getInputDOMNode();

    // workaround for Jest
    // it doesn't mount a real node so input will be null
    if (input && Object.getOwnPropertyDescriptor && Object.getPrototypeOf && Object.defineProperty) {
      var valueDescriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(input), 'value');
      this.canUseAccessors = !!(valueDescriptor && valueDescriptor.get && valueDescriptor.set);
    }

    if (this.mask && this.props.value == null) {
      this.updateUncontrolledInput();
    }
  }

  render = () => {
    var { mask, alwaysShowMask, maskChar, formatChars, ...props } = this.props;
    if (this.mask) {
      if (!props.disabled && !props.readOnly) {
        var handlersKeys = ['onFocus', 'onBlur', 'onChange', 'onKeyDown', 'onKeyPress', 'onPaste'];
        handlersKeys.forEach((key) => {
          props[key] = this[key];
        });
      }

      if (props.value != null) {
        props.value = this.state.value;
      }
    }
    return <input ref={ref => this.input = ref} {...props} />;
  }
}

export default InputElement;
<commit_msg>delete InputElement.js<commit_after><|endoftext|>


<commit_before>var plugins = require('../config.json').plugins;

module.exports = function(cb) {
  var js = [];
  var ng = [];
  var st = [];

  ng.push('assets/javascript/app.js');
  ng.push('assets/javascript/services/*.js');
  ng.push('assets/javascript/controllers/*.js');
  js.push('./helpers/*.js');
  js.push('./api/*.js');
  js.push('./web/*.js');
  js.push('./autoform/*.js');
  js.push('./*.js');
  js.push('assets/javascript/*.js');
  js.push('assets/javascript/services/*.js');
  js.push('assets/javascript/controllers/*.js');
  js.push('test/*.js');
  js.push('test/e2e/*.js');
  js.push('test/helpers/*.js');
  st.push('assets/styles/*.css');

  for(var idx in plugins){
    if(plugins.hasOwnProperty(idx)){
      var name = plugins[idx];
      js.push('plugins/' + name + '/helpers/*.js');
      js.push('plugins/' + name + '/api/*.js');
      js.push('plugins/' + name + '/*.js');
      js.push('plugins/' + name + '/web/*.js');
      js.push('plugins/' + name + '/api/*.js');
      js.push('plugins/' + name + '/assets/javascript/*.js');
      js.push('plugins/' + name + '/assets/javascript/services/*.js');
      js.push('plugins/' + name + '/assets/javascript/controllers/*.js');
      js.push('plugins/' + name + '/test/*.js');
      js.push('plugins/' + name + '/test/e2e/*.js');
      js.push('plugins/' + name + '/test/helpers/*.js');
      ng.push('plugins/' + name + '/assets/javascript/services/*.js');
      ng.push('plugins/' + name + '/assets/javascript/controllers/*.js');
      st.push('plugins/' + name + '/assets/styles/*.css');
    }
  }

  cb(js, ng, st);
};
<commit_msg>Add directives to gulp pipe.<commit_after>var plugins = require('../config.json').plugins;

module.exports = function(cb) {
  var js = [];
  var ng = [];
  var st = [];

  ng.push('assets/javascript/app.js');
  ng.push('assets/javascript/services/*.js');
  ng.push('assets/javascript/controllers/*.js');
  ng.push('assets/javascript/directives/*.js');
  js.push('./helpers/*.js');
  js.push('./api/*.js');
  js.push('./web/*.js');
  js.push('./autoform/*.js');
  js.push('./*.js');
  js.push('assets/javascript/*.js');
  js.push('assets/javascript/services/*.js');
  js.push('assets/javascript/controllers/*.js');
  js.push('assets/javascript/directives/*.js');
  js.push('test/*.js');
  js.push('test/e2e/*.js');
  js.push('test/helpers/*.js');
  st.push('assets/styles/*.css');

  for(var idx in plugins){
    if(plugins.hasOwnProperty(idx)){
      var name = plugins[idx];
      js.push('plugins/' + name + '/helpers/*.js');
      js.push('plugins/' + name + '/api/*.js');
      js.push('plugins/' + name + '/*.js');
      js.push('plugins/' + name + '/web/*.js');
      js.push('plugins/' + name + '/api/*.js');
      js.push('plugins/' + name + '/assets/javascript/*.js');
      js.push('plugins/' + name + '/assets/javascript/services/*.js');
      js.push('plugins/' + name + '/assets/javascript/controllers/*.js');
      js.push('plugins/' + name + '/assets/javascript/directives/*.js');
      js.push('plugins/' + name + '/test/*.js');
      js.push('plugins/' + name + '/test/e2e/*.js');
      js.push('plugins/' + name + '/test/helpers/*.js');
      ng.push('plugins/' + name + '/assets/javascript/services/*.js');
      ng.push('plugins/' + name + '/assets/javascript/controllers/*.js');
      ng.push('plugins/' + name + '/assets/javascript/directives/*.js');
      st.push('plugins/' + name + '/assets/styles/*.css');
    }
  }

  cb(js, ng, st);
};
<|endoftext|>


<commit_before>import moment from 'moment';
import 'moment-timezone';
import 'moment-business-time';

import { avgOfArray, percentOf } from '/server/calcOps';
import Config from '/server/hardConfig.js';
import { noIg } from './utility';

moment.updateLocale('en', {
  workinghours: Config.workingHours,
  shippinghours: Config.shippingHours
});

function toRelDiff(bSalesStart, bReleases) {

  const floorRelease = bReleases.find( x => x.type === 'floorRelease');
  const flrRelTime = floorRelease && floorRelease.time;

  const gapSale2Rel = !flrRelTime ? null :
    moment(flrRelTime).workingDiff(bSalesStart, 'days');

  return gapSale2Rel;
}

function toStrtDiff(bSalesStart, bTide) {

  const tideBegin = bTide && bTide.length > 0 ? bTide[0] : null;
  const beginTime = tideBegin ? tideBegin.startTime : null;

  const gapSale2Start = !beginTime ? null :
    moment(beginTime).workingDiff(bSalesStart, 'days');

  return gapSale2Start;
}

function toFFinDiff(bSalesStart, items) {
  const fitems = items.filter( i => i.completed );
  const itemS = fitems.sort( (i1, i2)=>
    i1.completedAt < i2.completedAt ? -1 : i1.completedAt > i2.completedAt ? 1 : 0 );

  const firstfinish = itemS.length > 0 ? itemS[0].completedAt : false;

  const gapSale2FFin = firstfinish ? moment(firstfinish).workingDiff(bSalesStart, 'days') : 0;
  return gapSale2FFin;
}

function toCompDiff(bSalesStart, bComplete) {
  const gapSale2Complete = moment(bComplete).workingDiff(bSalesStart, 'days');
  return gapSale2Complete;
}

function getWidgetDur(widget) {

  const turnStats = widget.turnStats || null;
  const statime = turnStats ? turnStats.updatedAt : null;
  const stale = !statime ? true :
            moment.duration(moment().diff(moment(statime))).as('hours') > 12;
  if(stale) {

    let relAvg = [];
    let stAvg = [];
    let ffinAvg = [];
    let compAvg = [];

    const compX = XBatchDB.find({widgetId: widget._id, completed: true}).fetch();
    for( let x of compX ) {
      const srs = XSeriesDB.findOne({_id: x._id});
      const items = srs ? srs.items : [];

      relAvg.push( toRelDiff(x.salesStart, x.releases) );
      stAvg.push( toStrtDiff(x.salesStart, x.tide) );
      ffinAvg.push( toFFinDiff(x.salesStart, items) );
      compAvg.push( toCompDiff(x.salesStart, x.completedAt) );
    }

    const avgWorkDays = {
      relAvg: avgOfArray( relAvg ),
      stAvg: avgOfArray( stAvg ),
      ffinAvg: avgOfArray( ffinAvg ),
      compAvg: avgOfArray( compAvg )
    };

    WidgetDB.update({_id: widget._id}, {
      $set : {
        turnStats: {
          stats: avgWorkDays,
          updatedAt: new Date(),
        }
      }});

    return avgWorkDays;
  }else{
    return turnStats.stats;
  }
}

function splitItmTm( items, tide, perBaseTen ) {
  const fitems = items.filter( i => i.completed );
  const etide = tide.filter( t => t.stopTime !== false);

  const durrs = Array.from(etide, x =>
                  moment.duration(moment(x.stopTime).diff(x.startTime)).asMinutes());
  const total = durrs.length > 0 ? durrs.reduce((x,y)=> x + y) : 0;

  if(fitems.length > 0 && tide && tide.length > 0) {
    const itemS = fitems.sort( (i1, i2)=>
      i1.completedAt < i2.completedAt ? -1 : i1.completedAt > i2.completedAt ? 1 : 0 );

    const itemCut = Math.floor( ( itemS.length / 100 ) * perBaseTen );
    const cutItem = itemS[itemCut];
    const cutMmnt = moment(cutItem.completedAt);

    let lTide = 0;

    for( let en of etide ) {
      if( cutMmnt.isBetween(en.startTime, en.stopTime ) ) {
        lTide += ( Math.abs( moment.duration(cutMmnt.diff(en.startTime)).asMinutes() ) );
      }else if( cutMmnt.isAfter(en.stopTime) ) {
        lTide += ( Math.abs( moment.duration(moment(en.stopTime).diff(en.startTime)).asMinutes() ) );
      }
    }

    const lpercent = percentOf(total, lTide);

    return lpercent;
  }else{
    return 0;
  }
}

Meteor.methods({

  oneWidgetTurnAround(wID) {

    const widget = WidgetDB.findOne({ _id: wID });

    const timeArr = getWidgetDur(widget);

    return timeArr;
  },

  estBatchTurnAround(bID, wID) {
    this.unblock();

    const now = moment().tz(Config.clientTZ);

    const batch = XBatchDB.findOne({ _id: bID },{fields:{'salesStart':1}});
    const salesMnt = moment(batch.salesStart).tz(Config.clientTZ);

    const widget = WidgetDB.findOne({ _id: wID },{fields:{'turnStats':1}});
    const turnStats = widget.turnStats || null;

    if(turnStats) {
      const trn = turnStats.stats;

      const rel = Math.round( trn.relAvg );
      const relEst = salesMnt.clone().addWorkingTime(rel, 'days');
      const relDif = relEst.workingDiff(now, 'days');

      const wrk = Math.round( trn.stAvg );
      const wrkEst = salesMnt.clone().addWorkingTime(wrk, 'days');
      const wrkDif = wrkEst.workingDiff(now, 'days');

      const ffinAvg = trn.ffinAvg;
      const fin = ffinAvg ? Math.round( ffinAvg ) : 0;
      const finEst = salesMnt.clone().addWorkingTime(fin, 'days');
      const finDif = finEst.workingDiff(now, 'days');

      const cmp = Math.round( trn.compAvg );
      const cmpEst = salesMnt.clone().addWorkingTime(cmp, 'days');
      const cmpDif = cmpEst.workingDiff(now, 'days');

      return [
        relEst.format(), relDif,
        wrkEst.format(), wrkDif,
        finEst.format(), finDif,
        cmpEst.format(), cmpDif
      ];
    }else{
      return 'na';
    }

  },

  updateAvgTimeShare(accessKey) {
    this.unblock();

    let onePerArr = [];
    let tenPerArr = [];
    let fftyPerArr = [];
    let svtyfvPerArr = [];
    let ntyPerArr = [];

    const xid = noIg();
    const compB = XBatchDB.find({live: false, groupId: { $ne: xid }});

    for( let b of compB ) {
      const srs = XSeriesDB.findOne({batch: b.batch});
      const items = srs ? srs.items : [];
      const tide = b.tide;

      onePerArr.push( splitItmTm(items, tide, 1) );
      tenPerArr.push( splitItmTm(items, tide, 10) );
      fftyPerArr.push( splitItmTm(items, tide, 50) );
      svtyfvPerArr.push( splitItmTm(items, tide, 75) );
      ntyPerArr.push( splitItmTm(items, tide, 90) );
    }

    const dataAvgs = [
      avgOfArray( onePerArr ),
      avgOfArray( tenPerArr ),
      avgOfArray( fftyPerArr ),
      avgOfArray( svtyfvPerArr ),
      avgOfArray( ntyPerArr )
    ];

    CacheDB.upsert({dataName: 'avgTimeShare'}, {
      $set : {
        orgKey: accessKey,
        lastUpdated: new Date(),
        dataName: 'avgTimeShare',
        dataNum: dataAvgs,
        dataTrend: 'flat'
    }});
  },

});<commit_msg>no tide fix<commit_after>import moment from 'moment';
import 'moment-timezone';
import 'moment-business-time';

import { avgOfArray, percentOf } from '/server/calcOps';
import Config from '/server/hardConfig.js';
import { noIg } from './utility';

moment.updateLocale('en', {
  workinghours: Config.workingHours,
  shippinghours: Config.shippingHours
});

function toRelDiff(bSalesStart, bReleases) {

  const floorRelease = bReleases.find( x => x.type === 'floorRelease');
  const flrRelTime = floorRelease && floorRelease.time;

  const gapSale2Rel = !flrRelTime ? null :
    moment(flrRelTime).workingDiff(bSalesStart, 'days');

  return gapSale2Rel;
}

function toStrtDiff(bSalesStart, bTide) {

  const tideBegin = bTide && bTide.length > 0 ? bTide[0] : null;
  const beginTime = tideBegin ? tideBegin.startTime : null;

  const gapSale2Start = !beginTime ? null :
    moment(beginTime).workingDiff(bSalesStart, 'days');

  return gapSale2Start;
}

function toFFinDiff(bSalesStart, items) {
  const fitems = items.filter( i => i.completed );
  const itemS = fitems.sort( (i1, i2)=>
    i1.completedAt < i2.completedAt ? -1 : i1.completedAt > i2.completedAt ? 1 : 0 );

  const firstfinish = itemS.length > 0 ? itemS[0].completedAt : false;

  const gapSale2FFin = firstfinish ? moment(firstfinish).workingDiff(bSalesStart, 'days') : 0;
  return gapSale2FFin;
}

function toCompDiff(bSalesStart, bComplete) {
  const gapSale2Complete = moment(bComplete).workingDiff(bSalesStart, 'days');
  return gapSale2Complete;
}

function getWidgetDur(widget) {

  const turnStats = widget.turnStats || null;
  const statime = turnStats ? turnStats.updatedAt : null;
  const stale = !statime ? true :
            moment.duration(moment().diff(moment(statime))).as('hours') > 12;
  if(stale) {

    let relAvg = [];
    let stAvg = [];
    let ffinAvg = [];
    let compAvg = [];

    const compX = XBatchDB.find({widgetId: widget._id, completed: true}).fetch();
    for( let x of compX ) {
      const srs = XSeriesDB.findOne({_id: x._id});
      const items = srs ? srs.items : [];

      relAvg.push( toRelDiff(x.salesStart, x.releases) );
      stAvg.push( toStrtDiff(x.salesStart, x.tide) );
      ffinAvg.push( toFFinDiff(x.salesStart, items) );
      compAvg.push( toCompDiff(x.salesStart, x.completedAt) );
    }

    const avgWorkDays = {
      relAvg: avgOfArray( relAvg ),
      stAvg: avgOfArray( stAvg ),
      ffinAvg: avgOfArray( ffinAvg ),
      compAvg: avgOfArray( compAvg )
    };

    WidgetDB.update({_id: widget._id}, {
      $set : {
        turnStats: {
          stats: avgWorkDays,
          updatedAt: new Date(),
        }
      }});

    return avgWorkDays;
  }else{
    return turnStats.stats;
  }
}

function splitItmTm( items, tide, perBaseTen ) {
  const fitems = items.filter( i => i.completed );
  const etide = tide.filter( t => t.stopTime !== false);

  const durrs = Array.from(etide, x =>
                  moment.duration(moment(x.stopTime).diff(x.startTime)).asMinutes());
  const total = durrs.length > 0 ? durrs.reduce((x,y)=> x + y) : 0;

  if(fitems.length > 0 && tide && tide.length > 0) {
    const itemS = fitems.sort( (i1, i2)=>
      i1.completedAt < i2.completedAt ? -1 : i1.completedAt > i2.completedAt ? 1 : 0 );

    const itemCut = Math.floor( ( itemS.length / 100 ) * perBaseTen );
    const cutItem = itemS[itemCut];
    const cutMmnt = moment(cutItem.completedAt);

    let lTide = 0;

    for( let en of etide ) {
      if( cutMmnt.isBetween(en.startTime, en.stopTime ) ) {
        lTide += ( Math.abs( moment.duration(cutMmnt.diff(en.startTime)).asMinutes() ) );
      }else if( cutMmnt.isAfter(en.stopTime) ) {
        lTide += ( Math.abs( moment.duration(moment(en.stopTime).diff(en.startTime)).asMinutes() ) );
      }
    }

    const lpercent = percentOf(total, lTide);

    return lpercent;
  }else{
    return 0;
  }
}

Meteor.methods({

  oneWidgetTurnAround(wID) {

    const widget = WidgetDB.findOne({ _id: wID });

    const timeArr = getWidgetDur(widget);

    return timeArr;
  },

  estBatchTurnAround(bID, wID) {
    this.unblock();

    const now = moment().tz(Config.clientTZ);

    const batch = XBatchDB.findOne({ _id: bID },{fields:{'salesStart':1}});
    const salesMnt = moment(batch.salesStart).tz(Config.clientTZ);

    const widget = WidgetDB.findOne({ _id: wID },{fields:{'turnStats':1}});
    const turnStats = widget.turnStats || null;

    if(turnStats) {
      const trn = turnStats.stats;

      const rel = Math.round( trn.relAvg );
      const relEst = salesMnt.clone().addWorkingTime(rel, 'days');
      const relDif = relEst.workingDiff(now, 'days');

      const wrk = Math.round( trn.stAvg );
      const wrkEst = salesMnt.clone().addWorkingTime(wrk, 'days');
      const wrkDif = wrkEst.workingDiff(now, 'days');

      const ffinAvg = trn.ffinAvg;
      const fin = ffinAvg ? Math.round( ffinAvg ) : 0;
      const finEst = salesMnt.clone().addWorkingTime(fin, 'days');
      const finDif = finEst.workingDiff(now, 'days');

      const cmp = Math.round( trn.compAvg );
      const cmpEst = salesMnt.clone().addWorkingTime(cmp, 'days');
      const cmpDif = cmpEst.workingDiff(now, 'days');

      return [
        relEst.format(), relDif,
        wrkEst.format(), wrkDif,
        finEst.format(), finDif,
        cmpEst.format(), cmpDif
      ];
    }else{
      return 'na';
    }

  },

  updateAvgTimeShare(accessKey) {
    this.unblock();

    let onePerArr = [];
    let tenPerArr = [];
    let fftyPerArr = [];
    let svtyfvPerArr = [];
    let ntyPerArr = [];

    const xid = noIg();
    const compB = XBatchDB.find({live: false, groupId: { $ne: xid }});

    for( let b of compB ) {
      const srs = XSeriesDB.findOne({batch: b.batch});
      const items = srs ? srs.items : [];
      const tide = b.tide ? b.tide : [];

      onePerArr.push( splitItmTm(items, tide, 1) );
      tenPerArr.push( splitItmTm(items, tide, 10) );
      fftyPerArr.push( splitItmTm(items, tide, 50) );
      svtyfvPerArr.push( splitItmTm(items, tide, 75) );
      ntyPerArr.push( splitItmTm(items, tide, 90) );
    }

    /*
      Number( Math.log(Number(0.95)) / Math.log(Number(0.75)) ).toFixed(2)
      "0.18"
      Number( Math.pow(0.75, 0.18) ).toFixed(2)
      "0.95"
    */

    const dataAvgs = [
      avgOfArray( onePerArr ),
      avgOfArray( tenPerArr ),
      avgOfArray( fftyPerArr ),
      avgOfArray( svtyfvPerArr ),
      avgOfArray( ntyPerArr )
    ];

    CacheDB.upsert({dataName: 'avgTimeShare'}, {
      $set : {
        orgKey: accessKey,
        lastUpdated: new Date(),
        dataName: 'avgTimeShare',
        dataNum: dataAvgs,
        dataTrend: 'flat'
    }});
  },

});<|endoftext|>


<commit_before>const express = require('express');
const basicAuth = require('basic-auth');
const models = require('../models');
const Errors = require('../lib/errors');

const Ticket = models.Ticket;
const Service = models.Service;
const router = express.Router({ mergeParams: true });

/**
 * @api {get} /tickets Get all tickets
 * @apiName GetTickets
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiSuccess  {Object[]}  tickets
 * @apiSuccess  {String}    tickets.service
 * @apiSuccess  {Date}      tickets.created
 * @apiSuccess  {Date}      tickets.updated
 * @apiSuccess  {Date}      tickets.completed
 * @apiSuccess  {Object}    tickets.details
 * @apiSuccess  {Object[]}  tickets.comments
 * @apiSuccess  {String}    tickets.comments.author
 * @apiSuccess  {String}    tickets.comments.body
 * @apiSuccess  {Date}      tickets.comments.created
 * @apiSuccess  {Date}      tickets.comments.updated
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.get('/', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    const options = {};
    if (req.body.service_id) {
      options.service = req.body.service_id;
    }

    Ticket.find(options)
      .then((tickets) => {
        res.body = tickets;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {get} /tickets/:ticketId Get specific ticket
 * @apiName GetTicketById
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam    {String}    ticketId
 *
 * @apiSuccess  {String}    service
 * @apiSuccess  {Date}      created
 * @apiSuccess  {Date}      updated
 * @apiSuccess  {Date}      completed
 * @apiSuccess  {Object}    details
 * @apiSuccess  {Object[]}  comments
 * @apiSuccess  {String}    comments.author
 * @apiSuccess  {String}    comments.body
 * @apiSuccess  {Date}      comments.created
 * @apiSuccess  {Date}      comments.updated
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.get('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        res.body = ticket;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets Create a ticket
 * @apiName PostTickets
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 * @apiDescription Registered services will post errors information to this resource. All
 * parameters passed into the request body will be recorded.
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission service
 */
router.post('/', (req, res, next) => {
  const credentials = basicAuth(req);
  if (!credentials || !credentials.name || !credentials.pass) {
    next(new Errors.Unauthorized());
  } else {
    Service.findById(credentials.name)
      .then(service => service.authorize(credentials.pass))
      .then(() => {
        const currentTime = new Date();
        const ticket = new Ticket({
          created: currentTime,
          updated: currentTime,
          service: credentials.name,
          details: req.body,
        });
        return ticket.save();
      })
      .then((ticket) => {
        res.body = ticket;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets/:ticketId Close a ticket
 * @apiName PostTicketsId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.post('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        ticket.completed = new Date();
        return ticket.save();
      })
      .then(() => {
        res.status(204);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {delete} /tickets/:ticketId Delete a ticket
 * @apiName DeleteTicketsId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.delete('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then(ticket => ticket.remove())
      .then(() => {
        res.status(204);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {get} /tickets/:ticketId/comments Get all ticket comments
 * @apiName GetTicketsIdComment
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiSuccess  {String}  _id     Comment ID
 * @apiSuccess  {String}  body    Comment contents
 * @apiSuccess  {Date}    created Creation time
 * @apiSuccess  {Date}    updated Update time
 * @apiSuccess  {String}  author  Comment's author
 *
 * @apiUse UnauthorizedError
 */
router.get('/:ticketId/comments', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .populate(['comments.author'])
      .then((ticket) => {
        res.body = ticket.comments;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets/:ticketId/comments Comment on a ticket
 * @apiName PostTicketsIdComment
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  message   Message contents for comment
 *
 * @apiSuccess  {String}  _id     Comment ID
 * @apiSuccess  {String}  body    Comment contents
 * @apiSuccess  {Date}    created Creation time
 * @apiSuccess  {Date}    updated Update time
 * @apiSuccess  {String}  author  Comment's author
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.post('/:ticketId/comments', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else if (!req.body.message) {
    next(new Errors.Generic('Message required', 400));
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        const comment = {
          author: req.session.user._id,
          body: req.body.message,
        };
        ticket.comments.push(comment);
        return ticket.save()
          .then(() => Promise.resolve(ticket.comments.pop()));
      })
      .then((comment) => {
        res.body = comment;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {get} /tickets/:ticketId/comments/:commentId Get a ticket comment
 * @apiName GetTicketsIdCommentId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  commentId ID of the comment
 *
 * @apiSuccess  {Object}  author          Author data
 * @apiSuccess  {String}  author._id      ID of author
 * @apiSuccess  {String}  author.name     Name of author
 * @apiSuccess  {String}  author.picture  Picture of author
 * @apiSuccess  {String}  _id             ID of comment
 * @apiSuccess  {String}  body            Body content of comment
 * @apiSuccess  {Date}    created         Comment creation date
 * @apiSuccess  {Date}    updated         Comment update date
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.get('/:ticketId/comments/:commentId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        res.body = ticket.comments.id(req.params.commentId);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {put} /tickets/:ticketId/comments/:commentId Update a ticket comment
 * @apiName PutTicketsIdCommentId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  commentId ID of the comment
 * @apiParam  {String}  message   Message contents for comment
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.put('/:ticketId/comments/:commentId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        const comment = ticket.comments.id(req.params.commentId);
        if (!comment || comment.author.toString() !== req.session.user._id) {
          return Promise.reject(new Errors.Unauthorized());
        }
        comment.body = req.body.message;
        comment.updated = new Date();
        return ticket.save();
      })
      .then((ticket) => {
        res.body = ticket.comments.id(req.params.commentId);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {delete} /tickets/:ticketId/comments/:commentId Delete a ticket comment
 * @apiName DeleteTicketsIdCommentId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  commentId ID of the comment
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.delete('/:ticketId/comments/:commentId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        ticket.comments.id(req.params.commentId).remove();
        return ticket.save();
      })
      .then(() => {
        res.status(204);
        next();
      })
      .catch(next);
  }
});

module.exports = router;

<commit_msg>Created pagination for tickets<commit_after>const express = require('express');
const basicAuth = require('basic-auth');
const models = require('../models');
const Errors = require('../lib/errors');

const Ticket = models.Ticket;
const Service = models.Service;
const router = express.Router({ mergeParams: true });

/**
 * @api {get} /tickets Get all tickets
 * @apiName GetTickets
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  (Querystring) {Number}  page=1    Current result set page
 * @apiParam  (Querystring) {Number}  limit=50  Maximum result set size
 *
 * @apiSuccess  {Object[]}  tickets
 * @apiSuccess  {String}    tickets.service
 * @apiSuccess  {Date}      tickets.created
 * @apiSuccess  {Date}      tickets.updated
 * @apiSuccess  {Date}      tickets.completed
 * @apiSuccess  {Object}    tickets.details
 * @apiSuccess  {Object[]}  tickets.comments
 * @apiSuccess  {String}    tickets.comments.author
 * @apiSuccess  {String}    tickets.comments.body
 * @apiSuccess  {Date}      tickets.comments.created
 * @apiSuccess  {Date}      tickets.comments.updated
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.get('/', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    const options = { where: {} };
    if (req.body.service_id) {
      options.where.service = req.body.service_id;
    }

    if (req.query.limit && req.query.limit < 250) {
      options.limit = +req.query.limit;
    } else {
      options.limit = 50;
    }

    if (req.query.page) {
      options.offset = +req.query.page - 1;
    } else {
      options.offset = 0;
    }

    Ticket.findAll(options)
      .then((tickets) => {
        res.body = tickets;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {get} /tickets/:ticketId Get specific ticket
 * @apiName GetTicketById
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam    {String}    ticketId
 *
 * @apiSuccess  {String}    service
 * @apiSuccess  {Date}      created
 * @apiSuccess  {Date}      updated
 * @apiSuccess  {Date}      completed
 * @apiSuccess  {Object}    details
 * @apiSuccess  {Object[]}  comments
 * @apiSuccess  {String}    comments.author
 * @apiSuccess  {String}    comments.body
 * @apiSuccess  {Date}      comments.created
 * @apiSuccess  {Date}      comments.updated
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.get('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        res.body = ticket;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets Create a ticket
 * @apiName PostTickets
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 * @apiDescription Registered services will post errors information to this resource. All
 * parameters passed into the request body will be recorded.
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission service
 */
router.post('/', (req, res, next) => {
  const credentials = basicAuth(req);
  if (!credentials || !credentials.name || !credentials.pass) {
    next(new Errors.Unauthorized());
  } else {
    Service.findById(credentials.name)
      .then(service => service.authorize(credentials.pass))
      .then(() => {
        const currentTime = new Date();
        const ticket = new Ticket({
          created: currentTime,
          updated: currentTime,
          service: credentials.name,
          details: req.body,
        });
        return ticket.save();
      })
      .then((ticket) => {
        res.body = ticket;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets/:ticketId Close a ticket
 * @apiName PostTicketsId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.post('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        ticket.completed = new Date();
        return ticket.save();
      })
      .then(() => {
        res.status(204);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {delete} /tickets/:ticketId Delete a ticket
 * @apiName DeleteTicketsId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.delete('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then(ticket => ticket.remove())
      .then(() => {
        res.status(204);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {get} /tickets/:ticketId/comments Get all ticket comments
 * @apiName GetTicketsIdComment
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiSuccess  {String}  _id     Comment ID
 * @apiSuccess  {String}  body    Comment contents
 * @apiSuccess  {Date}    created Creation time
 * @apiSuccess  {Date}    updated Update time
 * @apiSuccess  {String}  author  Comment's author
 *
 * @apiUse UnauthorizedError
 */
router.get('/:ticketId/comments', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .populate(['comments.author'])
      .then((ticket) => {
        res.body = ticket.comments;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets/:ticketId/comments Comment on a ticket
 * @apiName PostTicketsIdComment
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  message   Message contents for comment
 *
 * @apiSuccess  {String}  _id     Comment ID
 * @apiSuccess  {String}  body    Comment contents
 * @apiSuccess  {Date}    created Creation time
 * @apiSuccess  {Date}    updated Update time
 * @apiSuccess  {String}  author  Comment's author
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.post('/:ticketId/comments', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else if (!req.body.message) {
    next(new Errors.Generic('Message required', 400));
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        const comment = {
          author: req.session.user._id,
          body: req.body.message,
        };
        ticket.comments.push(comment);
        return ticket.save()
          .then(() => Promise.resolve(ticket.comments.pop()));
      })
      .then((comment) => {
        res.body = comment;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {get} /tickets/:ticketId/comments/:commentId Get a ticket comment
 * @apiName GetTicketsIdCommentId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  commentId ID of the comment
 *
 * @apiSuccess  {Object}  author          Author data
 * @apiSuccess  {String}  author._id      ID of author
 * @apiSuccess  {String}  author.name     Name of author
 * @apiSuccess  {String}  author.picture  Picture of author
 * @apiSuccess  {String}  _id             ID of comment
 * @apiSuccess  {String}  body            Body content of comment
 * @apiSuccess  {Date}    created         Comment creation date
 * @apiSuccess  {Date}    updated         Comment update date
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.get('/:ticketId/comments/:commentId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        res.body = ticket.comments.id(req.params.commentId);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {put} /tickets/:ticketId/comments/:commentId Update a ticket comment
 * @apiName PutTicketsIdCommentId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  commentId ID of the comment
 * @apiParam  {String}  message   Message contents for comment
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.put('/:ticketId/comments/:commentId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        const comment = ticket.comments.id(req.params.commentId);
        if (!comment || comment.author.toString() !== req.session.user._id) {
          return Promise.reject(new Errors.Unauthorized());
        }
        comment.body = req.body.message;
        comment.updated = new Date();
        return ticket.save();
      })
      .then((ticket) => {
        res.body = ticket.comments.id(req.params.commentId);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {delete} /tickets/:ticketId/comments/:commentId Delete a ticket comment
 * @apiName DeleteTicketsIdCommentId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  commentId ID of the comment
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.delete('/:ticketId/comments/:commentId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        ticket.comments.id(req.params.commentId).remove();
        return ticket.save();
      })
      .then(() => {
        res.status(204);
        next();
      })
      .catch(next);
  }
});

module.exports = router;

<|endoftext|>


<commit_before>var socket = io.connect(window.location.host);

var geocoder = new google.maps.Geocoder();

function makeMarker(coordinateArray, map, tweet){
  var marker = new google.maps.Marker({
    position: { lat: coordinateArray[0], lng: coordinateArray[1] },
    map: map
  })
//The following code was written by "Engineer", from StackOverflow. In June 19th 2012,
//"Engineer" decided to make a post to help people like me use Google Map API to help
//us display multiple infoboxes. Thank you Engineer!
  var content = tweet;

  var infowindow = new google.maps.InfoWindow()

  google.maps.event.addListener(marker,'click', (function(marker,content,infowindow){
          return function() {
             infowindow.setContent(content);
             infowindow.open(map,marker);
          };
      })(marker,content,infowindow));
};

var geocoding = function(address, map, tweet) {
    geocoder.geocode({"address": address}, function(results, status){
      if (status == google.maps.GeocoderStatus.OK){
        var location = results[0].geometry.location
        var lat = location.G
        var lng = location.K
        makeMarker([lat, lng], map, tweet);
      }
      else {
        // console.log("Geocode was not successful for the following reason: "+ status)
      }
    })
  };

$(document).ready(function(){
    $("#textarea1").focus();
    // socket.emit('reset')
    var mapOptions = {
      center: { lat: 20, lng: 0},
      zoom: 3
    };

    var map = new google.maps.Map(document.getElementById('map-canvas'),
        mapOptions);

    socket.on('tweet', function(data){
      makeMarker(data.coordinates, map, data.tweet);
    });

    $("#search-form").on('submit', function(event){
      event.preventDefault();
      var searchWord = $('#textarea1').val();
      socket.emit('search', { word: searchWord });
      $('#textarea1').val('');
    })

    // socket.emit('search', {word: 'cat'})

    socket.on('geocoder', function(data){
      var address = data.location
      if (address === "")
      {
        // console.log("Blank string")
      }
      else {
      setTimeout(geocoding(address, map, data.tweet), 500);
      };
    });

    socket.on('openModal', function(){
        $('#modal1').openModal();
    });

})

<commit_msg>add map styling<commit_after>var socket = io.connect(window.location.host);

var geocoder = new google.maps.Geocoder();

function makeMarker(coordinateArray, map, tweet){
  var marker = new google.maps.Marker({
    position: { lat: coordinateArray[0], lng: coordinateArray[1] },
    map: map,
    animation: google.maps.Animation.DROP
  })
//The following code was written by "Engineer", from StackOverflow. In June 19th 2012,
//"Engineer" decided to make a post to help people like me use Google Map API to help
//us display multiple infoboxes. Thank you Engineer!
  var content = tweet;

  var infowindow = new google.maps.InfoWindow()

  google.maps.event.addListener(marker,'click', (function(marker,content,infowindow){
          return function() {
             infowindow.setContent(content);
             infowindow.open(map,marker);
          };
      })(marker,content,infowindow));
};

var geocoding = function(address, map, tweet) {
    geocoder.geocode({"address": address}, function(results, status){
      if (status == google.maps.GeocoderStatus.OK){
        var location = results[0].geometry.location
        var lat = location.G
        var lng = location.K
        makeMarker([lat, lng], map, tweet);
      }
      else {
        // console.log("Geocode was not successful for the following reason: "+ status)
      }
    })
  };

var styles = [
  {
    "featureType": "landscape",
    "stylers": [
      { "saturation": -100 },
      { "visibility": "on" },
      { "lightness": 4 },
      { "gamma": 1.31 }
    ]
  },{
    "featureType": "water",
    "elementType": "geometry",
    "stylers": [
      { "saturation": -100 },
      { "visibility": "on" },
      { "gamma": 0.12 }
    ]
  },{
    "featureType": "administrative.country",
    "elementType": "geometry.stroke",
    "stylers": [
      { "visibility": "on" },
      { "saturation": -100 },
      { "lightness": 100 }
    ]
  },{
    "featureType": "administrative.country",
    "elementType": "labels",
    "stylers": [
      { "color": "#858080" },
      { "saturation": -100 },
      { "weight": 0.2 },
      { "visibility": "simplified" },
      { "lightness": -9 }
    ]
  },{
    "featureType": "administrative.province",
    "stylers": [
      { "weight": 0.1 },
      { "lightness": 67 },
      { "visibility": "off" }
    ]
  },{
    "featureType": "water",
    "elementType": "labels",
    "stylers": [
      { "saturation": -100 },
      { "lightness": 39 },
      { "visibility": "on" },
      { "weight": 0.9 }
    ]
  },{
  }
]

$(document).ready(function(){
    $("#textarea1").focus();
    // socket.emit('reset')
    var mapOptions = {
      center: { lat: 20, lng: 0},
      zoom: 3
    };

    var map = new google.maps.Map(document.getElementById('map-canvas'),
        mapOptions);
    map.setOptions({styles: styles});

    socket.on('tweet', function(data){
      makeMarker(data.coordinates, map, data.tweet);
    });

    $("#search-form").on('submit', function(event){
      event.preventDefault();
      var searchWord = $('#textarea1').val();
      socket.emit('search', { word: searchWord });
      $('#textarea1').val('');
    })

    // socket.emit('search', {word: 'cat'})

    socket.on('geocoder', function(data){
      var address = data.location
      if (address === "")
      {
        // console.log("Blank string")
      }
      else {
      setTimeout(geocoding(address, map, data.tweet), 500);
      };
    });

    socket.on('openModal', function(){
        $('#modal1').openModal();
    });

})

<|endoftext|>


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

/* <video id="video2" autoplay width="640px" height="480px"
						poster="img/webrtc.png"></video>
*/

var ws = new WebSocket('wss://' + location.host + '/call');
var mainVideo;
var otherVideos;
var otherVideosList;
var mainWebRtcPeer;
var otherWebRtcPeers;
var newVideoId;

window.onload = function() {
	console = new Console();
	mainVideo = document.getElementById('mainVideo');
	otherVideos = document.getElementById('student-cams');
	otherVideosList = [];
	otherWebRtcPeers = [];
	newVideoId = 0;

	disableStopButton();
	disableRecordButton();
}

window.onbeforeunload = function() {
	ws.close();
}

ws.onmessage = function(message) {
	var parsedMessage = JSON.parse(message.data);
	console.info('Received message: ' + message.data);

	switch (parsedMessage.id) {
		case 'addTeacherResponse':
			addTeacherResponse(parsedMessage);
			break;
		case 'addStudentToTeacher':
			addStudentToTeacher(parsedMessage);
			break;
		case 'removeStudentToTeacher':
			removeStudentToTeacher(parsedMessage);
			break;
		case 'addStudentResponse':
			addStudentResponse(parsedMessage);
			break;
		case 'removeStudentResponse':
			removeStudentResponse(parsedMessage);
			break;
		case 'iceCandidate':
			mainWebRtcPeer.addIceCandidate(parsedMessage.candidate, function(error) {
				if (error)
					return console.error('Error adding candidate: ' + error);
			});
			break;
		case 'stopCommunication':
			dispose();
			break;
		default:
			console.error('Unrecognized message', parsedMessage);
	}
}

function addTeacher() {
	if (!mainWebRtcPeer) {
		showSpinner(mainVideo);

		var options = {
			localVideo : mainVideo,
			onicecandidate : onIceCandidate
		}
		mainWebRtcPeer = new kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options,
				function(error) {
					if (error) {
						return console.error(error);
					}
					mainWebRtcPeer.generateOffer(onOfferTeacher);
				});

		enableStopButton();
		//enableButton('#record', 'record()');
	}
}

function onOfferTeacher(error, offerSdp) {
	if (error)
		return console.error('Error generating the offer');
	console.info('Invoking SDP offer callback function ' + location.host);
	var message = {
		id : 'addTeacher',
		sdpOffer : offerSdp
	}
	sendMessage(message);
}

function addTeacherResponse(message) {
	if (message.response != 'accepted') {
		var errorMsg = message.message ? message.message : 'Unknow error';
		console.info('Call not accepted for the following reason: ' + errorMsg);
		dispose();
	} else {
		mainWebRtcPeer.processAnswer(message.sdpAnswer, function(error) {
			if (error)
				return console.error(error);
		});
	}
}

function addStudentToTeacher(parsedMessage) {
	// add student id to list with corresponding video id
	// add video to videos list
}

function removeStudentToTeacher(parsedMessage) {
	// remove student id to list
	// remove video to videos list
}

function addStudent() {
	if (!mainWebRtcPeer) {
		showSpinner(mainVideo);
		addVideoPlayer(newVideoId);
		var tempPlayer = document.getElementById("cam-"+newVideoId);
		showSpinner(tempPlayer)
		var options = {
			remoteVideo : mainVideo,
			localVideo: tempPlayer,
			onicecandidate : onIceCandidate
		}
		mainWebRtcPeer = new kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options,
				function(error) {
					if (error) {
						return console.error(error);
					}
					this.generateOffer(onOfferStudent);
				});
		enableStopButton();

		newVideoId = newVideoId + 1;
	}
}

function onOfferStudent(error, offerSdp) {
	if (error)
		return console.error('Error generating the offer');
	console.info('Invoking SDP offer callback function ' + location.host);
	var message = {
		id : 'addStudent',
		sdpOffer : offerSdp
	}
	sendMessage(message);
}

function addStudentResponse(message) {
	if (message.response != 'accepted') {
		var errorMsg = message.message ? message.message : 'Unknow error';
		console.info('Call not accepted for the following reason: ' + errorMsg);
		dispose();
	} else {
		mainWebRtcPeer.processAnswer(message.sdpAnswer, function(error) {
			if (error)
				return console.error(error);
		});
	}
}

function removeStudentResponse(parsedMessage) {
	// purge students list
	// remove own video and display "disconnected in teacher video"
}

function onIceCandidate(candidate) {
	console.log("Local candidate" + JSON.stringify(candidate));

	var message = {
		id : 'onIceCandidate',
		candidate : candidate
	};
	sendMessage(message);
}

function stop() {
	var message = {
		id : 'stop'
	}
	sendMessage(message);
	dispose();
}

function dispose() {
	if (mainWebRtcPeer) {
		mainWebRtcPeer.dispose();
		mainWwebRtcPeer = null;
	}
	hideSpinner(mainVideo);

	disableStopButton();
}

function disableStopButton() {
	enableButton('#teacher', 'addTeacher()');
	enableButton('#student', 'addStudent()');
	disableButton('#stop');
}

function disableRecordButton() {
	disableButton('#record');
}

function enableStopButton() {
	disableButton('#teacher');
	disableButton('#student');
	enableButton('#stop', 'stop()');
}

function disableButton(id) {
	$(id).attr('disabled', true);
	$(id).removeAttr('onclick');
}

function enableButton(id, functionName) {
	$(id).attr('disabled', false);
	$(id).attr('onclick', functionName);
}

function addVideoPlayer(playerID) {
	console.log(otherVideos);
	var li = document.createElement("LI");
	li.innerHTML = "<video id='cam-" + playerID + "' autoplay width='640px'" +
		"height='480px' poster='img/webrtc.png'></video>"

	otherVideos.appendChild(li);

}

function removeVideoPlayer(playerID) {
	// remove player from student players list by playerID
}

function sendMessage(message) {
	var jsonMessage = JSON.stringify(message);
	console.log('Senging message: ' + jsonMessage);
	ws.send(jsonMessage);
}

function showSpinner() {
	for (var i = 0; i < arguments.length; i++) {
		arguments[i].poster = './img/transparent-1px.png';
		arguments[i].style.background = 'center transparent url("./img/spinner.gif") no-repeat';
	}
}

function hideSpinner() {
	for (var i = 0; i < arguments.length; i++) {
		arguments[i].src = '';
		arguments[i].poster = './img/webrtc.png';
		arguments[i].style.background = '';
	}
}

/**
 * Lightbox utility (to display media pipeline image in a modal dialog)
 */
$(document).delegate('*[data-toggle="lightbox"]', 'click', function(event) {
	event.preventDefault();
	$(this).ekkoLightbox();
});
<commit_msg>teacher response to student being added<commit_after>/*
 * (C) Copyright 2014 Kurento (http://kurento.org/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

/* <video id="video2" autoplay width="640px" height="480px"
						poster="img/webrtc.png"></video>
*/

var ws = new WebSocket('wss://' + location.host + '/call');
var mainVideo;
var otherVideos;
var otherVideosList;
var mainWebRtcPeer;
var otherWebRtcPeers;
var newVideoId;

window.onload = function() {
	console = new Console();
	mainVideo = document.getElementById('mainVideo');
	otherVideos = document.getElementById('student-cams');
	otherVideosList = [];
	otherWebRtcPeers = [];
	newVideoId = 0;

	disableStopButton();
	disableRecordButton();
}

window.onbeforeunload = function() {
	ws.close();
}

ws.onmessage = function(message) {
	var parsedMessage = JSON.parse(message.data);
	console.info('Received message: ' + message.data);

	switch (parsedMessage.id) {
		case 'addTeacherResponse':
			addTeacherResponse(parsedMessage);
			break;
		case 'addStudentToTeacher':
			addStudentToTeacher(parsedMessage);
			break;
		case 'removeStudentToTeacher':
			removeStudentToTeacher(parsedMessage);
			break;
		case 'addStudentResponse':
			addStudentResponse(parsedMessage);
			break;
		case 'removeStudentResponse':
			removeStudentResponse(parsedMessage);
			break;
		case 'iceCandidate':
			mainWebRtcPeer.addIceCandidate(parsedMessage.candidate, function(error) {
				if (error)
					return console.error('Error adding candidate: ' + error);
			});
			break;
		case 'stopCommunication':
			dispose();
			break;
		default:
			console.error('Unrecognized message', parsedMessage);
	}
}

function addTeacher() {
	if (!mainWebRtcPeer) {
		showSpinner(mainVideo);

		var options = {
			localVideo : mainVideo,
			onicecandidate : onIceCandidate
		}
		mainWebRtcPeer = new kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options,
				function(error) {
					if (error) {
						return console.error(error);
					}
					mainWebRtcPeer.generateOffer(onOfferTeacher);
				});

		enableStopButton();
		//enableButton('#record', 'record()');
	}
}

function onOfferTeacher(error, offerSdp) {
	if (error)
		return console.error('Error generating the offer');
	console.info('Invoking SDP offer callback function ' + location.host);
	var message = {
		id : 'addTeacher',
		sdpOffer : offerSdp
	}
	sendMessage(message);
}

function addTeacherResponse(message) {
	if (message.response != 'accepted') {
		var errorMsg = message.message ? message.message : 'Unknow error';
		console.info('Call not accepted for the following reason: ' + errorMsg);
		dispose();
	} else {
		mainWebRtcPeer.processAnswer(message.sdpAnswer, function(error) {
			if (error)
				return console.error(error);
		});
	}
}

function addStudentToTeacher(parsedMessage) {
	console,log(parsedMessage);
	// add student id to list with corresponding video id
	// add video to videos list
}

function removeStudentToTeacher(parsedMessage) {
	// remove student id to list
	// remove video to videos list
}

function addStudent() {
	if (!mainWebRtcPeer) {
		showSpinner(mainVideo);
		addVideoPlayer(newVideoId);
		var tempPlayer = document.getElementById("cam-"+newVideoId);
		showSpinner(tempPlayer)
		var options = {
			remoteVideo : mainVideo,
			localVideo: tempPlayer,
			onicecandidate : onIceCandidate
		}
		mainWebRtcPeer = new kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options,
				function(error) {
					if (error) {
						return console.error(error);
					}
					this.generateOffer(onOfferStudent);
				});
		enableStopButton();

		newVideoId = newVideoId + 1;
	}
}

function onOfferStudent(error, offerSdp) {
	if (error)
		return console.error('Error generating the offer');
	console.info('Invoking SDP offer callback function ' + location.host);
	var message = {
		id : 'addStudent',
		sdpOffer : offerSdp
	}
	sendMessage(message);
}

function addStudentResponse(message) {
	if (message.response != 'accepted') {
		var errorMsg = message.message ? message.message : 'Unknow error';
		console.info('Call not accepted for the following reason: ' + errorMsg);
		dispose();
	} else {
		mainWebRtcPeer.processAnswer(message.sdpAnswer, function(error) {
			if (error)
				return console.error(error);
		});
	}
}

function removeStudentResponse(parsedMessage) {
	// purge students list
	// remove own video and display "disconnected in teacher video"
}

function onIceCandidate(candidate) {
	console.log("Local candidate" + JSON.stringify(candidate));

	var message = {
		id : 'onIceCandidate',
		candidate : candidate
	};
	sendMessage(message);
}

function stop() {
	var message = {
		id : 'stop'
	}
	sendMessage(message);
	dispose();
}

function dispose() {
	if (mainWebRtcPeer) {
		mainWebRtcPeer.dispose();
		mainWwebRtcPeer = null;
	}
	hideSpinner(mainVideo);

	disableStopButton();
}

function disableStopButton() {
	enableButton('#teacher', 'addTeacher()');
	enableButton('#student', 'addStudent()');
	disableButton('#stop');
}

function disableRecordButton() {
	disableButton('#record');
}

function enableStopButton() {
	disableButton('#teacher');
	disableButton('#student');
	enableButton('#stop', 'stop()');
}

function disableButton(id) {
	$(id).attr('disabled', true);
	$(id).removeAttr('onclick');
}

function enableButton(id, functionName) {
	$(id).attr('disabled', false);
	$(id).attr('onclick', functionName);
}

function addVideoPlayer(playerID) {
	console.log(otherVideos);
	var li = document.createElement("LI");
	li.innerHTML = "<video id='cam-" + playerID + "' autoplay width='640px'" +
		"height='480px' poster='img/webrtc.png'></video>"

	otherVideos.appendChild(li);

}

function removeVideoPlayer(playerID) {
	// remove player from student players list by playerID
}

function sendMessage(message) {
	var jsonMessage = JSON.stringify(message);
	console.log('Senging message: ' + jsonMessage);
	ws.send(jsonMessage);
}

function showSpinner() {
	for (var i = 0; i < arguments.length; i++) {
		arguments[i].poster = './img/transparent-1px.png';
		arguments[i].style.background = 'center transparent url("./img/spinner.gif") no-repeat';
	}
}

function hideSpinner() {
	for (var i = 0; i < arguments.length; i++) {
		arguments[i].src = '';
		arguments[i].poster = './img/webrtc.png';
		arguments[i].style.background = '';
	}
}

/**
 * Lightbox utility (to display media pipeline image in a modal dialog)
 */
$(document).delegate('*[data-toggle="lightbox"]', 'click', function(event) {
	event.preventDefault();
	$(this).ekkoLightbox();
});
<|endoftext|>


<commit_before>'use strict'

/**
 * @typedef {Object} Parsed
 * @property {string} type
 * @property {string} raw - raw source string
 */

/**
 * @typedef {Parsed} ParsedObject
 * @property {Array<ParsedKeyValue>} properties
 * @property {number} cursor - property index the cursor is at
 */

/**
 * @typedef {Parsed} ParsedKeyValue
 * @property {string} key
 * @property {Parsed} value
 * @property {number} keyCursor - cursor position in key text
 */

/**
 * @typedef {Parsed} ParsedArray
 * @property {Array<Parsed>} values
 * @property {number} cursor - value index the cursor is at
 */

/**
 * @typedef {Parsed} ParsedSource
 * @property {number} cursor - cursor position in raw text
 */

/**
 * @param {string} str
 * @param {number} cursor
 * @returns {Parsed}
 */
module.exports = function (str, cursor) {
	return readValue({
		type: 'source',
		raw: str,
		cursor: cursor
	})
}

/**
 * Extract a JS expression from the beginning of the string.
 * It uses delimiter couting to know where the expression ends.
 * It is not an error to leave delimiter unclosed, like in "{a: 2".
 * Unmatched closing delimiters are ignore, like ')' in "{a: )}"
 * @param {ParsedSource} source - will be modified
 * @returns {Parsed}
 */
function readValue(source) {
	// Stack of open delimiters: {, [, (, `, ', "
	let stack = [],
		// Last stack level
		mode = 'root',
		// Whether this is may be a pure object or array
		seemsPure = true,
		// First delimiter
		first = '',
		i

	for (i = 0; i < source.raw.length; i++) {
		let c = source.raw[i]

		if ((mode === '{' && c === '}') ||
			(mode === '[' && c === ']') ||
			(mode === '(' && c === ')') ||
			(mode === '`' && c === '`') ||
			(mode === '\'' && c === '\'') ||
			(mode === '"' && c === '"')) {
			// Close delimiter
			mode = stack.pop()
		} else if (c === '{' || c === '[' || c === '(' ||
			c === '`' || c === '\'' || c === '"') {
			// Open delimiter
			stack.push(mode)
			mode = c

			if (!first) {
				first = c
			}
		} else if ((mode === '`' || mode === '\'' || mode === '"') && c === '\\') {
			// Escape next char
			i += 1
		} else if (mode === 'root' && c === ',') {
			// End of expression
			break
		} else if (seemsPure && mode === 'root' && !/^\s$/.test(c)) {
			// Pure objects/arrays can't have any non-whitespace char at root
			seemsPure = false
		}
	}

	// Slice source
	let valueSource = {
		type: 'source',
		raw: source.raw.slice(0, i + 1),
		cursor: sliceCursor(source.cursor, 0, i + 1)
	}
	source.cursor = sliceCursor(source.cursor, i + 1, source.raw.length)
	source.raw = source.raw.slice(i + 1)

	// Promote pure types
	if (seemsPure && first === '{') {
		return promoteObject(valueSource, mode === 'root')
	} else if (seemsPure && first === '[') {
		return promoteArray(valueSource, mode === 'root')
	}

	// Fallback to raw source
	return valueSource
}

/**
 * Parse an object source
 * @param {ParsedSource} source
 * @param {boolean} gentleEnd - whether the object body was ended correctly
 * @returns {ParsedObject}
 */
function promoteObject(source, gentleEnd) {
	// Extract object body string
	// The raw string is garanteed to start with spaces and a '{'
	// Depending on gentleEnd it may end with or without '}' spaces and ','
	let match = source.raw.match(gentleEnd ? /^(\s*\{)(.*)\}\s*,?$/ : /^(\s*\{)(.*)$/),
		before = match[1],
		body = match[2],
		subSource = {
			type: 'source',
			raw: body,
			cursor: sliceCursor(source.cursor, before.length, before.length + body.length)
		}

	// Read properties
	let properties = [],
		cursor = -1,
		key
	while (/\S/.test(subSource.raw)) {
		let hadCursor = subSource.cursor !== -1
		key = readKey(subSource)
		properties.push({
			key: key.name,
			value: readValue(subSource),
			keyCursor: key.cursor
		})
		if (hadCursor && subSource.cursor === -1) {
			// Cursor is trapped in the property
			cursor = properties.length - 1
		}
	}

	if (subSource.cursor !== -1) {
		// Cursor at the end
		cursor = properties.length
	}

	return {
		type: 'object',
		raw: source.raw,
		properties: properties,
		cursor: cursor
	}
}

/**
 * Parse an array source
 * @param {ParsedSource} source
 * @param {boolean} gentleEnd - whether the array body was ended correctly
 * @returns {ParsedArray}
 */
function promoteArray(source, gentleEnd) {
	// Extract array body string
	// The raw string is garanteed to start with spaces and a '['
	// Depending on gentleEnd it may end with or without ']' spaces and ','
	let match = source.raw.match(gentleEnd ? /^(\s*\[)(.*)\]\s*,?$/ : /^(\s*\[)(.*)$/),
		before = match[1],
		body = match[2],
		subSource = {
			type: 'source',
			raw: body,
			cursor: sliceCursor(source.cursor, before.length, before.length + body.length)
		}

	// Read values
	let values = [],
		cursor = -1
	while (/\S/.test(subSource.raw)) {
		let hadCursor = subSource.cursor !== -1
		values.push(readValue(subSource))
		if (hadCursor && subSource.cursor === -1) {
			// Cursor is trapped in the property
			cursor = values.length - 1
		}
	}

	if (subSource.cursor !== -1) {
		// Cursor at the end
		cursor = values.length
	}

	return {
		type: 'array',
		raw: source.raw,
		values: values,
		cursor: cursor
	}
}

/**
 * Extract an object key from the beginning of the string.
 * It is not an error to not use quotes when needed, like: "a.b: 2".
 * Invalid characters between close quotes and ':' are ignore, like 'x' in '"a"x: 2'
 * @param {ParsedSource} source - will be modified
 * @returns {?{name: string, cursor: number}}
 */
function readKey(source) {
	let mode = 'start',
		// First char in the key
		startIndex = -1,
		// First char not in the key
		endIndex = -1,
		i

	for (i = 0; i < source.raw.length; i++) {
		let c = source.raw[i]

		if (mode === 'start') {
			if (/^\s$/.test(c)) {
				// Ignore leading spaces
			} else if (c === '\'' || c === '"') {
				// Quoted key
				mode = c
				startIndex = i + 1
			} else {
				// Unquoted
				mode = 'bare'
				startIndex = i
			}
		} else if (mode === '\'' || mode === '"') {
			if (c === '\\') {
				// Escape next char
				i += 1
			} else if (c === mode) {
				// End of key
				mode = 'end'
				endIndex = i
			}
		} else if (mode === 'bare') {
			if (c === ':') {
				// End unquoted key
				endIndex = i
				break
			}
		} else if (mode === 'end') {
			if (c === ':') {
				// End quoted key
				break
			}
		}
	}

	if (startIndex === -1) {
		// Not found
		return null
	}

	if (endIndex === -1) {
		// Assume the whole text is part of the key
		endIndex = source.raw.length
	}

	let key = {
		name: source.raw.slice(startIndex, endIndex),
		cursor: sliceCursor(source.cursor, startIndex, endIndex)
	}
	source.cursor = sliceCursor(source.cursor, i + 1, source.raw.length)
	source.raw = source.raw.slice(i + 1)
	return key
}

/**
 * Compute the new position for the cursor after a slice
 * @param {number} cursor
 * @param {number} start
 * @param {number} end
 */
function sliceCursor(cursor, start, end) {
	if (cursor === -1) {
		// Already out of bounds
		return -1
	} else if (cursor >= start && cursor <= end) {
		// Moved
		return cursor - start
	} else {
		// Will be out of bounds
		return -1
	}
}<commit_msg>Leveled up parser<commit_after>'use strict'

/**
 * @typedef {Object} Parsed
 * @property {string} type
 * @property {string} raw - raw source string
 */

/**
 * @typedef {Parsed} ParsedObject
 * @property {Array<ParsedKeyValue>} properties
 * @property {number} cursor - property index the cursor is at
 */

/**
 * @typedef {Parsed} ParsedKeyValue
 * @property {string} key
 * @property {Parsed} value
 * @property {number} keyCursor - cursor position in key text
 */

/**
 * @typedef {Parsed} ParsedArray
 * @property {Array<Parsed>} values
 * @property {number} cursor - value index the cursor is at
 */

/**
 * @typedef {Parsed} ParsedSource
 * @property {number} cursor - cursor position in raw text
 */

/**
 * @param {string} str
 * @param {number} cursor
 * @returns {Parsed}
 */
module.exports = function (str, cursor) {
	return readValue({
		type: 'source',
		raw: str,
		cursor: cursor
	})
}

/**
 * Extract a JS expression from the beginning of the string.
 * It uses delimiter couting to know where the expression ends.
 * It is not an error to leave delimiter unclosed, like in "{a: 2".
 * Unmatched closing delimiters are ignored, like ')' in "{a: )}"
 * @param {ParsedSource} source - will be modified
 * @param {boolean} [useStopChars=false] - whether to stop parsing on a stop-char (like '}')
 * @returns {Parsed}
 */
function readValue(source, useStopChars) {
	// Stack of open delimiters: {, [, (, ', "
	let stack = [],
		// Last stack level
		mode = 'root',
		// Whether this is may be a pure object or array
		seemsPure = true,
		// First delimiter
		first = '',
		i

	for (i = 0; i < source.raw.length; i++) {
		let c = source.raw[i]

		if ((mode === '{' && c === '}') ||
			(mode === '[' && c === ']') ||
			(mode === '(' && c === ')') ||
			(mode === '\'' && c === '\'') ||
			(mode === '"' && c === '"')) {
			// Close delimiter
			mode = stack.pop()
		} else if (useStopChars && i >= source.cursor && mode !== 'root' &&
			(c === '}' || c === ']' || c === ')')) {
			// Unmatched close delimiter after cursor
			// Assume it should close some corresponding open delimiter
			let targetMode = c === '}' ? '{' : (c === ']' ? '[' : ')')
			do {
				mode = stack.pop()
			} while (mode !== 'root' && mode !== targetMode)

			if (mode !== 'root') {
				// Pop the c level
				mode = stack.pop()
			}
		} else if (mode === '\'' || mode === '"') {
			if (c === '\\') {
				// Escape next char
				i += 1
			}
		} else if (c === '{' || c === '[' || c === '(' ||
			c === '\'' || c === '"') {
			// Open delimiter
			stack.push(mode)
			mode = c

			if (!first) {
				first = c
			}
		} else if (mode === 'root' && c === ',') {
			// End of expression
			break
		} else if (seemsPure && mode === 'root' && !/^\s$/.test(c)) {
			// Pure objects/arrays can't have any non-whitespace char at root
			seemsPure = false
		}
	}

	if (!useStopChars && mode !== 'root' && source.cursor !== -1) {
		// Not properly closed, like in:
		// {a: ['|], b: 2}
		// We'll assume the any unmatched }, ], ), ', " marks the end
		return readValue(source, true)
	}

	// Slice source
	let valueSource = {
		type: 'source',
		raw: source.raw.slice(0, i + 1),
		cursor: sliceCursor(source.cursor, 0, i + 1)
	}
	source.cursor = sliceCursor(source.cursor, i + 1, source.raw.length)
	source.raw = source.raw.slice(i + 1)

	// Promote pure types
	if (seemsPure && first === '{') {
		return promoteObject(valueSource, mode === 'root')
	} else if (seemsPure && first === '[') {
		return promoteArray(valueSource, mode === 'root')
	}

	// Fallback to raw source
	return valueSource
}

/**
 * Parse an object source
 * @param {ParsedSource} source
 * @param {boolean} gentleEnd - whether the object body was ended correctly
 * @returns {ParsedObject}
 */
function promoteObject(source, gentleEnd) {
	// Extract object body string
	// The raw string is garanteed to start with spaces and a '{'
	// Depending on gentleEnd it may end with or without '}' spaces and ','
	let match = source.raw.match(gentleEnd ? /^(\s*\{)(.*)\}\s*,?$/ : /^(\s*\{)(.*)$/),
		before = match[1],
		body = match[2],
		subSource = {
			type: 'source',
			raw: body,
			cursor: sliceCursor(source.cursor, before.length, before.length + body.length)
		}

	// Read properties
	let properties = [],
		cursor = -1,
		key
	while (/\S/.test(subSource.raw)) {
		let hadCursor = subSource.cursor !== -1
		key = readKey(subSource)
		properties.push({
			key: key.name,
			value: readValue(subSource),
			keyCursor: key.cursor
		})
		if (hadCursor && subSource.cursor === -1) {
			// Cursor is trapped in the property
			cursor = properties.length - 1
		}
	}

	if (subSource.cursor !== -1) {
		// Cursor at the end
		cursor = properties.length
	}

	return {
		type: 'object',
		raw: source.raw,
		properties: properties,
		cursor: cursor
	}
}

/**
 * Parse an array source
 * @param {ParsedSource} source
 * @param {boolean} gentleEnd - whether the array body was ended correctly
 * @returns {ParsedArray}
 */
function promoteArray(source, gentleEnd) {
	// Extract array body string
	// The raw string is garanteed to start with spaces and a '['
	// Depending on gentleEnd it may end with or without ']' spaces and ','
	let match = source.raw.match(gentleEnd ? /^(\s*\[)(.*)\]\s*,?$/ : /^(\s*\[)(.*)$/),
		before = match[1],
		body = match[2],
		subSource = {
			type: 'source',
			raw: body,
			cursor: sliceCursor(source.cursor, before.length, before.length + body.length)
		}

	// Read values
	let values = [],
		cursor = -1
	while (/\S/.test(subSource.raw)) {
		let hadCursor = subSource.cursor !== -1
		values.push(readValue(subSource))
		if (hadCursor && subSource.cursor === -1) {
			// Cursor is trapped in the property
			cursor = values.length - 1
		}
	}

	if (subSource.cursor !== -1) {
		// Cursor at the end
		cursor = values.length
	}

	return {
		type: 'array',
		raw: source.raw,
		values: values,
		cursor: cursor
	}
}

/**
 * Extract an object key from the beginning of the string.
 * It is not an error to not use quotes when needed, like: "a.b: 2".
 * Invalid characters between close quotes and ':' are ignore, like 'x' in '"a"x: 2'
 * @param {ParsedSource} source - will be modified
 * @returns {?{name: string, cursor: number}}
 */
function readKey(source) {
	let mode = 'start',
		// First char in the key
		startIndex = -1,
		// First char not in the key
		endIndex = -1,
		i

	for (i = 0; i < source.raw.length; i++) {
		let c = source.raw[i]

		if (mode === 'start') {
			if (/^\s$/.test(c)) {
				// Ignore leading spaces
			} else if (c === '\'' || c === '"') {
				// Quoted key
				mode = c
				startIndex = i + 1
			} else {
				// Unquoted
				mode = 'bare'
				startIndex = i
			}
		} else if (mode === '\'' || mode === '"') {
			if (c === '\\') {
				// Escape next char
				i += 1
			} else if (c === mode) {
				// End of key
				mode = 'end'
				endIndex = i
			}
		} else if (mode === 'bare') {
			if (c === ':') {
				// End unquoted key
				endIndex = i
				break
			}
		} else if (mode === 'end') {
			if (c === ':') {
				// End quoted key
				break
			}
		}
	}

	if (startIndex === -1) {
		// Not found
		return null
	}

	if (endIndex === -1) {
		// Assume the whole text is part of the key
		endIndex = source.raw.length
	}

	let key = {
		name: source.raw.slice(startIndex, endIndex),
		cursor: sliceCursor(source.cursor, startIndex, endIndex)
	}
	source.cursor = sliceCursor(source.cursor, i + 1, source.raw.length)
	source.raw = source.raw.slice(i + 1)
	return key
}

/**
 * Compute the new position for the cursor after a slice
 * @param {number} cursor
 * @param {number} start
 * @param {number} end
 */
function sliceCursor(cursor, start, end) {
	if (cursor === -1) {
		// Already out of bounds
		return -1
	} else if (cursor >= start && cursor <= end) {
		// Moved
		return cursor - start
	} else {
		// Will be out of bounds
		return -1
	}
}<|endoftext|>