<commit_before>'use strict';
var token = require( './token' );
var lex = require( './lex' );
var expr = require( './expr' );

/*
    Transform a list of lexer tokens into a expr-symbols.
*/

const T_NOT = 'not';
const T_OR  = 'or';

module.exports = function parse ( input ) {

    // try to act as passthrough for lex()
    if ( input instanceof String || typeof input === 'string' )
        input = lex( input );
    if ( !( input instanceof Array ) && typeof input !== 'array' )
        throw new Error( "parse() accepts string or array-of-tokens" );

    // strip out whitespace and quotes
    input = input.filter( ( tk ) => {
        return !(
            tk instanceof token.wts ||
            tk instanceof token.quo
        );
    } );

    // promote all lexer 'trm's to expr 'terms'
    // convert 'OR's and 'NOT's from terms to negators
    input = input.map( ( tk ) => {
        if ( !( tk instanceof token.trm ) )
            return tk;
        else if ( tk.getText().toLowerCase() === T_NOT )
            return new token.neg( tk.getSite() );
        else if ( tk.getText().toLowerCase() === T_OR  )
            return new token.oro( tk.getSite() );
        else
            return new expr.term( tk );
    } );

    // combine tag pairs
    var idx_tag;
    while ( ( idx_tag = input.findIndex( ( tk ) => {
        return tk instanceof token.tag;
    } ) ) !== -1 ) {
        if ( idx_tag === 0 || idx_tag === input.length - 1 )
            throw input[ idx_tag ].error( "Unpaired tag delimiter" );
        if ( !( input[ idx_tag - 1 ] instanceof expr.term ) )
            throw input[ idx_tag - 1 ].error( "Tag attribute not given as term" );
        if ( !( input[ idx_tag + 1 ] instanceof expr.term ) )
            throw input[ idx_tag + 1 ].error( "Tag value not given as term" );

        let triplet = input.splice( idx_tag - 1, 3 );
        let tag = new expr.tagp([
            triplet[ 0 ].getTokens()[ 0 ],
            triplet[ 1 ],
            triplet[ 2 ].getTokens()[ 0 ]
        ]);
        if ( triplet[ 0 ].isNegated() )
            tag = tag.negate();
        input.splice( idx_tag - 1, 0, tag );
    }

    // fold parenthetical groups, recursive descent
    function ps_expr ( input, expectRpr ) {
        let output = [];
        while ( input.length ) {
            let curr = input.shift();
            if ( curr instanceof token.lpr ) {
                let subexpr;
                try {
                    subexpr = parse( ps_expr( input, true ) );
                } catch ( ex ) {
                    throw curr.error( ex.message );
                }
                output.push( subexpr );
            } else if ( curr instanceof token.rpr ) {
                if ( !expectRpr )
                    throw curr.error( "Unbalanced paren" );
                return output;
            } else {
                output.push( curr );
            }
        }
        if ( expectRpr )
            throw new Error( "Unbalanced paren" );
        return output;
    }
    input = ps_expr( input );

    // apply negation
    var idx_neg;
    while ( ( idx_neg = input.findIndex( ( tk ) => {
        return tk instanceof token.neg;
    } ) ) !== -1 ) {
        if ( idx_neg === input.length - 1 )
            throw input[ idx_neg ].error( "Stray negator" );
        input.splice( idx_neg, 2, input[ idx_neg + 1 ].negate() );
    }

    // combine OR'ed expressions
    var idx_oro;
    while ( ( idx_oro = input.findIndex( ( tk ) => {
        return tk instanceof token.oro;
    } ) ) !== -1 ) {
        if ( idx_oro === 0 || idx_oro === input.length - 1 )
            throw input[ idx_oro ].error( "Unpaired OR operator" );
        let triplet = input.splice( idx_oro - 1, 3 );
        input.splice( idx_oro - 1, 0, new expr.disj([ triplet[ 0 ], triplet[ 2 ] ]) );
    }

    // clean it up
    input = new expr.conj( input ).normalize();

    // all terms are implicitly AND'ed -- always return something recursive
    if ( !input.isRecursive() )
        input = new expr.conj([ input ]);

    return input;

};
<commit_msg>Fix: parse() handles negation in the wrong direction<commit_after>'use strict';
var token = require( './token' );
var lex = require( './lex' );
var expr = require( './expr' );

/*
    Transform a list of lexer tokens into a expr-symbols.
*/

const T_NOT = 'not';
const T_OR  = 'or';

module.exports = function parse ( input ) {

    // try to act as passthrough for lex()
    if ( input instanceof String || typeof input === 'string' )
        input = lex( input );
    if ( !( input instanceof Array ) && typeof input !== 'array' )
        throw new Error( "parse() accepts string or array-of-tokens" );

    // strip out whitespace and quotes
    input = input.filter( ( tk ) => {
        return !(
            tk instanceof token.wts ||
            tk instanceof token.quo
        );
    } );

    // promote all lexer 'trm's to expr 'terms'
    // convert 'OR's and 'NOT's from terms to negators
    input = input.map( ( tk ) => {
        if ( !( tk instanceof token.trm ) )
            return tk;
        else if ( tk.getText().toLowerCase() === T_NOT )
            return new token.neg( tk.getSite() );
        else if ( tk.getText().toLowerCase() === T_OR  )
            return new token.oro( tk.getSite() );
        else
            return new expr.term( tk );
    } );

    // combine tag pairs
    var idx_tag;
    while ( ( idx_tag = input.findIndex( ( tk ) => {
        return tk instanceof token.tag;
    } ) ) !== -1 ) {
        if ( idx_tag === 0 || idx_tag === input.length - 1 )
            throw input[ idx_tag ].error( "Unpaired tag delimiter" );
        if ( !( input[ idx_tag - 1 ] instanceof expr.term ) )
            throw input[ idx_tag - 1 ].error( "Tag attribute not given as term" );
        if ( !( input[ idx_tag + 1 ] instanceof expr.term ) )
            throw input[ idx_tag + 1 ].error( "Tag value not given as term" );

        let triplet = input.splice( idx_tag - 1, 3 );
        let tag = new expr.tagp([
            triplet[ 0 ].getTokens()[ 0 ],
            triplet[ 1 ],
            triplet[ 2 ].getTokens()[ 0 ]
        ]);
        if ( triplet[ 0 ].isNegated() )
            tag = tag.negate();
        input.splice( idx_tag - 1, 0, tag );
    }

    // fold parenthetical groups, recursive descent
    function ps_expr ( input, expectRpr ) {
        let output = [];
        while ( input.length ) {
            let curr = input.shift();
            if ( curr instanceof token.lpr ) {
                let subexpr;
                try {
                    subexpr = parse( ps_expr( input, true ) );
                } catch ( ex ) {
                    throw curr.error( ex.message );
                }
                output.push( subexpr );
            } else if ( curr instanceof token.rpr ) {
                if ( !expectRpr )
                    throw curr.error( "Unbalanced paren" );
                return output;
            } else {
                output.push( curr );
            }
        }
        if ( expectRpr )
            throw new Error( "Unbalanced paren" );
        return output;
    }
    input = ps_expr( input );

    // apply negation
    var idx_neg;
    if ( input[ input.length - 1 ] instanceof token.neg )
        throw input[ input.length - 1 ].error( "Stray negator" );
    for ( var idx_neg = input.length - 2; idx_neg >= 0; idx_neg-- ) {
        if ( input[ idx_neg ] instanceof token.neg )
            input.splice( idx_neg, 2, input[ idx_neg + 1 ].negate() );
    }

    // combine OR'ed expressions
    var idx_oro;
    while ( ( idx_oro = input.findIndex( ( tk ) => {
        return tk instanceof token.oro;
    } ) ) !== -1 ) {
        if ( idx_oro === 0 || idx_oro === input.length - 1 )
            throw input[ idx_oro ].error( "Unpaired OR operator" );
        let triplet = input.splice( idx_oro - 1, 3 );
        input.splice( idx_oro - 1, 0, new expr.disj([ triplet[ 0 ], triplet[ 2 ] ]) );
    }

    // clean it up
    input = new expr.conj( input ).normalize();

    // all terms are implicitly AND'ed -- always return something recursive
    if ( !input.isRecursive() )
        input = new expr.conj([ input ]);

    return input;

};
<|endoftext|>


<commit_before>import KasenArray from "./array";

export default KasenArray;
<commit_msg>Use require and module.exports in only entry point<commit_after>const { default: KasenArray } = require("./array");

module.exports = KasenArray;
<|endoftext|>


<commit_before>'use strict';

var React = require('react');
var ReactDOM = require('react-dom');
var classNames = require('classnames');
var omit = require('object.omit');
var ClassNameMixin = require('./mixins/ClassNameMixin');
var DimmerMixin = require('./mixins/DimmerMixin');
var Events = require('./utils/Events');
var Close = require('./Close');
var Icon = require('./Icon');

var Modal = React.createClass({
  mixins: [ClassNameMixin, DimmerMixin],

  propTypes: {
    classPrefix: React.PropTypes.string.isRequired,
    type: React.PropTypes.oneOf(['alert', 'confirm', 'prompt', 'loading',
      'actions', 'popup']),
    title: React.PropTypes.node,
    confirmText: React.PropTypes.string,
    cancelText: React.PropTypes.string,
    closeIcon: React.PropTypes.bool,
    closeViaDimmer: React.PropTypes.bool,
    onRequestClose: React.PropTypes.func
  },

  getDefaultProps: function() {
    return {
      classPrefix: 'modal',
      closeIcon: true,
      confirmText: '确定',
      cancelText: '取消',
      onRequestClose: function() {}
    };
  },

  getInitialState: function() {
    return {
      transitioning: false
    };
  },

  componentDidMount: function() {
    this._documentKeyupListener =
      Events.on(document, 'keyup', this.handleDocumentKeyUp, false);

    this.setDimmerContainer();

    // TODO: 何为添加动画效果的最佳时机？ render 完成以后添加动画 Class？
    this.setState({
      transitioning: true
    });
  },

  componentWillUnmount: function() {
    this._documentKeyupListener.off();
    this.resetDimmerContainer();
  },

  handleDimmerClick: function() {
    if (this.props.closeViaDimmer) {
      this.props.onRequestClose();
    }
  },

  handleBackdropClick: function(e) {
    if (e.target !== e.currentTarget) {
      return;
    }

    this.props.onRequestClose();
  },

  handleDocumentKeyUp: function(e) {
    if (!this.props.keyboard && e.keyCode === 27) {
      this.props.onRequestClose();
    }
  },

  isPopup: function() {
    return this.props.type === 'popup';
  },

  isActions: function() {
    return this.props.type === 'actions';
  },

  // Get input data for prompt modal
  getPromptData: function() {
    var data = [];
    var inputs = ReactDOM.findDOMNode(this).querySelectorAll('input');

    if (inputs) {
      var i = 0;

      for (; i < inputs.length; i++) {
        data.push(inputs[i].value);
      }
    }

    return (data.length === 0) ? null : ((data.length === 1) ? data[0] : data);
  },

  handleConfirm: function(e) {
    var data = e;

    if (this.props.type === 'prompt') {
      data = this.getPromptData();
    }

    this.props.onConfirm(data);
  },

  renderActions: function() {
    return (

        {this.props.children}

    );
  },

  renderPopup: function() {
    return (

            {this.props.title ? (

                {this.props.title}

            ) : null}
            <Close onClick={this.props.onRequestClose} />

            {this.props.children}

    );
  },

  renderHeader: function() {
    var title = this.props.title;
    var closeIcon = this.props.closeIcon && !this.props.type ? (
      <Close
        spin
        onClick={this.props.onRequestClose}
      />
    ) : null;

    return (this.props.title || closeIcon) ? (

        {title ? (

            {title}

        ) : null}
        {closeIcon}

) : null;
  },

  // Render alert/confirm/prompt buttons
  renderFooter: function() {
    var buttons;
    var btnClass = this.prefixClass('btn');
    var props = this.props;

    switch (this.props.type) {
      case 'alert':
        buttons = (

            {this.props.confirmText}
          );
        break;
      case 'confirm':
      case 'prompt':
        buttons = [props.cancelText, props.confirmText].map(function(text, i) {
          return (

              {text}

          );
        }.bind(this));
        break;
      default:
        buttons = null;
    }

    return buttons ? (

        {buttons}

    ) : null;
  },

  render: function() {
    if (this.isActions()) {
      return this.renderDimmer(this.renderActions());
    }

    if (this.isPopup()) {
      return this.renderDimmer(this.renderPopup());
    }

    var classSet = this.getClassSet();
    var props = this.props;
    var footer = this.renderFooter();
    var style = {
        display: 'block',
        // marginLeft: props.marginLeft,
        // marginTop: props.marginTop
      };
    var dialogDimension = {
      width: props.modalWidth,
      height: props.modalHeight,
    };
    var restProps = omit(this.props, Object.keys(this.constructor.propTypes));

    classSet[this.prefixClass('active')] = this.state.transitioning;

    // .am-modal-no-btn -> refactor this style using `~` selector
    classSet[this.prefixClass('no-btn')] = !footer;
    props.type && (classSet[this.prefixClass(props.type)] = true);

    var modal = (

          {this.renderHeader()}

            {props.type === 'loading' ?
              (props.children ? props.children : <Icon icon="spinner" spin />) :
              props.children}

          {footer}

    );

    return this.renderDimmer(modal);
  }
});

module.exports = Modal;

// TODO: Modal 动画效果实现
// -> 如何关闭 Loading Modal?
// -> 关闭 Modal 以后窗口滚动会原来滚动条所在位置
<commit_msg>fixes unknown props warning in Modal<commit_after>'use strict';

var React = require('react');
var ReactDOM = require('react-dom');
var classNames = require('classnames');
var omit = require('object.omit');
var ClassNameMixin = require('./mixins/ClassNameMixin');
var DimmerMixin = require('./mixins/DimmerMixin');
var Events = require('./utils/Events');
var Close = require('./Close');
var Icon = require('./Icon');

var Modal = React.createClass({
  mixins: [ClassNameMixin, DimmerMixin],

  propTypes: {
    classPrefix: React.PropTypes.string.isRequired,
    type: React.PropTypes.oneOf(['alert', 'confirm', 'prompt', 'loading',
      'actions', 'popup']),
    title: React.PropTypes.node,
    confirmText: React.PropTypes.string,
    cancelText: React.PropTypes.string,
    closeIcon: React.PropTypes.bool,
    closeViaDimmer: React.PropTypes.bool,
    onRequestClose: React.PropTypes.func,
    marginTop: React.PropTypes.string,
    marginLeft: React.PropTypes.string,
    modalWidth: React.PropTypes.string,
    modalHeight: React.PropTypes.string,
    onConfirm: React.PropTypes.func,
    onCancel: React.PropTypes.func
  },

  getDefaultProps: function() {
    return {
      classPrefix: 'modal',
      closeIcon: true,
      confirmText: '确定',
      cancelText: '取消',
      onRequestClose: function() {}
    };
  },

  getInitialState: function() {
    return {
      transitioning: false
    };
  },

  componentDidMount: function() {
    this._documentKeyupListener =
      Events.on(document, 'keyup', this.handleDocumentKeyUp, false);

    this.setDimmerContainer();

    // TODO: 何为添加动画效果的最佳时机？ render 完成以后添加动画 Class？
    this.setState({
      transitioning: true
    });
  },

  componentWillUnmount: function() {
    this._documentKeyupListener.off();
    this.resetDimmerContainer();
  },

  handleDimmerClick: function() {
    if (this.props.closeViaDimmer) {
      this.props.onRequestClose();
    }
  },

  handleBackdropClick: function(e) {
    if (e.target !== e.currentTarget) {
      return;
    }

    this.props.onRequestClose();
  },

  handleDocumentKeyUp: function(e) {
    if (!this.props.keyboard && e.keyCode === 27) {
      this.props.onRequestClose();
    }
  },

  isPopup: function() {
    return this.props.type === 'popup';
  },

  isActions: function() {
    return this.props.type === 'actions';
  },

  // Get input data for prompt modal
  getPromptData: function() {
    var data = [];
    var inputs = ReactDOM.findDOMNode(this).querySelectorAll('input');

    if (inputs) {
      var i = 0;

      for (; i < inputs.length; i++) {
        data.push(inputs[i].value);
      }
    }

    return (data.length === 0) ? null : ((data.length === 1) ? data[0] : data);
  },

  handleConfirm: function(e) {
    var data = e;

    if (this.props.type === 'prompt') {
      data = this.getPromptData();
    }

    this.props.onConfirm(data);
  },

  renderActions: function() {
    return (

        {this.props.children}

    );
  },

  renderPopup: function() {
    return (

            {this.props.title ? (

                {this.props.title}

            ) : null}
            <Close onClick={this.props.onRequestClose} />

            {this.props.children}

    );
  },

  renderHeader: function() {
    var title = this.props.title;
    var closeIcon = this.props.closeIcon && !this.props.type ? (
      <Close
        spin
        onClick={this.props.onRequestClose}
      />
    ) : null;

    return (this.props.title || closeIcon) ? (

        {title ? (

            {title}

        ) : null}
        {closeIcon}

) : null;
  },

  // Render alert/confirm/prompt buttons
  renderFooter: function() {
    var buttons;
    var btnClass = this.prefixClass('btn');
    var props = this.props;

    switch (this.props.type) {
      case 'alert':
        buttons = (

            {this.props.confirmText}
          );
        break;
      case 'confirm':
      case 'prompt':
        buttons = [props.cancelText, props.confirmText].map(function(text, i) {
          return (

              {text}

          );
        }.bind(this));
        break;
      default:
        buttons = null;
    }

    return buttons ? (

        {buttons}

    ) : null;
  },

  render: function() {
    if (this.isActions()) {
      return this.renderDimmer(this.renderActions());
    }

    if (this.isPopup()) {
      return this.renderDimmer(this.renderPopup());
    }

    var classSet = this.getClassSet();
    var props = this.props;
    var footer = this.renderFooter();
    var style = {
        display: 'block',
        // marginLeft: props.marginLeft,
        // marginTop: props.marginTop
      };
    var dialogDimension = {
      width: props.modalWidth,
      height: props.modalHeight,
    };
    var restProps = omit(this.props, Object.keys(this.constructor.propTypes));

    classSet[this.prefixClass('active')] = this.state.transitioning;

    // .am-modal-no-btn -> refactor this style using `~` selector
    classSet[this.prefixClass('no-btn')] = !footer;
    props.type && (classSet[this.prefixClass(props.type)] = true);

    var modal = (

          {this.renderHeader()}

            {props.type === 'loading' ?
              (props.children ? props.children : <Icon icon="spinner" spin />) :
              props.children}

          {footer}

    );

    return this.renderDimmer(modal);
  }
});

module.exports = Modal;

// TODO: Modal 动画效果实现
// -> 如何关闭 Loading Modal?
// -> 关闭 Modal 以后窗口滚动会原来滚动条所在位置
<|endoftext|>


<commit_before>// The lexer.

var tokens = [
    [/^(?:\r\n|\r|\n)/, null],
    [/^\s+/, null],
    [/^"(?:\\(?:.|\r\n|\r|\n)|[^"\\\n])*"/i, 'string'],
    [/^'(?:\\(?:.|\r\n|\r|\n)|[^'\\\n])*'/i, 'string'],
    [/^#[^\n\r]*/i, 'comment'],
    // Floats must be matched before integers
    [/^[1-9][0-9]+\.[0-9]+/, 'float'],
    [/^0\.[0-9]+/, 'float'],
    [/^[1-9][0-9]*(?!\.)/, 'integer'],
    [/^0(?!\.)/, 'integer'],
    [/^;/, ';'],
    [/^,/, ','],
    [/^\{/, '{'],
    [/^\}/, '}'],
    [/^\[/, '['],
    [/^\]/, ']'],
    [/^\(/, '('],
    [/^\)/, ')'],
    [/^\+/, '+'],
    [/^\-/, '-'],
    [/^\//, '/'],
    [/^\*/, '*'],
    [/^%/, '%'],
    [/^==/, '=='],
    [/^!=/, '!='],
    [/^<=/, '<='],
    [/^>=/, '>='],
    [/^=/, '='],
    [/^</, '<'],
    [/^>/, '>'],
    [/^:/, ':'],  // For `case` and types
    [/^!/, '!'],

    [/^var(?!\w)/, 'var'],
    [/^switch(?!\w)/, 'switch'],
    [/^case(?!\w)/, 'case'],
    [/^do(?!\w)/, 'do'],
    [/^while(?!\w)/, 'while'],
    [/^if(?!\w)/, 'if'],
    [/^else(?!\w)/, 'else'],
    [/^true(?!\w)/, 'true'],
    [/^false(?!\w)/, 'false'],
    [/^null(?!\w)/, 'null'],
    [/^for(?!\w)/, 'for'],
    [/^continue(?!\w)/, 'continue'],
    [/^break(?!\w)/, 'break'],
    [/^or(?!\w)/, 'or'],
    [/^and(?!\w)/, 'and'],
    [/^func(?!\w)/, 'func'],
    [/^return(?!\w)/, 'return'],

    [/^[a-zA-Z]\w*/, 'identifier'],
];

function Token(text, type, start, end) {
    this.text = text;
    this.type = type;
    this.start = start;
    this.end = end;
};

module.exports = function(data) {
    var pointer = 0;
    var remainingData = data;
    var currentLine = 1;

    function readToken() {
        var match;
        var startPointer = pointer;
        for (var i = 0; i < tokens.length; i++) {
            if (!(match = tokens[i][0].exec(remainingData))) {
                continue;
            }
            remainingData = remainingData.substr(match[0].length);
            currentLine += match[0].split(/(?:\r\n|\r|\n)/).length - 1;
            pointer += match[0].length;
            if (!tokens[i][1]) {
                i = -1;
                continue;
            }
            return new Token(match[0], tokens[i][1], startPointer, pointer);
        }
        return null;
    }
    return function() {
        if (!remainingData) return 'EOF';
        var token = readToken();
        if (!token) {
            console.error('Syntax Error');
            console.error(remainingData);
            throw new SyntaxError('Unknown token at line ' + currentLine);
        }
        return token;
    };
};

module.exports.token = Token;
<commit_msg>Fix trailing whitespace breaking the lexer<commit_after>// The lexer.

var tokens = [
    [/^(?:\r\n|\r|\n)/, null],
    [/^\s+/, null],
    [/^"(?:\\(?:.|\r\n|\r|\n)|[^"\\\n])*"/i, 'string'],
    [/^'(?:\\(?:.|\r\n|\r|\n)|[^'\\\n])*'/i, 'string'],
    [/^#[^\n\r]*/i, 'comment'],
    // Floats must be matched before integers
    [/^[1-9][0-9]+\.[0-9]+/, 'float'],
    [/^0\.[0-9]+/, 'float'],
    [/^[1-9][0-9]*(?!\.)/, 'integer'],
    [/^0(?!\.)/, 'integer'],
    [/^;/, ';'],
    [/^,/, ','],
    [/^\{/, '{'],
    [/^\}/, '}'],
    [/^\[/, '['],
    [/^\]/, ']'],
    [/^\(/, '('],
    [/^\)/, ')'],
    [/^\+/, '+'],
    [/^\-/, '-'],
    [/^\//, '/'],
    [/^\*/, '*'],
    [/^%/, '%'],
    [/^==/, '=='],
    [/^!=/, '!='],
    [/^<=/, '<='],
    [/^>=/, '>='],
    [/^=/, '='],
    [/^</, '<'],
    [/^>/, '>'],
    [/^:/, ':'],  // For `case` and types
    [/^!/, '!'],

    [/^var(?!\w)/, 'var'],
    [/^switch(?!\w)/, 'switch'],
    [/^case(?!\w)/, 'case'],
    [/^do(?!\w)/, 'do'],
    [/^while(?!\w)/, 'while'],
    [/^if(?!\w)/, 'if'],
    [/^else(?!\w)/, 'else'],
    [/^true(?!\w)/, 'true'],
    [/^false(?!\w)/, 'false'],
    [/^null(?!\w)/, 'null'],
    [/^for(?!\w)/, 'for'],
    [/^continue(?!\w)/, 'continue'],
    [/^break(?!\w)/, 'break'],
    [/^or(?!\w)/, 'or'],
    [/^and(?!\w)/, 'and'],
    [/^func(?!\w)/, 'func'],
    [/^return(?!\w)/, 'return'],

    [/^[a-zA-Z]\w*/, 'identifier'],
];

function Token(text, type, start, end) {
    this.text = text;
    this.type = type;
    this.start = start;
    this.end = end;
};

module.exports = function(data) {
    var pointer = 0;
    var remainingData = data;
    var currentLine = 1;

    function readToken() {
        var match;
        var startPointer = pointer;
        for (var i = 0; i < tokens.length; i++) {
            if (!(match = tokens[i][0].exec(remainingData))) {
                continue;
            }
            remainingData = remainingData.substr(match[0].length);
            currentLine += match[0].split(/(?:\r\n|\r|\n)/).length - 1;
            pointer += match[0].length;
            if (!tokens[i][1]) {
                i = -1;
                continue;
            }
            return new Token(match[0], tokens[i][1], startPointer, pointer);
        }
        return null;
    }
    return function() {
        if (!remainingData.trim()) return 'EOF';
        var token = readToken();
        if (!token) {
            console.error('Syntax Error');
            console.error(remainingData);
            throw new SyntaxError('Unknown token at line ' + currentLine);
        }
        return token;
    };
};

module.exports.token = Token;
<|endoftext|>


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

const ToggleElement = require('./ToggleElement')
const util = require('./util.js')
class Modal extends ToggleElement {

  constructor(options) {
    options = options || {}
    let element = util.div('modal')
    element.style.right = options.baseright || '0'
    element.style.left = options.baseleft || '0'
    element.style.top = options.basetop || '0'
    element.style.bottom = options.basebottom || '0'
    element.style['background-color'] = options.backgroundcolor || 'rgba(0,0,0,0.4)'
    element.style.height = options.baseheight || '100%'
    element.style.width = options.basewidth || '100%'

    let content = util.div('modal-content')
    content.style.width = options.width || 'auto'
    content.style.height = options.height || 'auto'
    content.style.maxwidth = options.maxwidth || '100%'
    content.style.maxheight = options.maxheight || '100%'

    if (options.draggable) {
      content.draggable = true

      content.ondragstart = (ev) => {
        ev.dataTransfer.dropEffect = "move"
        content.y = Number(content.style.top.replace("px", ""))
        content.x = Number(content.style.left.replace("px", ""))
        content.mouseY = ev.clientY
        content.mouseX = ev.clientX
      }
      content.ondrag = (ev) => {}

      element.ondrop = (ev) => {
        let dX = ev.clientX - content.mouseX
        let dY = ev.clientY - content.mouseY
        content.style.left = `${content.x+dX}px`
        content.style.top = `${content.y+dY}px`
      }
      element.ondragover = (ev) => {
        ev.preventDefault()
      }
    }

    element.addEventListener('click', () => {
      if (!options.permanent) this.destroy()
      if (typeof options.oncancel === 'function') {
        options.oncancel()
      }
    });

    content.addEventListener('click', (e) => {
      e.stopPropagation()
    });

    element.autofocus = true;
    element.addEventListener("keydown", (e) => {
      if (e.keyCode == 13) { //enter
        if (typeof options.onsubmit === 'function') {
          options.onsubmit()
        }
        if (!options.permanent) this.destroy()
      }
      if (e.keyCode == 27) { //escape or supr
        if (typeof options.oncancel === 'function') {
          options.oncancel()
        }
        if (!options.permanent) this.destroy()
      }
    });

    element.appendChild(content)
    options.parent = options.parent || util.body
    options.parent.appendChild(element)
    super(element)
    this.options = options
    this.content = content
    this.addTitle(options.title)
    this.addBody(options.body)
    this.addFooter(options.footer)
    this.hide()
  }

  destroy() {
    this.hide()
    this.clear()
    this.options.parent.removeChild(this.element)
  }

  addHeader(header) {
    this.addTitle(header)
  }

  addTitle(title) {
    if (title) {
      if (typeof title === 'string') {
        this.header = util.div('modal-header')
        this.header.innerHTML = title
        this.content.insertBefore(this.header, this.content.firstChild)
      } else if (title instanceof ToggleElement) {
        this.header = title
        this.content.insertBefore(this.header.element, this.content.firstChild)
      } else if (title.appendChild) {
        this.header = title
        this.content.insertBefore(this.header, this.content.firstChild)
      }
    }
    return this
  }

  addBody(body) {
    if (body) {
      if (body instanceof ToggleElement) {
        body.element.classList.add('modal-body')
        body.appendTo(this.content)
        this.body = body
      } else if (body.appendChild) {
        body.classList.add('modal-body')
        this.content.appendChild(body)
        this.body = body
      } else if (typeof body === 'string') {
        this.body = util.div('modal-body')
        this.body.innerHTML = body
        this.content.appendChild(this.body)
      }
    }
    return this
  }

  addFooter(footer) {
    if (footer) {
      if (footer instanceof ToggleElement) {
        footer.element.classList.add('toolbar','toolbar-footer')
        footer.appendTo(this.content)
        this.footer = footer
      } else if (footer.appendChild) {
        footer.classList.add('toolbar','toolbar-footer')
        this.content.appendChild(footer)
        this.footer = footer
      } else if (typeof footer === 'string') {
        this.footer = util.div('toolbar toolbar-footer')
        this.footer.innerHTML = footer
        this.content.appendChild(this.footer)
      }
    }
    return this
  }

}

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

const ToggleElement = require('./ToggleElement')
const util = require('./util.js')
class Modal extends ToggleElement {

  constructor(options) {
    options = options || {}
    let element = util.div('modal')
    element.style.right = options.baseright || '0'
    element.style.left = options.baseleft || '0'
    element.style.top = options.basetop || '0'
    element.style.bottom = options.basebottom || '0'
    element.style['background-color'] = options.backgroundcolor || 'rgba(0,0,0,0.4)'
    element.style.height = options.baseheight || '100%'
    element.style.width = options.basewidth || '100%'

    let content = util.div('modal-content')
    content.style.width = options.width || 'auto'
    content.style.height = options.height || 'auto'
    content.style.maxwidth = options.maxwidth || '100%'
    content.style.maxheight = options.maxheight || '100%'

    if (options.draggable) {
      content.draggable = true

      content.ondragstart = (ev) => {
        ev.dataTransfer.dropEffect = "move"
        content.y = Number(content.style.top.replace("px", ""))
        content.x = Number(content.style.left.replace("px", ""))
        content.mouseY = ev.clientY
        content.mouseX = ev.clientX
      }
      content.ondrag = (ev) => {}

      element.ondrop = (ev) => {
        let dX = ev.clientX - content.mouseX
        let dY = ev.clientY - content.mouseY
        content.style.left = `${content.x+dX}px`
        content.style.top = `${content.y+dY}px`
      }
      element.ondragover = (ev) => {
        ev.preventDefault()
      }
    }

    element.addEventListener('click', () => {
      if (!options.permanent) this.destroy()
      if (typeof options.oncancel === 'function') {
        options.oncancel()
      }
    });

    content.addEventListener('click', (e) => {
      e.stopPropagation()
    });

    element.autofocus = true;
    element.addEventListener("keydown", (e) => {
      if (e.keyCode == 13) { //enter
        if (typeof options.onsubmit === 'function') {
          options.onsubmit()
        }
        if (!options.permanent) this.destroy()
      }
      if (e.keyCode == 27) { //escape or supr
        if (typeof options.oncancel === 'function') {
          options.oncancel()
        }
        if (!options.permanent) this.destroy()
      }
    });

    element.appendChild(content)
    options.parent = options.parent || util.body
    options.parent.appendChild(element)
    super(element)
    this.options = options
    this.content = content
    this.addTitle(options.title)
    this.addBody(options.body)
    this.addFooter(options.footer)
    this.hide()
  }

  destroy() {
    this.hide()
    this.clear()
    this.options.parent.removeChild(this.element)
  }

  addHeader(header) {
    this.addTitle(header)
  }

  show(){
    super.show()
    let inps = this.element.getElementsByTagName('INPUT')
    if (inps.length>0) inps[0].focus()
  }

  addTitle(title) {
    if (title) {
      if (typeof title === 'string') {
        this.header = util.div('modal-header')
        this.header.innerHTML = title
        this.content.insertBefore(this.header, this.content.firstChild)
      } else if (title instanceof ToggleElement) {
        this.header = title
        this.content.insertBefore(this.header.element, this.content.firstChild)
      } else if (title.appendChild) {
        this.header = title
        this.content.insertBefore(this.header, this.content.firstChild)
      }
    }
    return this
  }

  addBody(body) {
    if (body) {
      if (body instanceof ToggleElement) {
        body.element.classList.add('modal-body')
        body.appendTo(this.content)
        this.body = body
      } else if (body.appendChild) {
        body.classList.add('modal-body')
        this.content.appendChild(body)
        this.body = body
      } else if (typeof body === 'string') {
        this.body = util.div('modal-body')
        this.body.innerHTML = body
        this.content.appendChild(this.body)
      }
    }
    return this
  }

  addFooter(footer) {
    if (footer) {
      if (footer instanceof ToggleElement) {
        footer.element.classList.add('toolbar','toolbar-footer')
        footer.appendTo(this.content)
        this.footer = footer
      } else if (footer.appendChild) {
        footer.classList.add('toolbar','toolbar-footer')
        this.content.appendChild(footer)
        this.footer = footer
      } else if (typeof footer === 'string') {
        this.footer = util.div('toolbar toolbar-footer')
        this.footer.innerHTML = footer
        this.content.appendChild(this.footer)
      }
    }
    return this
  }

}

module.exports = Modal
<|endoftext|>


<commit_before>var Parse = require('parse');
var ngParseModule = require('./module.js');

/**
 * @ngdoc object
 * @name ngParse.ParseProvider
 *
 * @description
 * Provider for Parse service.
 */
ParseProvider.$inject = [];
function ParseProvider() {
  /**
   * Defines getters and setters for the attributes
   * of the given object or function prototype.
   * Or create a decorator that defines getters
   * and setters for the subclass Parse.Object.
   *
   * @param {Object|Function|String|String[]} object
   * @param {...String|String[]=} attributes
   * @returns {*}
   */
  function defineAttributes(object, attributes) {
    if (object instanceof Parse.Object) {
      if (!(attributes instanceof Array)) attributes = Array.prototype.slice.call(arguments, 1);
      attributes.forEach(function (attribute) {
        Object.defineProperty(object, attribute, {
          get: function () {
            return this.get(attribute);
          },
          set: function (value) {
            this.set(attribute, value);
          },
          configurable: true,
          enumerable: true
        });
      });
    } else if (typeof object == 'function') {
      return defineAttributes(object.prototype, attributes)
    } else {
      if (object instanceof Array) attributes = object;
      else attributes = Array.prototype.slice.call(arguments, 0);
      return function defineAttributesDecorator(target) {
        defineAttributes(target, attributes);
      }
    }
  }

  // Parse
  var AngularParse = Object.create(Parse);

  // ParseObject
  Object.defineProperty(AngularParse.Object.prototype, '$id', {
    get: function () {
      return this.$$id === undefined || this.$$id === null ? this.id : this.$$id;
    },
    set: function (value) {
      this.$$id = value;
    },
    configurable: true,
    enumerable: false
  });

  // ParseUser
  defineAttributes(AngularParse.User, ['email', 'password', 'username']);

  var provider = AngularParse;

  /**
   * @ngdoc method
   * @name ngParse.ParseProvider#defineAttributes
   * @methodOf ngParse.ParseProvider
   * @static
   * @see {@link defineAttributes}
   */
  provider.defineAttributes = defineAttributes;

  /**
   * @ngdoc service
   * @name ngParse.Parse
   *
   * @requires $q
   *
   * @description
   * This is a wrapper for [Parse]{@link https://parse.com/docs/js/api/classes/Parse.html}.
   */
  provider.$get = ParseFactory;
  ParseFactory.$inject = ['$q'];
  function ParseFactory($q) {
    /**
     * Wraps Promise.
     *
     * @param {Object} promise
     * @param {Object} parsePromise
     * @returns {Object}
     */
    function wrapParsePromise(promise, parsePromise) {
      promise._rejected = parsePromise._rejected;
      promise._rejectedCallbacks = parsePromise._rejectedCallbacks;
      promise._resolved = parsePromise._resolved;
      promise._resolvedCallbacks = parsePromise._resolvedCallbacks;
      promise._result = parsePromise._result;
      promise.reject = parsePromise.reject;
      promise.resolve = parsePromise.resolve;

      ['_continueWith', '_thenRunCallbacks', 'always', 'done', 'fail'].forEach(function (method) {
        promise[method] = wrap(parsePromise[method]);
      });

      ['then', 'catch'].forEach(function (method) {
        var func = promise[method];
        promise[method] = function wrappedAngularPromise() {
          var args = Array.prototype.slice.call(arguments, 0);
          var promise = func.apply(this, args);
          wrapParsePromise(promise, parsePromise);
          return promise;
        };
      });

      return promise;
    }

    /**
     * Wraps function.
     *
     * @param {Function} func Function that returns
     * [Parse.Promise]{@link https://parse.com/docs/js/api/classes/Parse.Promise.html}.
     * @returns {Function} Function that returns $q promises.
     */
    function wrap(func) {
      return function wrappedParsePromise() {
        var args = Array.prototype.slice.call(arguments, 0);
        var parsePromise = func.apply(this, args);
        var promise = $q(parsePromise.then.bind(parsePromise));
        wrapParsePromise(promise, parsePromise);
        return promise;
      };
    }

    /**
     * Wraps object.
     *
     * @param {Object} object
     * @param {...String|String[]=} methods
     */
    function wrapObject(object, methods) {
      if (!(methods instanceof Array)) methods = Array.prototype.slice.call(arguments, 1);
      methods.forEach(function (method) {
        object[method] = wrap(object[method]);
      });
    }

    /**
     * @ngdoc method
     * @name ngParse.Parse#wrapObject
     * @methodOf ngParse.Parse
     * @static
     * @see {@link wrapObject}
     */
    AngularParse.wrapObject = wrapObject;

    // ParseCloud
    wrapObject(AngularParse.Cloud, ['run']);

    // ParseConfig
    wrapObject(AngularParse.Config, ['get']);

    //FacebookUtils
    wrapObject(AngularParse.FacebookUtils, ['link', 'logIn', 'unlink']);

    // ParseFile
    wrapObject(AngularParse.File.prototype, ['save']);

    // ParseObject
    wrapObject(AngularParse.Object, ['destroyAll', 'fetchAll', 'fetchAllIfNeeded', 'saveAll']);
    wrapObject(AngularParse.Object.prototype, ['destroy', 'fetch', 'save']);

    // ParsePromise
    wrapObject(AngularParse.Promise, ['_continueWhile', 'as', 'error', 'when']);

    // ParsePush
    wrapObject(AngularParse.Push, ['send']);

    // ParseQuery
    wrapObject(AngularParse.Query.prototype, ['count', 'each', 'find', 'first', 'get']);

    // ParseSession
    wrapObject(AngularParse.Session, ['current']);

    // ParseUser
    wrapObject(AngularParse.User, ['become', 'currentAsync', 'enableRevocableSession', 'logIn', 'logOut', 'requestPasswordReset', 'signUp']);
    wrapObject(AngularParse.User.prototype, ['logIn', 'signUp']);

    return AngularParse;
  }

  return provider;
}

ngParseModule
  .provider('Parse', ParseProvider);
<commit_msg>v0.2.0<commit_after>var Parse = require('parse');
var ngParseModule = require('./module.js');

/**
 * @ngdoc object
 * @name ngParse.ParseProvider
 *
 * @description
 * Provider for Parse service.
 */
ParseProvider.$inject = [];
function ParseProvider() {
  /**
   * Defines getters and setters for the attributes
   * of the given object or function prototype.
   * Or create a decorator that defines getters
   * and setters for the subclass Parse.Object.
   *
   * @param {Object|Function|String|String[]} object
   * @param {...String|String[]=} attributes
   * @returns {*}
   */
  function defineAttributes(object, attributes) {
    if (object instanceof Parse.Object) {
      if (!(attributes instanceof Array)) attributes = Array.prototype.slice.call(arguments, 1);
      attributes.forEach(function (attribute) {
        Object.defineProperty(object, attribute, {
          get: function () {
            return this.get(attribute);
          },
          set: function (value) {
            this.set(attribute, value);
          },
          configurable: true,
          enumerable: true
        });
      });
    } else if (typeof object == 'function') {
      return defineAttributes(object.prototype, attributes)
    } else {
      if (object instanceof Array) attributes = object;
      else attributes = Array.prototype.slice.call(arguments, 0);
      return function defineAttributesDecorator(target) {
        defineAttributes(target, attributes);
      }
    }
  }

  // Parse
  var AngularParse = Object.create(Parse);

  // ParseObject
  Object.defineProperty(AngularParse.Object.prototype, '$id', {
    get: function () {
      return this.$$id === undefined || this.$$id === null ? this.id : this.$$id;
    },
    set: function (value) {
      this.$$id = value;
    },
    configurable: true,
    enumerable: false
  });

  // ParseUser
  defineAttributes(AngularParse.User, ['email', 'password', 'username']);

  var provider = AngularParse;

  /**
   * @ngdoc method
   * @name ngParse.ParseProvider#defineAttributes
   * @methodOf ngParse.ParseProvider
   * @static
   * @see {@link defineAttributes}
   */
  provider.defineAttributes = defineAttributes;

  /**
   * @ngdoc service
   * @name ngParse.Parse
   *
   * @requires $q
   *
   * @description
   * This is a wrapper for [Parse]{@link https://parse.com/docs/js/api/classes/Parse.html}.
   */
  provider.$get = ParseFactory;
  ParseFactory.$inject = ['$q'];
  function ParseFactory($q) {
    /**
     * Wraps Promise.
     *
     * @param {Object} promise
     * @param {Object} parsePromise
     * @returns {Object}
     */
    function wrapParsePromise(promise, parsePromise) {
      ['_rejected', '_rejectedCallbacks', '_resolved', '_resolvedCallbacks', '_result', 'reject', 'resolve']
        .forEach(function (prop) {
          promise[prop] = parsePromise[prop];
        });

      ['_continueWith', '_thenRunCallbacks', 'always', 'done', 'fail'].forEach(function (method) {
        promise[method] = wrap(parsePromise[method]);
      });

      ['then', 'catch'].forEach(function (method) {
        var func = promise[method];
        promise[method] = function wrappedAngularPromise() {
          var args = Array.prototype.slice.call(arguments, 0);
          var promise = func.apply(this, args);
          wrapParsePromise(promise, parsePromise);
          return promise;
        };
      });

      return promise;
    }

    /**
     * Wraps function.
     *
     * @param {Function} func Function that returns
     * [Parse.Promise]{@link https://parse.com/docs/js/api/classes/Parse.Promise.html}.
     * @returns {Function} Function that returns $q promises.
     */
    function wrap(func) {
      return function wrappedParsePromise() {
        var args = Array.prototype.slice.call(arguments, 0);
        var parsePromise = func.apply(this, args);
        var promise = $q(parsePromise.then.bind(parsePromise));
        wrapParsePromise(promise, parsePromise);
        return promise;
      };
    }

    /**
     * Wraps object.
     *
     * @param {Object} object
     * @param {...String|String[]=} methods
     */
    function wrapObject(object, methods) {
      if (!(methods instanceof Array)) methods = Array.prototype.slice.call(arguments, 1);
      methods.forEach(function (method) {
        object[method] = wrap(object[method]);
      });
    }

    /**
     * @ngdoc method
     * @name ngParse.Parse#wrapObject
     * @methodOf ngParse.Parse
     * @static
     * @see {@link wrapObject}
     */
    AngularParse.wrapObject = wrapObject;

    // ParseCloud
    wrapObject(AngularParse.Cloud, ['run']);

    // ParseConfig
    wrapObject(AngularParse.Config, ['get']);

    //FacebookUtils
    wrapObject(AngularParse.FacebookUtils, ['link', 'logIn', 'unlink']);

    // ParseFile
    wrapObject(AngularParse.File.prototype, ['save']);

    // ParseObject
    wrapObject(AngularParse.Object, ['destroyAll', 'fetchAll', 'fetchAllIfNeeded', 'saveAll']);
    wrapObject(AngularParse.Object.prototype, ['destroy', 'fetch', 'save']);

    // ParsePromise
    wrapObject(AngularParse.Promise, ['_continueWhile', 'as', 'error', 'when']);

    // ParsePush
    wrapObject(AngularParse.Push, ['send']);

    // ParseQuery
    wrapObject(AngularParse.Query.prototype, ['count', 'each', 'find', 'first', 'get']);

    // ParseSession
    wrapObject(AngularParse.Session, ['current']);

    // ParseUser
    wrapObject(AngularParse.User, ['become', 'currentAsync', 'enableRevocableSession', 'logIn', 'logOut', 'requestPasswordReset', 'signUp']);
    wrapObject(AngularParse.User.prototype, ['logIn', 'signUp']);

    return AngularParse;
  }

  return provider;
}

ngParseModule
  .provider('Parse', ParseProvider);
<|endoftext|>


<commit_before>'use strict'

let ServiceApi = require('resource-management-framework')
	.ServiceApi;

class Qa {
	constructor() {
		this.emitter = message_bus;
	}

	init() {
		this.iris = new ServiceApi();
		this.iris.initContent();
	}
	launch() {
			this.emitter.on('ticket.emit.state', ({
				ticket,
				org_addr,
				workstation,
				event_name
			}) => {
				if (event_name == 'close') {
					let to_join = ['ticket.closed', org_addr, workstation];
					console.log("QA EMITTING TICKSTATE", _.join(to_join, "."));
					this.emitter.emit('broadcast', {
						event: _.join(to_join, "."),
						data: ticket
					});
				}
			});
			return Promise.resolve(true);
		}
		//API
	actionQuestions({
		code,
		device_type
	}) {
		let ticket;
		return this.emitter.addTask('ticket', {
				_action: "by-code",
				code
			})
			.then((res) => {
				if (!res.success) return Promise.reject(new Error('Ticket not found.'));

				ticket = res.ticket;

				let permitted = [];
				switch (device_type) {
				case 'terminal':
					permitted = ['closed'];
					break;
				case 'qa':
					permitted = ['processing'];
					break;
				}
				if (device_type && !_.find(permitted, v => (v == ticket.state)))
					return Promise.reject(new Error(`Ticket ${ticket.state}.`));
				if (_.find(ticket.history, (entry) => (entry.event_name == 'qa-check')))
					return Promise.reject(new Error(`Rating done.`));
				return this.iris.getQaQuestions();
			})
			.then((res) => {
				return {
					success: true,
					questions: res
				};
			})
			.catch((err) => {
				global.logger && logger.error(
					err, {
						module: 'qa',
						method: 'questions'
					});
				return {
					success: false,
					reason: ticket ? err.message : "Ticket not found."
				}
			});
	}

	actionAnswers({
		answers,
		workstation,
		code
	}) {
		let org;
		let tick;
		return Promise.props({
				history: this.emitter.addTask('history', {
					_action: 'make-entry',
					subject: {
						type: 'system',
						id: workstation
					},
					event_name: 'qa-check',
					reason: {}
				}),
				ticket: this.emitter.addTask('ticket', {
					_action: 'by-code',
					code
				}),
				pre: this.emitter.addTask('workstation', {
					_action: 'workstation-organization-data',
					workstation
				})
			})
			.then(({
				history,
				ticket,
				pre
			}) => {
				// console.log("QA", ticket, pre, history);
				org = pre[workstation];
				if (!ticket.success) return Promise.reject(new Error('Ticket not found.'));

				tick = ticket.ticket;
				if (_.find(tick.history, (entry) => (entry.event_name == 'qa-check')))
					return Promise.reject(new Error('Rating done.'));
				history.object = tick.id;
				history.local_time = moment.tz(org.org_merged.org_timezone);
				tick.history = tick.history || [];
				tick.history.push(history);
				tick.qa_answers = answers;
				return Promise.props({
					srv: this.iris.getEntryTypeless(tick.service)
						.then(res => res[tick.service]),
					tick: this.emitter.addTask('ticket', {
						_action: 'set-ticket',
						ticket: tick
					})
				});
			})
			.then(({
				srv
			}) => {
				this.emitter.command('mkgu.send.rates', {
					service: srv,
					organization: org.org_merged,
					ticket: tick
				});
				return Promise.resolve({
					success: true
				});
			});
	}

	actionBootstrap({}) {
		return Promise.resolve({
			success: true
		});
	}

	actionReady({}) {
		return Promise.resolve({
			success: true
		});
	}
}

module.exports = Qa;
<commit_msg>route<commit_after>'use strict'

let ServiceApi = require('resource-management-framework')
	.ServiceApi;

class Qa {
	constructor() {
		this.emitter = message_bus;
	}

	init() {
		this.iris = new ServiceApi();
		this.iris.initContent();
	}
	launch() {
			this.emitter.on('ticket.emit.state', ({
				ticket,
				org_addr,
				workstation,
				event_name
			}) => {
				if (event_name == 'close' || event_name == 'route') {
					let to_join = ['ticket.closed', org_addr, workstation];
					console.log("QA EMITTING TICKSTATE", _.join(to_join, "."));
					this.emitter.emit('broadcast', {
						event: _.join(to_join, "."),
						data: ticket
					});
				}
			});
			return Promise.resolve(true);
		}
		//API
	actionQuestions({
		code,
		device_type
	}) {
		let ticket;
		return this.emitter.addTask('ticket', {
				_action: "by-code",
				code
			})
			.then((res) => {
				if (!res.success) return Promise.reject(new Error('Ticket not found.'));

				ticket = res.ticket;

				let permitted = [];
				switch (device_type) {
				case 'terminal':
					permitted = ['closed'];
					break;
				case 'qa':
					permitted = ['processing'];
					break;
				}
				if (device_type && !_.find(permitted, v => (v == ticket.state)))
					return Promise.reject(new Error(`Ticket ${ticket.state}.`));
				if (_.find(ticket.history, (entry) => (entry.event_name == 'qa-check')))
					return Promise.reject(new Error(`Rating done.`));
				return this.iris.getQaQuestions();
			})
			.then((res) => {
				return {
					success: true,
					questions: res
				};
			})
			.catch((err) => {
				global.logger && logger.error(
					err, {
						module: 'qa',
						method: 'questions'
					});
				return {
					success: false,
					reason: ticket ? err.message : "Ticket not found."
				}
			});
	}

	actionAnswers({
		answers,
		workstation,
		code
	}) {
		let org;
		let tick;
		return Promise.props({
				history: this.emitter.addTask('history', {
					_action: 'make-entry',
					subject: {
						type: 'system',
						id: workstation
					},
					event_name: 'qa-check',
					reason: {}
				}),
				ticket: this.emitter.addTask('ticket', {
					_action: 'by-code',
					code
				}),
				pre: this.emitter.addTask('workstation', {
					_action: 'workstation-organization-data',
					workstation
				})
			})
			.then(({
				history,
				ticket,
				pre
			}) => {
				// console.log("QA", ticket, pre, history);
				org = pre[workstation];
				if (!ticket.success) return Promise.reject(new Error('Ticket not found.'));

				tick = ticket.ticket;
				if (_.find(tick.history, (entry) => (entry.event_name == 'qa-check')))
					return Promise.reject(new Error('Rating done.'));
				history.object = tick.id;
				history.local_time = moment.tz(org.org_merged.org_timezone);
				tick.history = tick.history || [];
				tick.history.push(history);
				tick.qa_answers = answers;
				return Promise.props({
					srv: this.iris.getEntryTypeless(tick.service)
						.then(res => res[tick.service]),
					tick: this.emitter.addTask('ticket', {
						_action: 'set-ticket',
						ticket: tick
					})
				});
			})
			.then(({
				srv
			}) => {
				this.emitter.command('mkgu.send.rates', {
					service: srv,
					organization: org.org_merged,
					ticket: tick
				});
				return Promise.resolve({
					success: true
				});
			});
	}

	actionBootstrap({}) {
		return Promise.resolve({
			success: true
		});
	}

	actionReady({}) {
		return Promise.resolve({
			success: true
		});
	}
}

module.exports = Qa;<|endoftext|>


<commit_before>'use strict';

var famous = require('famous');
var Consts = require('./Consts.js');

var Node = famous.core.Node;
var Position = famous.components.Position;
var DOMElement = famous.domRenderables.DOMElement;

function Score() {
	Node.call(this);

	// Center dot.
	this
		.setMountPoint(0, 0, 0)
		.setAlign(0.5, 0.5, 0)
		.setSizeMode('absolute', 'absolute', 'absolute')
		.setAbsoluteSize(Consts.DOT_SIDE * Consts.DIMENSION, Consts.DOT_SIDE * Consts.DIMENSION / 2, Consts.DOT_SIDE);

	this.score = 0;

	this.domElement = new DOMElement(this, {
		tagName: 'h2',
		classes: ['Scores'],
		properties: {
			color: '#fff',
			fontSize: '32px'
		},
		content: 'Score: <var class="Score">' + this.score + '</var>'
	});

	this.domElement.setAttribute('role', 'log');
	this.domElement.setAttribute('aria-live', 'polite');

	this.scoreSetContent = function scoreSetContent(value) {
		this.domElement.setContent('Score: <var class="Score">' + value + '</var>');
	}

	this.scoreInc = function scoreInc(inc) {
		this.score += inc;
		this.scoreSetContent(this.score);
	};

	this.scoreReset = function scoreReset() {
		this.score = 0;
		this.scoreSetContent(this.score);
	};

	this.scoreSurcharge = function scoreSurcharge() {
		this.score = Number.parseInt(this.score * Consts.SCORE__SURCHARGE);
		this.scoreSetContent(this.score);
	};

	this.position = new Position(this);
}

Score.prototype = Object.create(Node.prototype);
Score.prototype.constructor = Score;

module.exports = Score;
<commit_msg>Remove 3rd dimension<commit_after>'use strict';

var famous = require('famous');
var Consts = require('./Consts.js');

var Node = famous.core.Node;
var Position = famous.components.Position;
var DOMElement = famous.domRenderables.DOMElement;

function Score() {
	Node.call(this);

	// Center dot.
	this
		.setMountPoint(0, 0)
		.setAlign(0.5, 0.5)
		.setSizeMode('absolute', 'absolute')
		.setAbsoluteSize(Consts.DOT_SIDE * Consts.DIMENSION, Consts.DOT_SIDE * Consts.DIMENSION / 2);

	this.score = 0;

	this.domElement = new DOMElement(this, {
		tagName: 'h2',
		classes: ['Scores'],
		properties: {
			color: '#fff',
			fontSize: '32px'
		},
		content: 'Score: <var class="Score">' + this.score + '</var>'
	});

	this.domElement.setAttribute('role', 'log');
	this.domElement.setAttribute('aria-live', 'polite');

	this.scoreSetContent = function scoreSetContent(value) {
		this.domElement.setContent('Score: <var class="Score">' + value + '</var>');
	}

	this.scoreInc = function scoreInc(inc) {
		this.score += inc;
		this.scoreSetContent(this.score);
	};

	this.scoreReset = function scoreReset() {
		this.score = 0;
		this.scoreSetContent(this.score);
	};

	this.scoreSurcharge = function scoreSurcharge() {
		this.score = Number.parseInt(this.score * Consts.SCORE__SURCHARGE);
		this.scoreSetContent(this.score);
	};

	this.position = new Position(this);
}

Score.prototype = Object.create(Node.prototype);
Score.prototype.constructor = Score;

module.exports = Score;
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2020
 */
import Bugsnag from '@bugsnag/react-native';
import AsyncStorage from '@react-native-community/async-storage';
import { persistStore, persistReducer } from 'redux-persist';
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';

import { navigationMiddleware } from './navigation';
import reducers from './reducers';

const persistConfig = {
  keyPrefix: '',
  key: 'root',
  storage: AsyncStorage,
  blacklist: [
    'pages',
    'user',
    'prescription',
    'patient',
    'finalise',
    'form',
    'prescriber',
    'wizard',
    'payment',
    'insurance',
    'dashboard',
    'dispensary',
    'modules',
    'supplierCredit',
    'fridge',
    'breach',
    'rowDetail',
    'permission',
    'cashTransaction',
    'vaccine',
    'newSensor',
    'sensorDetail',
    'entities',
  ],
};

const persistedReducer = persistReducer(persistConfig, reducers);

const bugsnagMiddleware = () => next => action => {
  const { type = 'No action type!' } = action;
  Bugsnag.leaveBreadcrumb(type);

  next(action);
};

const store = createStore(
  persistedReducer,
  {},
  applyMiddleware(thunk, navigationMiddleware, bugsnagMiddleware)
);

const persistedStore = persistStore(store);

export { store, persistedStore };
<commit_msg>Remove newSensor from blacklist<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2020
 */
import Bugsnag from '@bugsnag/react-native';
import AsyncStorage from '@react-native-community/async-storage';
import { persistStore, persistReducer } from 'redux-persist';
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';

import { navigationMiddleware } from './navigation';
import reducers from './reducers';

const persistConfig = {
  keyPrefix: '',
  key: 'root',
  storage: AsyncStorage,
  blacklist: [
    'pages',
    'user',
    'prescription',
    'patient',
    'finalise',
    'form',
    'prescriber',
    'wizard',
    'payment',
    'insurance',
    'dashboard',
    'dispensary',
    'modules',
    'supplierCredit',
    'fridge',
    'breach',
    'rowDetail',
    'permission',
    'cashTransaction',
    'vaccine',
    'sensorDetail',
    'entities',
  ],
};

const persistedReducer = persistReducer(persistConfig, reducers);

const bugsnagMiddleware = () => next => action => {
  const { type = 'No action type!' } = action;
  Bugsnag.leaveBreadcrumb(type);

  next(action);
};

const store = createStore(
  persistedReducer,
  {},
  applyMiddleware(thunk, navigationMiddleware, bugsnagMiddleware)
);

const persistedStore = persistStore(store);

export { store, persistedStore };
<|endoftext|>


<commit_before>import JSONSerializer from './Serializers/JSONSerializer';

/**
 * A decorator for a Storage implementation
 *
 * @author Sean Tymon <tymon148@gmail.com>
 */
class Store {

    /**
     * Create a Store instance.
     *
     * @param   {Storage}  driver      The storage driver
     * @param   {Object}   serializer  The serializer to use
     *
     * @return  {void}
     */
    constructor (driver, serializer) {
        this.driver = driver;
        this.serializer = serializer || JSONSerializer;
    }

    /**
     * Add a key/value pair to storage
     *
     * @param  {String}  key  The key to add
     * @param  {Mixed}   val  The value to add
     *
     * @throws {Error}  If the storage access is disallowed.
     *
     * @return {Store}
     */
    setItem (key, val) {
        try {
            this.driver.setItem(key, this.serializer.serialize(val));
        } catch (e) {
            if (['QUOTA_EXCEEDED_ERR',
                'NS_ERROR_DOM_QUOTA_REACHED',
                'QuotaExceededError'].indexOf(e.name) !== -1) {
                throw new Error('The Storage quota has been exceeded');
            } else {
                throw new Error(`Could not add item with key "${key}"`);
            }
        }

        return this;
    }

    /**
     * Retrieve a value from storage by key
     *
     * @param  {String}  key  The key to retreive
     *
     * @return {Mixed}
     */
    getItem (key) {
        return this.serializer.unserialize(this.driver.getItem(key));
    }

    /**
     * Determine whether the key exists in storage.
     *
     * @param   {String}   key  The key to check
     *
     * @return  {Boolean}
     */
    hasItem (key) {
        return this.driver.hasOwnProperty(key);
    }

    /**
     * Remove an item from storage.
     *
     * @param   {String}  key  The key to remove
     *
     * @return  {Boolean}
     */
    removeItem (key) {
        if (! this.hasItem(key)) return false;

        this.driver.removeItem(key);

        return true;
    }

    /**
     * Remove all items from storage.
     *
     * @return  {Store}
     */
    clear () {
        this.driver.clear();

        return this;
    }

    /**
     * Iterate through the items in storage and execute the callback.
     *
     * @param   {Function}  callback            The callback function
     * @param   {Object}    [thisContext=this]  The context of this keyword
     *
     * @return  {void}
     */
    forEach (callback, thisContext = this) {
        for (let item in this.driver) {
            callback.call(thisContext, this.driver[item], item);
        }
    }

    /**
     * Determine whether the storage driver is supported.
     *
     * @return  {Boolean}
     */
    isSupported () {
        let l = 'l';
        try {
            this.driver.setItem(l, l);
            this.driver.removeItem(l);

            return true;
        } catch (e) {
            return false;
        }
    }

    /**
     * Get the number of items in storage.
     *
     * @return  {Integer}
     */
    get length () {
        return this.driver.length;
    }

}

export default Store;
<commit_msg>fix docs<commit_after>import JSONSerializer from './Serializers/JSONSerializer';

/**
 * A decorator for a Storage implementation
 *
 * @author Sean Tymon <tymon148@gmail.com>
 */
class Store {

    /**
     * Create a Store instance.
     *
     * @param   {Storage}  driver        The storage driver
     * @param   {Object}   [serializer]  The serializer to use
     *
     * @return  {void}
     */
    constructor (driver, serializer) {
        this.driver = driver;
        this.serializer = serializer || JSONSerializer;
    }

    /**
     * Add a key/value pair to storage
     *
     * @param  {String}  key  The key to add
     * @param  {Mixed}   val  The value to add
     *
     * @throws {Error}  If the storage access is disallowed.
     *
     * @return {Store}
     */
    setItem (key, val) {
        try {
            this.driver.setItem(key, this.serializer.serialize(val));
        } catch (e) {
            if (['QUOTA_EXCEEDED_ERR',
                'NS_ERROR_DOM_QUOTA_REACHED',
                'QuotaExceededError'].indexOf(e.name) !== -1) {
                throw new Error('The Storage quota has been exceeded');
            } else {
                throw new Error(`Could not add item with key "${key}"`);
            }
        }

        return this;
    }

    /**
     * Retrieve a value from storage by key
     *
     * @param  {String}  key  The key to retreive
     *
     * @return {Mixed}
     */
    getItem (key) {
        return this.serializer.unserialize(this.driver.getItem(key));
    }

    /**
     * Determine whether the key exists in storage.
     *
     * @param   {String}   key  The key to check
     *
     * @return  {Boolean}
     */
    hasItem (key) {
        return this.driver.hasOwnProperty(key);
    }

    /**
     * Remove an item from storage.
     *
     * @param   {String}  key  The key to remove
     *
     * @return  {Boolean}
     */
    removeItem (key) {
        if (! this.hasItem(key)) return false;

        this.driver.removeItem(key);

        return true;
    }

    /**
     * Remove all items from storage.
     *
     * @return  {Store}
     */
    clear () {
        this.driver.clear();

        return this;
    }

    /**
     * Iterate through the items in storage and execute the callback.
     *
     * @param   {Function}  callback            The callback function
     * @param   {Object}    [thisContext=this]  The context of this keyword
     *
     * @return  {void}
     */
    forEach (callback, thisContext = this) {
        for (let item in this.driver) {
            callback.call(thisContext, this.driver[item], item);
        }
    }

    /**
     * Determine whether the storage driver is supported.
     *
     * @return  {Boolean}
     */
    isSupported () {
        let l = 'l';
        try {
            this.driver.setItem(l, l);
            this.driver.removeItem(l);

            return true;
        } catch (e) {
            return false;
        }
    }

    /**
     * Get the number of items in storage.
     *
     * @return  {Integer}
     */
    get length () {
        return this.driver.length;
    }

}

export default Store;
<|endoftext|>


<commit_before>/* jslint node:true */

'use strict';

exports = module.exports = {
    getAll: getAll,
    get: get,
    add: add,
    put: put,
    del: del,
    exp: exp,
    imp: imp,
    publicLink: publicLink,
    getByShareId: getByShareId,
    extractURLs: extractURLs,
    extractTags: extractTags,
    facelift: facelift,
    cleanupTags: cleanupTags,

    TYPE_IMAGE: 'image',
    TYPE_UNKNOWN: 'unknown'
};

var async = require('async'),
    url = require('url'),
    tags = require('./database/tags.js'),
    things = require('./database/things.js'),
    shares = require('./database/shares.js'),
    superagent = require('superagent');

var GET_URL = new RegExp('(^|[ \t\r\n])((ftp|http|https|gopher|mailto|news|nntp|telnet|wais|file|prospero|aim|webcal):(([A-Za-z0-9$_.+!*(),;/?:@&~=-])|%[A-Fa-f0-9]{2}){2,}(#([a-zA-Z0-9$_.+!*(),;/?:@&~=%-]*))?([A-Za-z0-9$_+!*();/?:~-]))', 'g');
var PRETTY_URL_LENGTH = 40;

function extractURLs(content) {
    var lines = content.split('\n');
    var urls = [];

    lines.forEach(function (line) {
        var tmp = line.match(GET_URL);
        if (tmp === null) return;

        urls = urls.concat(tmp.map(function (url) {
            return url.trim();
        }));
    });

    return urls.filter(function (item, pos, self) {
        return self.indexOf(item) === pos;
    });
}

function escapeRegExp(str) {
    return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}

function extractTags(content) {
    var tagObjects = [];

    // first replace all urls which might contain # with placeholders
    var urls = extractURLs(content);
    urls.forEach(function (u) {
        content = content.replace(new RegExp(escapeRegExp(u), 'gmi'), ' --URL_PLACEHOLDER-- ');
    });

    var lines = content.split('\n');
    lines.forEach(function (line) {
        var tmp = line.match(/#([\u00C0-\u017Fa-zA-Z]+)/g);
        if (tmp === null) return;

        tagObjects = tagObjects.concat(tmp.map(function (tag) {
            return tag.slice(1).toLowerCase();
        }));
    });

    return tagObjects;
}

function extractExternalContent(content, callback) {
    var urls = extractURLs(content);
    var externalContent = [];

    async.each(urls, function (url, callback) {
        superagent.get(url).end(function (error, result) {
            var obj = { url: url, type: exports.TYPE_UNKNOWN };

            if (error) {
                console.log('[WARN] failed to fetch external content %s', url);
            } else {
                if (result.type.indexOf('image/') === 0) {
                    obj = { url: url, type: exports.TYPE_IMAGE };
                }

                console.log('[INFO] external content type %s - %s', obj.type, obj.url);
            }

            externalContent.push(obj);

            callback(null);
        });
    }, function () {
        callback(null, externalContent);
    });
}

function facelift(userId, thing, callback) {
    var data = thing.content;
    var tagObjects = thing.tags;
    var externalContent = thing.externalContent;
    var attachments = thing.attachments || [];

    function wrapper() {

        // Enrich with tag links
        tagObjects.forEach(function (tag) {
            data = data.replace(new RegExp('#' + tag + '(#|\\s|$)', 'gmi'), '[#' + tag + '](#search?#' + tag + ')$1').trim();
        });

        // Enrich with image links
        externalContent.forEach(function (obj) {
            if (obj.type === exports.TYPE_IMAGE) {
                data = data.replace(new RegExp(escapeRegExp(obj.url), 'gmi'), '![' + obj.url + '](' + obj.url + ')');
            } else {
                // make urls look prettier
                var tmp = url.parse(obj.url);

                var pretty = obj.url.slice(tmp.protocol.length + 2);
                if (pretty.length > PRETTY_URL_LENGTH) pretty = pretty.slice(0, PRETTY_URL_LENGTH) + '...';

                data = data.replace(new RegExp(escapeRegExp(obj.url), 'gmi'), '[' + pretty + '](' + obj.url + ')');
            }
        });

        // Enrich with attachments
        attachments.forEach(function (a) {
            if (a.type === exports.TYPE_IMAGE) {
                data = data.replace(new RegExp('\\[' + a.fileName + '\\]', 'gmi'), '![/api/files/' + a.identifier + '](/api/files/' + a.identifier + ')');
            } else {
                data = data.replace(new RegExp('\\[' + a.fileName + '\\]', 'gmi'), '[/api/files/' + a.identifier + '](/api/files/' + a.identifier + ')');
            }
        });

        callback(null, data);
    }

    if (Array.isArray(externalContent)) return wrapper();

    // old entry extract external content first
    extractExternalContent(thing.content, function (error, result) {
        if (error) {
            console.error('Failed to extract external content:', error);

            externalContent = [];

            return wrapper();
        }

        // set for wrapper()
        externalContent = result;

        console.log('[INFO] update %s with new external content.', thing._id, result);

        things.put(userId, thing._id, thing.content, thing.tags, attachments, result, function (error) {
            if (error) console.error('Failed to update external content:', error);

            wrapper();
        });
    });
}

function getAll(userId, query, skip, limit, callback) {
    things.getAll(userId, query, skip, limit, function (error, result) {
        if (error) return callback(error);
        if (!result) return callback(null, []);

        async.each(result, function (thing, callback) {
            facelift(userId, thing, function (error, data) {
                if (error) console.error('Failed to facelift:', error);

                thing.attachments = thing.attachments || [];
                thing.richContent = data || thing.content;

                callback(null);
            });
        }, function () {
            callback(null, result);
        });
    });
}

function get(userId, id, callback) {
    things.get(userId, id, function (error, result) {
        if (error) return callback(error);
        if (result.length === 0) return callback(new Error('not found'));

        var thing = result[0];
        facelift(userId, thing, function (error, data) {
            if (error) console.error('Failed to facelift:', error);

            thing.attachments = thing.attachments || [];
            thing.richContent = data || thing.content;

            callback(null, thing);
        });
    });
}

function add(userId, content, attachments, callback) {
    extractExternalContent(content, function (error, result) {
        if (error) return callback(error);

        var doc = {
            content: content,
            createdAt: new Date(),
            modifiedAt: new Date(),
            tags: extractTags(content),
            externalContent: result,
            attachments: attachments
        };

        async.eachSeries(doc.tags, tags.update, function (error) {
            if (error) return callback(error);

            things.add(userId, doc.content, doc.tags, doc.attachments, doc.externalContent, function (error, result) {
                if (error) return callback(error);
                if (!result) return callback(new Error('no result returned'));

                callback(null, result);
            });
        });
    });
}

function put(userId, id, content, attachments, callback) {
    var tagObjects = extractTags(content);

    async.eachSeries(tagObjects, tags.update, function (error) {
        if (error) return callback(error);

        extractExternalContent(content, function (error, externalContent) {
            if (error) console.error('Failed to extract external content:', error);

            things.put(userId, id, content, tagObjects, attachments, externalContent, function (error) {
                if (error) return callback(error);

                get(userId, id, callback);
            });
        });
    });
}

function del(userId, id, callback) {
    things.del(userId, id, function (error) {
        if (error) return callback(error);
        callback(null);
    });
}

function exp(userId, callback) {
    things.getAllLean(userId, function (error, result) {
        if (error) return callback(error);
        if (!result) return (null, '');

        var out = result.map(function (thing) {
            return {
                createdAt: thing.createdAt,
                modifiedAt: thing.modifiedAt,
                content: thing.content,
                externalContent: thing.externalContent || [],
                attachments: thing.attachments || []
            };
        });

        callback(null, { things: out });
    });
}

function imp(userId, data, callback) {
    async.eachSeries(data.things, function (thing, next) {
        var tagObjects = extractTags(thing.content);

        async.eachSeries(tagObjects, tags.update, function (error) {
            if (error) return next(error);

            things.addFull(userId, thing.content, tagObjects, thing.attachments || [], thing.externalContent || [], thing.createdAt, thing.modifiedAt || thing.createdAt, function (error, result) {
                if (error) return next(error);
                if (!result) return next(new Error('no result returned'));

                next(null, result._id);
            });
        });
    }, callback);
}

function publicLink(userId, thingId, callback) {
    shares.add(userId, thingId, function (error, result) {
        if (error) return callback(error);
        if (!result) return callback(new Error('no result returned'));

        callback(null, result);
    });
}

function getByShareId(userId, shareId, callback) {
    shares.get(userId, shareId, function (error, result) {
        if (error) return callback(error);
        if (result.length === 0) return callback(new Error('not found'));

        get(userId, result.thingId, function (error, result) {
            if (error) return callback(error);

            callback(null, result);
        });
    });
}

function cleanupTags(userId) {
    things.getAllLean(userId, function (error, result) {
        if (error) return console.error(new Error(error));

        var tags = [];
        result.forEach(function (thing) {
            tags = tags.concat(extractTags(thing.content));
        });

        tags.get(userId, function (error, result) {
            if (error) return console.error(new Error(error));

            async.each(result, function (tag, callback) {
                if (tags.indexOf(tag.name) !== -1) return callback(null);

                console.log('Cleanup tag', tag.name);

                tags.del(userId, tag._id, callback);
            }, function (error) {
                if (error) console.error('Failed to cleanup tags:', error);
            });
        });
    });
}
<commit_msg>Fix up more copy'n'paste errors<commit_after>/* jslint node:true */

'use strict';

exports = module.exports = {
    getAll: getAll,
    get: get,
    add: add,
    put: put,
    del: del,
    exp: exp,
    imp: imp,
    publicLink: publicLink,
    getByShareId: getByShareId,
    extractURLs: extractURLs,
    extractTags: extractTags,
    facelift: facelift,
    cleanupTags: cleanupTags,

    TYPE_IMAGE: 'image',
    TYPE_UNKNOWN: 'unknown'
};

var async = require('async'),
    url = require('url'),
    tags = require('./database/tags.js'),
    things = require('./database/things.js'),
    shares = require('./database/shares.js'),
    superagent = require('superagent');

var GET_URL = new RegExp('(^|[ \t\r\n])((ftp|http|https|gopher|mailto|news|nntp|telnet|wais|file|prospero|aim|webcal):(([A-Za-z0-9$_.+!*(),;/?:@&~=-])|%[A-Fa-f0-9]{2}){2,}(#([a-zA-Z0-9$_.+!*(),;/?:@&~=%-]*))?([A-Za-z0-9$_+!*();/?:~-]))', 'g');
var PRETTY_URL_LENGTH = 40;

function extractURLs(content) {
    var lines = content.split('\n');
    var urls = [];

    lines.forEach(function (line) {
        var tmp = line.match(GET_URL);
        if (tmp === null) return;

        urls = urls.concat(tmp.map(function (url) {
            return url.trim();
        }));
    });

    return urls.filter(function (item, pos, self) {
        return self.indexOf(item) === pos;
    });
}

function escapeRegExp(str) {
    return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}

function extractTags(content) {
    var tagObjects = [];

    // first replace all urls which might contain # with placeholders
    var urls = extractURLs(content);
    urls.forEach(function (u) {
        content = content.replace(new RegExp(escapeRegExp(u), 'gmi'), ' --URL_PLACEHOLDER-- ');
    });

    var lines = content.split('\n');
    lines.forEach(function (line) {
        var tmp = line.match(/#([\u00C0-\u017Fa-zA-Z]+)/g);
        if (tmp === null) return;

        tagObjects = tagObjects.concat(tmp.map(function (tag) {
            return tag.slice(1).toLowerCase();
        }));
    });

    return tagObjects;
}

function extractExternalContent(content, callback) {
    var urls = extractURLs(content);
    var externalContent = [];

    async.each(urls, function (url, callback) {
        superagent.get(url).end(function (error, result) {
            var obj = { url: url, type: exports.TYPE_UNKNOWN };

            if (error) {
                console.log('[WARN] failed to fetch external content %s', url);
            } else {
                if (result.type.indexOf('image/') === 0) {
                    obj = { url: url, type: exports.TYPE_IMAGE };
                }

                console.log('[INFO] external content type %s - %s', obj.type, obj.url);
            }

            externalContent.push(obj);

            callback(null);
        });
    }, function () {
        callback(null, externalContent);
    });
}

function facelift(userId, thing, callback) {
    var data = thing.content;
    var tagObjects = thing.tags;
    var externalContent = thing.externalContent;
    var attachments = thing.attachments || [];

    function wrapper() {

        // Enrich with tag links
        tagObjects.forEach(function (tag) {
            data = data.replace(new RegExp('#' + tag + '(#|\\s|$)', 'gmi'), '[#' + tag + '](#search?#' + tag + ')$1').trim();
        });

        // Enrich with image links
        externalContent.forEach(function (obj) {
            if (obj.type === exports.TYPE_IMAGE) {
                data = data.replace(new RegExp(escapeRegExp(obj.url), 'gmi'), '![' + obj.url + '](' + obj.url + ')');
            } else {
                // make urls look prettier
                var tmp = url.parse(obj.url);

                var pretty = obj.url.slice(tmp.protocol.length + 2);
                if (pretty.length > PRETTY_URL_LENGTH) pretty = pretty.slice(0, PRETTY_URL_LENGTH) + '...';

                data = data.replace(new RegExp(escapeRegExp(obj.url), 'gmi'), '[' + pretty + '](' + obj.url + ')');
            }
        });

        // Enrich with attachments
        attachments.forEach(function (a) {
            if (a.type === exports.TYPE_IMAGE) {
                data = data.replace(new RegExp('\\[' + a.fileName + '\\]', 'gmi'), '![/api/files/' + a.identifier + '](/api/files/' + a.identifier + ')');
            } else {
                data = data.replace(new RegExp('\\[' + a.fileName + '\\]', 'gmi'), '[/api/files/' + a.identifier + '](/api/files/' + a.identifier + ')');
            }
        });

        callback(null, data);
    }

    if (Array.isArray(externalContent)) return wrapper();

    // old entry extract external content first
    extractExternalContent(thing.content, function (error, result) {
        if (error) {
            console.error('Failed to extract external content:', error);

            externalContent = [];

            return wrapper();
        }

        // set for wrapper()
        externalContent = result;

        console.log('[INFO] update %s with new external content.', thing._id, result);

        things.put(userId, thing._id, thing.content, thing.tags, attachments, result, function (error) {
            if (error) console.error('Failed to update external content:', error);

            wrapper();
        });
    });
}

function getAll(userId, query, skip, limit, callback) {
    things.getAll(userId, query, skip, limit, function (error, result) {
        if (error) return callback(error);
        if (!result) return callback(null, []);

        async.each(result, function (thing, callback) {
            facelift(userId, thing, function (error, data) {
                if (error) console.error('Failed to facelift:', error);

                thing.attachments = thing.attachments || [];
                thing.richContent = data || thing.content;

                callback(null);
            });
        }, function () {
            callback(null, result);
        });
    });
}

function get(userId, id, callback) {
    things.get(userId, id, function (error, result) {
        if (error) return callback(error);

        facelift(userId, result, function (error, data) {
            if (error) console.error('Failed to facelift:', error);

            result.attachments = result.attachments || [];
            result.richContent = data || result.content;

            callback(null, result);
        });
    });
}

function add(userId, content, attachments, callback) {
    extractExternalContent(content, function (error, result) {
        if (error) return callback(error);

        var doc = {
            content: content,
            createdAt: new Date(),
            modifiedAt: new Date(),
            tags: extractTags(content),
            externalContent: result,
            attachments: attachments
        };

        async.eachSeries(doc.tags, tags.update, function (error) {
            if (error) return callback(error);

            things.add(userId, doc.content, doc.tags, doc.attachments, doc.externalContent, function (error, result) {
                if (error) return callback(error);
                if (!result) return callback(new Error('no result returned'));

                callback(null, result);
            });
        });
    });
}

function put(userId, id, content, attachments, callback) {
    var tagObjects = extractTags(content);

    async.eachSeries(tagObjects, tags.update, function (error) {
        if (error) return callback(error);

        extractExternalContent(content, function (error, externalContent) {
            if (error) console.error('Failed to extract external content:', error);

            things.put(userId, id, content, tagObjects, attachments, externalContent, function (error) {
                if (error) return callback(error);

                get(userId, id, callback);
            });
        });
    });
}

function del(userId, id, callback) {
    things.del(userId, id, function (error) {
        if (error) return callback(error);
        callback(null);
    });
}

function exp(userId, callback) {
    things.getAllLean(userId, function (error, result) {
        if (error) return callback(error);
        if (!result) return (null, '');

        var out = result.map(function (thing) {
            return {
                createdAt: thing.createdAt,
                modifiedAt: thing.modifiedAt,
                content: thing.content,
                externalContent: thing.externalContent || [],
                attachments: thing.attachments || []
            };
        });

        callback(null, { things: out });
    });
}

function imp(userId, data, callback) {
    async.eachSeries(data.things, function (thing, next) {
        var tagObjects = extractTags(thing.content);

        async.eachSeries(tagObjects, tags.update, function (error) {
            if (error) return next(error);

            things.addFull(userId, thing.content, tagObjects, thing.attachments || [], thing.externalContent || [], thing.createdAt, thing.modifiedAt || thing.createdAt, function (error, result) {
                if (error) return next(error);
                if (!result) return next(new Error('no result returned'));

                next(null, result._id);
            });
        });
    }, callback);
}

function publicLink(userId, thingId, callback) {
    shares.add(userId, thingId, function (error, result) {
        if (error) return callback(error);
        if (!result) return callback(new Error('no result returned'));

        callback(null, result);
    });
}

function getByShareId(userId, shareId, callback) {
    shares.get(userId, shareId, function (error, result) {
        if (error) return callback(error);
        if (result.length === 0) return callback(new Error('not found'));

        get(userId, result.thingId, function (error, result) {
            if (error) return callback(error);

            callback(null, result);
        });
    });
}

function cleanupTags(userId) {
    things.getAllLean(userId, function (error, result) {
        if (error) return console.error(new Error(error));

        var tags = [];
        result.forEach(function (thing) {
            tags = tags.concat(extractTags(thing.content));
        });

        tags.get(userId, function (error, result) {
            if (error) return console.error(new Error(error));

            async.each(result, function (tag, callback) {
                if (tags.indexOf(tag.name) !== -1) return callback(null);

                console.log('Cleanup tag', tag.name);

                tags.del(userId, tag._id, callback);
            }, function (error) {
                if (error) console.error('Failed to cleanup tags:', error);
            });
        });
    });
}
<|endoftext|>


<commit_before>var paths = function (packageName) {
  'use strict';

  if (typeof packageName === 'undefined') {
    throw new Error('packageName is undefined');
  }

  const _ = require('lodash');
  const path = require('path');
  const slash = require('slash');

  // External Paths:
  // Paths used for writing files and navigating the package source
  const rootDirectory = path.resolve(__dirname, '../');
  const widgetsDirectory = path.join(rootDirectory, '/widgets');
  const componentsDirectory = path.join(rootDirectory, 'src/components/');

  // Internal Paths:
  // Paths used inside themes, schemes, widgets, etc.
  // Relative to the Sublime Text Packages directory
  const packagesRoot = `Packages/${packageName}/`;
  const assetsDirectory = `${packageName}/assets/`;
  const commonsDirectory = `${assetsDirectory}commons/`;

  const paths = {
    external: {
      root: rootDirectory,
      widgets: widgetsDirectory,
      components: componentsDirectory,
    },
    internal: {
      packagesRoot: packagesRoot,
      assets: assetsDirectory,
      commons: commonsDirectory,
      widgets: `${packagesRoot}widgets/`,
    },
  };

  // Sublime Text expects forward-slashed paths
  paths.internal = _.mapValues(paths.internal, p => slash(p));

  return paths;
};

module.exports = paths;
<commit_msg>Removed unnecessary call to slash in paths<commit_after>var paths = function (packageName) {
  'use strict';

  if (typeof packageName === 'undefined') {
    throw new Error('packageName is undefined');
  }

  const path = require('path');

  // External Paths:
  // Paths used for writing files and navigating the package source
  const rootDirectory = path.resolve(__dirname, '../');
  const widgetsDirectory = path.join(rootDirectory, '/widgets');
  const componentsDirectory = path.join(rootDirectory, 'src/components/');

  // Internal Paths:
  // Paths used inside themes, schemes, widgets, etc.
  // Relative to the Sublime Text Packages directory
  //
  // Sublime Text expects forward-slashed paths
  const packagesRoot = `Packages/${packageName}/`;
  const assetsDirectory = `${packageName}/assets/`;
  const commonsDirectory = `${assetsDirectory}commons/`;

  const paths = {
    external: {
      root: rootDirectory,
      widgets: widgetsDirectory,
      components: componentsDirectory,
    },
    internal: {
      packagesRoot: packagesRoot,
      assets: assetsDirectory,
      commons: commonsDirectory,
      widgets: `${packagesRoot}widgets/`,
    },
  };

  return paths;
};

module.exports = paths;
<|endoftext|>