<commit_before>(function() {

  var Group = Two.Group = function(o) {

    Two.Shape.call(this, true);

    delete this.stroke;
    delete this.fill;
    delete this.linewidth;
    delete this.opacity;

    delete this.cap;
    delete this.join;
    delete this.miter;

    Group.MakeGetterSetter(this, Two.Shape.Properties);

    this.children = {};

  };

  _.extend(Group, {

    MakeGetterSetter: function(group, properties) {

      if (!_.isArray(properties)) {
        properties = [properties];
      }

      _.each(properties, function(k) {

        var secret = '_' + k;

        Object.defineProperty(group, k, {
          get: function() {
            return this[secret];
          },
          set: function(v) {
            this[secret] = v;
            _.each(this.children, function(child) { // Trickle down styles
              child[k] = v;
            });
          }
        });

      });

    }

  });

  _.extend(Group.prototype, Two.Shape.prototype, {

    /**
     * Group has a gotcha in that it's at the moment required to be bound to
     * an instance of two in order to add elements correctly. This needs to
     * be rethought and fixed.
     */
    clone: function(parent) {

      var parent = parent || this.parent;

      var children = _.map(this.children, function(child) {
        return child.clone(parent);
      });

      var group = new Group();
      parent.add(group);
      group.add(children);

      group.translation.copy(this.translation);
      group.rotation = this.rotation;
      group.scale = this.scale;

      return group;

    },

    /**
     * Anchors all children around the center of the group.
     */
    center: function() {

      var rect = this.getBoundingClientRect();

      rect.centroid = {
        x: rect.left + rect.width / 2,
        y: rect.top + rect.height / 2
      };

      _.each(this.children, function(child) {
        child.translation.subSelf(rect.centroid);
      });

      this.translation.copy(rect.centroid);

      return this;

    },

    /**
     * Add an object to the group.
     */
    add: function(o) {

      var l = arguments.length,
        objects = o,
        children = this.children,
        grandparent = this.parent,
        ids = [];

      if (!_.isArray(o)) {
        objects = _.toArray(arguments);
      }

      // A bubbled up version of 'change' event for the children.

      var broadcast = _.bind(function(id, property, value, closed, curved, strokeChanged) {
        this.trigger(Two.Events.change, id, property, value, closed, curved, strokeChanged);
      }, this);

      // Add the objects

      _.each(objects, function(object) {

        var id = object.id, parent = object.parent;

        if (_.isUndefined(id)) {
          grandparent.add(object);
          id = object.id;
        }

        if (_.isUndefined(children[id])) {
          // Release object from previous parent.
          if (parent) {
            delete parent.children[id];
          }
          // Add it to this group and update parent-child relationship.
          children[id] = object;
          object.parent = this;
          object.unbind(Two.Events.change)
            .bind(Two.Events.change, broadcast);
          ids.push(id);
        }

      }, this);

      if (ids.length > 0) {
        this.trigger(Two.Events.change, this.id, Two.Properties.hierarchy, ids);
      }

      return this;
      // return this.center();

    },

    /**
     * Remove an object from the group.
     */
    remove: function(o) {

      var l = arguments.length,
        objects = o,
        children = this.children,
        grandparent = this.parent,
        ids = [];

      if (l <= 0 && grandparent) {
        grandparent.remove(this);
        return this;
      }

      if (!_.isArray(o)) {
        objects = _.toArray(arguments);
      }

      _.each(objects, function(object) {

        var id = object.id, grandchildren = object.children;

        if (!(id in children)) {
          return;
        }

        delete children[id];
        object.unbind(Two.Events.change);

        ids.push(id);

      });

      if (ids.length > 0) {
        this.trigger(Two.Events.change, this.id, Two.Properties.demotion, ids);
      }

      return this;
      // return this.center();

    },

    /**
     * Return an object with top, left, right, bottom, width, and height
     * parameters of the group.
     */
    getBoundingClientRect: function() {

      var left = Infinity, right = -Infinity,
        top = Infinity, bottom = -Infinity;

      _.each(this.children, function(child) {

        var rect = child.getBoundingClientRect();

        top = Math.min(rect.top, top);
        left = Math.min(rect.left, left);
        right = Math.max(rect.right, right);
        bottom = Math.max(rect.bottom, bottom);

      }, this);

      var ul = this._matrix.multiply(left, top, 1);
      var ll = this._matrix.multiply(right, bottom, 1);

      return {
        top: ul.y,
        left: ul.x,
        right: ll.x,
        bottom: ll.y,
        width: ll.x - ul.x,
        height: ll.y - ul.y
      };

    },

    /**
     * Trickle down of noFill
     */
    noFill: function() {
      _.each(this.children, function(child) {
        child.noFill();
      });
      return this;
    },

    /**
     * Trickle down of noStroke
     */
    noStroke: function() {
      _.each(this.children, function(child) {
        child.noStroke();
      });
      return this;
    }

  });

})();<commit_msg>Skip getBoundingClientRect of children if it doesn\'t exist.<commit_after>(function() {

  var Group = Two.Group = function(o) {

    Two.Shape.call(this, true);

    delete this.stroke;
    delete this.fill;
    delete this.linewidth;
    delete this.opacity;

    delete this.cap;
    delete this.join;
    delete this.miter;

    Group.MakeGetterSetter(this, Two.Shape.Properties);

    this.children = {};

  };

  _.extend(Group, {

    MakeGetterSetter: function(group, properties) {

      if (!_.isArray(properties)) {
        properties = [properties];
      }

      _.each(properties, function(k) {

        var secret = '_' + k;

        Object.defineProperty(group, k, {
          get: function() {
            return this[secret];
          },
          set: function(v) {
            this[secret] = v;
            _.each(this.children, function(child) { // Trickle down styles
              child[k] = v;
            });
          }
        });

      });

    }

  });

  _.extend(Group.prototype, Two.Shape.prototype, {

    /**
     * Group has a gotcha in that it's at the moment required to be bound to
     * an instance of two in order to add elements correctly. This needs to
     * be rethought and fixed.
     */
    clone: function(parent) {

      var parent = parent || this.parent;

      var children = _.map(this.children, function(child) {
        return child.clone(parent);
      });

      var group = new Group();
      parent.add(group);
      group.add(children);

      group.translation.copy(this.translation);
      group.rotation = this.rotation;
      group.scale = this.scale;

      return group;

    },

    /**
     * Anchors all children around the center of the group.
     */
    center: function() {

      var rect = this.getBoundingClientRect();

      rect.centroid = {
        x: rect.left + rect.width / 2,
        y: rect.top + rect.height / 2
      };

      _.each(this.children, function(child) {
        child.translation.subSelf(rect.centroid);
      });

      this.translation.copy(rect.centroid);

      return this;

    },

    /**
     * Add an object to the group.
     */
    add: function(o) {

      var l = arguments.length,
        objects = o,
        children = this.children,
        grandparent = this.parent,
        ids = [];

      if (!_.isArray(o)) {
        objects = _.toArray(arguments);
      }

      // A bubbled up version of 'change' event for the children.

      var broadcast = _.bind(function(id, property, value, closed, curved, strokeChanged) {
        this.trigger(Two.Events.change, id, property, value, closed, curved, strokeChanged);
      }, this);

      // Add the objects

      _.each(objects, function(object) {

        var id = object.id, parent = object.parent;

        if (_.isUndefined(id)) {
          grandparent.add(object);
          id = object.id;
        }

        if (_.isUndefined(children[id])) {
          // Release object from previous parent.
          if (parent) {
            delete parent.children[id];
          }
          // Add it to this group and update parent-child relationship.
          children[id] = object;
          object.parent = this;
          object.unbind(Two.Events.change)
            .bind(Two.Events.change, broadcast);
          ids.push(id);
        }

      }, this);

      if (ids.length > 0) {
        this.trigger(Two.Events.change, this.id, Two.Properties.hierarchy, ids);
      }

      return this;
      // return this.center();

    },

    /**
     * Remove an object from the group.
     */
    remove: function(o) {

      var l = arguments.length,
        objects = o,
        children = this.children,
        grandparent = this.parent,
        ids = [];

      if (l <= 0 && grandparent) {
        grandparent.remove(this);
        return this;
      }

      if (!_.isArray(o)) {
        objects = _.toArray(arguments);
      }

      _.each(objects, function(object) {

        var id = object.id, grandchildren = object.children;

        if (!(id in children)) {
          return;
        }

        delete children[id];
        object.unbind(Two.Events.change);

        ids.push(id);

      });

      if (ids.length > 0) {
        this.trigger(Two.Events.change, this.id, Two.Properties.demotion, ids);
      }

      return this;
      // return this.center();

    },

    /**
     * Return an object with top, left, right, bottom, width, and height
     * parameters of the group.
     */
    getBoundingClientRect: function() {

      var left = Infinity, right = -Infinity,
        top = Infinity, bottom = -Infinity;

      _.each(this.children, function(child) {

        var rect = child.getBoundingClientRect();

        if (!top || !left || !right || !bottom) {
          return;
        }

        top = Math.min(rect.top, top);
        left = Math.min(rect.left, left);
        right = Math.max(rect.right, right);
        bottom = Math.max(rect.bottom, bottom);

      }, this);

      var ul = this._matrix.multiply(left, top, 1);
      var ll = this._matrix.multiply(right, bottom, 1);

      return {
        top: ul.y,
        left: ul.x,
        right: ll.x,
        bottom: ll.y,
        width: ll.x - ul.x,
        height: ll.y - ul.y
      };

    },

    /**
     * Trickle down of noFill
     */
    noFill: function() {
      _.each(this.children, function(child) {
        child.noFill();
      });
      return this;
    },

    /**
     * Trickle down of noStroke
     */
    noStroke: function() {
      _.each(this.children, function(child) {
        child.noStroke();
      });
      return this;
    }

  });

})();
<|endoftext|>


<commit_before>import React from 'react'
import {smoothScroll} from './mixin/smoothScroll'
import {RecordBtn, PlayBtn, QuestionBtn} from './button.js'
const styles = {
    panel : {
        zIndex: '9999',
        position:'fixed',
        display:'flex',
        justifyContent:'center',
        alignItems:'center',
        right:'10%',
        bottom:'10%',
        fontWeight:'bold',
        color:'#FFF',
        cursor:'pointer',
        height:'3em',
        width:'3em',
        borderRadius:'50%',
        background:'rgb(0, 188, 212)',
        boxShadow:'0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)',
    },
}

export default class Guide extends React.Component{
    constructor(props){
        super(props);
        let actionLog = [];
        this.state = {
            record: false,
        }
        this._handleScrollEvent = this._handleScrollEvent.bind(this);
        this._handleClickEvent = this._handleClickEvent.bind(this);
        this._handleMouseDown = this._handleMouseDown.bind(this);
        this._handleMouseMove = this._handleMouseMove.bind(this);
        this._currentPosition = this._currentPosition.bind(this);
        this._startRecord = this._startRecord.bind(this);
        this._stopRecord = this._stopRecord.bind(this);
        this._addListen = this._addListen.bind(this);
        this._removeListen = this._removeListen.bind(this);
        this._replay = this._replay.bind(this);
        this._getCurrentTime = this._getCurrentTime.bind(this);
        this.panelEvent = this.panelEvent.bind(this);
    }
    componentDidMount() {

    }
    _getCurrentTime() {
      let currentTime = new Date();
      return `${currentTime.getMinutes()}:${currentTime.getSeconds()}`;
    }
    _handleClickEvent(e) {
      this.actionLog.push({click: [e.target, this._getCurrentTime()]});
    }
    _handleMouseDown(e) {
      console.log(e);
    }
    _handleMouseMove(e) {
      console.log(`Mouse Move! \n ${e}`);
    }
    /* Add and Remove event listener
     *
     */
    _addListen() {
        this.actionLog = [];
        window.addEventListener('click', this._handleClickEvent);
        window.addEventListener('scroll',this._handleScrollEvent);
        window.addEventListener('mousedown',this._handleMouseDown);
        // maybe we should only listen on move after mousedown
        window.addEventListener('mousemove',this._handleMouseMove);
    }
    _removeListen() {
        window.removeEventListener('click', this._handleClickEvent);
        window.removeEventListener('scroll',this._handleScrollEvent);
    }
    _handleScrollEvent(){
        let currentTop = this._currentPosition();
        let currentTime = new Date();
        this.actionLog.push({scroll: [currentTop, this._getCurrentTime()]});
    }
    _currentPosition() {
        let doc = document.documentElement;
        let top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
        return top
    }
    _startRecord (){
        this.setState({record: true});
    }
    _stopRecord (){
        this.setState({record: false});
    }

    _replay () {
        let preStepTime = 0;
        const _scrollFilter = (action) => {
            return action.hasOwnProperty('scroll');
        }
        for (let key in this.actionLog) {
          let action = this.actionLog[key];

          if ( action.hasOwnProperty('scroll')) {
           // Need to calculate time with timestamp
            // to get the real simulation of website owner behavior
            /*
            let prePosition = key > 1 ? this.actionLog[key-1]['scroll'][0] :
                                        this.actionLog.filter(_scrollFilter);
            */
            ( (offset, time) => {
                setTimeout( ()=>window.scrollTo(0, offset), time);
                preStepTime = time;
            })(action['scroll'][0], preStepTime + 20);
          }
          else if (action.hasOwnProperty('click')) {
            // Ignore start record step
            if (key != 0 ) {
              const clickEvent = this._createSimulatedClick();
              ( (target, time, e) => {
                setTimeout( ()=> target.dispatchEvent(e), time);
                preStepTime = time;
              })(action['click'][0], preStepTime + (400) , clickEvent);
            }
          }
          else if (action.hasOwnProperty('halt')) {
            preStepTime += 200;
          }
        }
    }

    _createSimulatedClick() {
      const e = document.createEvent('MouseEvent');
      e.initMouseEvent('click',true,true);
      return e
    }
    panelEvent() {
        this.refs.playBtn.openBtn();
        this.refs.recordBtn.openBtn();
        this.refs.qBtn.openBtn();
    }
    render(){
        if (this.state.record) {
            this._addListen();
        }
        else {
            this._removeListen();
        }
        let recordBtn = !this.state.record ? <RecordBtn ref="recordBtn" handleRecord={this._startRecord}/>: <RecordBtn ref="recordBtn" handleRecord={this._stopRecord}/>;

        return

                  G

                  {recordBtn}
                  <PlayBtn ref="playBtn" handleReplay={this._replay}/>
                  <QuestionBtn ref="qBtn" />

    }
}
<commit_msg>mouse down up move done<commit_after>import React from 'react'
import {smoothScroll} from './mixin/smoothScroll'
import {RecordBtn, PlayBtn, QuestionBtn} from './button.js'
const styles = {
    panel : {
        zIndex: '9999',
        position:'fixed',
        display:'flex',
        justifyContent:'center',
        alignItems:'center',
        right:'10%',
        bottom:'10%',
        fontWeight:'bold',
        color:'#FFF',
        cursor:'pointer',
        height:'3em',
        width:'3em',
        borderRadius:'50%',
        background:'rgb(0, 188, 212)',
        boxShadow:'0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)',
    },
}

export default class Guide extends React.Component{
    constructor(props){
        super(props);
        let actionLog = [];
        this.state = {
            record: false,
        }
        this._handleScrollEvent = this._handleScrollEvent.bind(this);
        this._handleClickEvent = this._handleClickEvent.bind(this);
        this._handleMouseDown = this._handleMouseDown.bind(this);
        this._handleMouseMove = this._handleMouseMove.bind(this);
        this._handleMouseUp = this._handleMouseUp.bind(this);
        this._currentPosition = this._currentPosition.bind(this);
        this._startRecord = this._startRecord.bind(this);
        this._stopRecord = this._stopRecord.bind(this);
        this._addListen = this._addListen.bind(this);
        this._removeListen = this._removeListen.bind(this);
        this._replay = this._replay.bind(this);
        this._getCurrentTime = this._getCurrentTime.bind(this);
        this.panelEvent = this.panelEvent.bind(this);
        this._createSimulatedMouseEvent = this._createSimulatedMouseEvent.bind(this);
    }
    componentDidMount() {

    }
    _getCurrentTime() {
      let currentTime = new Date();
      return `${currentTime.getMinutes()}:${currentTime.getSeconds()}`;
    }
    /*  Handle event put here !
        actionLog[event].push([param1, param2, param3 ])
        event: type of action
        param1: target
        param2: time
        param3: additional info
    */

    _handleScrollEvent(){
        let currentTop = this._currentPosition();
        let currentTime = new Date();
        this.actionLog.push({scroll: [currentTop, this._getCurrentTime()]});
    }
    _handleClickEvent(e) {
      this.actionLog.push({click: [e.target, this._getCurrentTime()]});
    }
    _handleMouseDown(e) {
      this.actionLog.push({mousedown: [e.target, this._getCurrentTime()]});
    }
    _handleMouseMove(e) {
      this.actionLog.push({mousemove: [e.target, this._getCurrentTime(),
        {cx: e.clientX, cy:e.clientY, sx:e.screenX, sy:e.screenY}]});
    }
    _handleMouseUp(e) {
      this.actionLog.push({mouseup: [e.target, this._getCurrentTime()]});
    }
    /* Add and Remove event listener
     *
     */
    _addListen() {
        this.actionLog = [];
        window.addEventListener('click', this._handleClickEvent);
        window.addEventListener('scroll',this._handleScrollEvent);
        window.addEventListener('mousedown',this._handleMouseDown);
        window.addEventListener('mousemove',this._handleMouseMove);
        window.addEventListener('mouseup',this._handleMouseUp);
    }
    _removeListen() {
        window.removeEventListener('click', this._handleClickEvent);
        window.removeEventListener('scroll',this._handleScrollEvent);
        window.removeEventListener('mousedown', this._handleMouseDown);
        window.removeEventListener('mousemove',this._handleMouseMove);
        window.removeEventListener('mouseup',this._handleMouseUp);
    }

    _currentPosition() {
        let doc = document.documentElement;
        let top = (window.pageYOffset || doc.scrollTop)  - (doc.clientTop || 0);
        return top
    }
    _startRecord (){
        this.setState({record: true});
    }
    _stopRecord (){
        this.setState({record: false});
    }

    _replay () {
        let preStepTime = 0;
        const _scrollFilter = (action) => {
            return action.hasOwnProperty('scroll');
        }
        for (let key in this.actionLog) {
          let action = this.actionLog[key];

          if ( action.hasOwnProperty('scroll')) {

            ( (offset, time) => {
                setTimeout( ()=>window.scrollTo(0, offset), time);
                preStepTime = time;
            })(action['scroll'][0], preStepTime + 20);
          }
          else if (action.hasOwnProperty('click')) {
            // Ignore start record step
            if (key != 0 ) {
              const clickEvent = this._createSimulatedMouseEvent('click');
              ( (target, time, e) => {
                setTimeout( ()=> target.dispatchEvent(e), time);
                preStepTime = time;
              })(action['click'][0], preStepTime + 400 , clickEvent);
            }
          }
          else if (action.hasOwnProperty('mousedown')) {
            ((target, time, e) => {
              setTimeout( ()=> target.dispatchEvent(e), time);
              preStepTime = time;
            })(action['mousedown'][0], preStepTime + 10, this._createSimulatedMouseEvent('mousedown'));
          } else if (action.hasOwnProperty('mouseup')) {
            ((target, time, e) => {
              setTimeout( ()=> target.dispatchEvent(e), time);
              preStepTime = time;
            })(action['mouseup'][0], preStepTime + 10, this._createSimulatedMouseEvent('mouseup'));
          }
          else if (action.hasOwnProperty('mousemove')) {
            const additionalInfo = action['mousemove'][2];
            ((target, time, e) => {
              setTimeout( ()=> target.dispatchEvent(e), time);
              preStepTime = time;
            })(action['mousemove'][0], preStepTime + 10, this._createSimulatedMouseEvent('mousemove',additionalInfo));
          }
          else if (action.hasOwnProperty('halt')) {
            preStepTime += 200;
          }
        }
    }
    /* addition info will not null, if mouse event need extra info */
    _createSimulatedMouseEvent(action, info) {
      const e = document.createEvent('MouseEvent');
      if (action === 'click') {
        e.initMouseEvent('click',true,true);
      } else if ( action === 'mousedown' ) {
        e.initMouseEvent('mousedown',true,true);
      } else if (action === 'mousemove') {
        e.initMouseEvent('mousemove',true,true,document.defaultView,0,info.sx,info.sy,info.cx,info.cy,false,false,false,false,0,null);
      } else if (action === 'mouseup') {
        e.initMouseEvent('mouseup', true,true);
      }
      return e
    }
    panelEvent() {
        this.refs.playBtn.openBtn();
        this.refs.recordBtn.openBtn();
        this.refs.qBtn.openBtn();
    }
    render(){
        if (this.state.record) {
            this._addListen();
        }
        else {
            this._removeListen();
        }
        let recordBtn = !this.state.record ? <RecordBtn ref="recordBtn" handleRecord={this._startRecord}/>: <RecordBtn ref="recordBtn" handleRecord={this._stopRecord}/>;

        return

                  G

                  {recordBtn}
                  <PlayBtn ref="playBtn" handleReplay={this._replay}/>
                  <QuestionBtn ref="qBtn" />

    }
}
<|endoftext|>


<commit_before>'use strict';

var _, Logger, env, modifiers, stream, util, imgType;

_         = require('lodash');
Logger    = require('./utils/logger');
env       = require('./config/environment_vars');
modifiers = require('./lib/modifiers');
stream    = require('stream');
util      = require('util');
imgType   = require('image-type');

// Simple stream to represent an error at an early stage, for instance a
// request to an excluded source.
function ErrorStream(image){
  stream.Readable.call(this, { objectMode : true });
  this.image = image;
}
util.inherits(ErrorStream, stream.Readable);

ErrorStream.prototype._read = function(){
  this.push(this.image);
  this.push(null);
};

function Image(request){
  // placeholder for any error objects
  this.error = null;

  // set a mark for the start of the process
  this.mark = Date.now();

  // determine the name and format (mime) of the requested image
  this.parseImage(request);

  // determine the requested modifications
  this.modifiers = modifiers.parse(request.path);

  // pull the various parts needed from the request params
  this.parseUrl(request);

  // placeholder for the buffer/stream coming from s3, will hold the image
  this.contents = null;

  // placeholder for the size of the original image
  this.originalContentLength = 0;

  // set the default expiry length, can be altered by a source file
  this.expiry = env.IMAGE_EXPIRY;

  // all logging strings will be queued here to be written on response
  this.log = new Logger();
}

Image.validInputFormats  = ['jpeg', 'jpg', 'gif', 'png', 'webp'];
Image.validOutputFormats = ['jpeg', 'png', 'webp'];

// Determine the name and format of the requested image
Image.prototype.parseImage = function(request){
  var fileStr = _.last(request.path.split('/'));
  var exts = fileStr.split('.').map( function (item) {
    return item.toLowerCase();
  });

  // clean out any metadata format
  if (exts[exts.length - 1] === 'json') {
    this.format = exts[exts.length - 2];
    exts.pop();
    fileStr = exts.join('.');
  }

  // if path contains valid output format, remove it from path
  if (exts.length >= 3) {
    var inputFormat = exts[exts.length - 2];
    var outputFormat = exts.pop();

    if (_.indexOf(Image.validInputFormats, inputFormat) > -1 &&
        _.indexOf(Image.validOutputFormats, outputFormat) > -1) {
      this.outputFormat = outputFormat;
      fileStr = exts.join('.');
    }
  }

  this.image  = fileStr;
};

// Determine the file path for the requested image
Image.prototype.parseUrl = function(request){
  var parts = request.path.replace(/^\//,'').split('/');

  // overwrite the image name with the parsed version so metadata requests do
  // not mess things up
  parts[parts.length - 1] = this.image;

  // if there is a modifier string remove it
  if (this.modifiers.hasModStr) {
    parts.shift();
  }

  this.path = parts.join('/');

  // account for any spaces in the path
  this.path = decodeURI(this.path);
};

Image.prototype.isError = function(){ return this.error !== null; };

Image.prototype.isStream = function(){
  var Stream = require('stream').Stream;
  return !!this.contents && this.contents instanceof Stream;
};

Image.prototype.isBuffer = function(){
  return !!this.contents && Buffer.isBuffer(this.contents);
};

Image.prototype.getFile = function(){
  var sources = require('./streams/sources'),
      excludes = env.EXCLUDE_SOURCES ? env.EXCLUDE_SOURCES.split(',') : [],
      streamType = env.DEFAULT_SOURCE,
      Stream = null;

  // look to see if the request has a specified source
  if (_.has(this.modifiers, 'external')){
    if (_.has(sources, this.modifiers.external)){
      streamType = this.modifiers.external;
    } else if (_.has(env.externalSources, this.modifiers.external)) {
      Stream = sources.external;
      return new Stream(this, this.modifiers.external, env.externalSources[this.modifiers.external]);
    }
  }

  // if this request is for an excluded source create an ErrorStream
  if (excludes.indexOf(streamType) > -1){
    this.error = new Error(streamType + ' is an excluded source');
    Stream = ErrorStream;
  }

  // if all is well find the appropriate stream
  else {
    this.log.log('new stream created!');
    Stream = sources[streamType];
  }

  return new Stream(this);
};

Image.prototype.sizeReduction = function(){
  var size = this.contents.length;
  return (this.originalContentLength - size)/1000;
};

Image.prototype.sizeSaving = function(){
  var oCnt = this.originalContentLength,
      size = this.contents.length;
  return ((oCnt - size)/oCnt * 100).toFixed(2);
};

Image.prototype.isFormatValid = function () {
  if (Image.validInputFormats.indexOf(this.format) === -1) {
    this.error = new Error(
      'The listed format (' + this.format + ') is not valid.'
    );
  }
};

// Setter/getter for image format that normalizes jpeg formats
Object.defineProperty(Image.prototype, 'format', {
  get: function () { return this._format; },
  set: function (value) {
    this._format = value.toLowerCase();
    if (this._format === 'jpg') { this._format = 'jpeg'; }
  }
});

// Setter/getter for image contents that determines the format from the content
// of the image to be processed.
Object.defineProperty(Image.prototype, 'contents', {
  get: function () { return this._contents; },
  set: function (data) {
    this._contents = data;

    if (this.isBuffer()) {
      this.format = imgType(data).ext;
      this.isFormatValid();
    }
  }
});

module.exports = Image;
<commit_msg>support external source as default stream<commit_after>'use strict';

var _, Logger, env, modifiers, stream, util, imgType;

_         = require('lodash');
Logger    = require('./utils/logger');
env       = require('./config/environment_vars');
modifiers = require('./lib/modifiers');
stream    = require('stream');
util      = require('util');
imgType   = require('image-type');

// Simple stream to represent an error at an early stage, for instance a
// request to an excluded source.
function ErrorStream(image){
  stream.Readable.call(this, { objectMode : true });
  this.image = image;
}
util.inherits(ErrorStream, stream.Readable);

ErrorStream.prototype._read = function(){
  this.push(this.image);
  this.push(null);
};

function Image(request){
  // placeholder for any error objects
  this.error = null;

  // set a mark for the start of the process
  this.mark = Date.now();

  // determine the name and format (mime) of the requested image
  this.parseImage(request);

  // determine the requested modifications
  this.modifiers = modifiers.parse(request.path);

  // pull the various parts needed from the request params
  this.parseUrl(request);

  // placeholder for the buffer/stream coming from s3, will hold the image
  this.contents = null;

  // placeholder for the size of the original image
  this.originalContentLength = 0;

  // set the default expiry length, can be altered by a source file
  this.expiry = env.IMAGE_EXPIRY;

  // all logging strings will be queued here to be written on response
  this.log = new Logger();
}

Image.validInputFormats  = ['jpeg', 'jpg', 'gif', 'png', 'webp'];
Image.validOutputFormats = ['jpeg', 'png', 'webp'];

// Determine the name and format of the requested image
Image.prototype.parseImage = function(request){
  var fileStr = _.last(request.path.split('/'));
  var exts = fileStr.split('.').map( function (item) {
    return item.toLowerCase();
  });

  // clean out any metadata format
  if (exts[exts.length - 1] === 'json') {
    this.format = exts[exts.length - 2];
    exts.pop();
    fileStr = exts.join('.');
  }

  // if path contains valid output format, remove it from path
  if (exts.length >= 3) {
    var inputFormat = exts[exts.length - 2];
    var outputFormat = exts.pop();

    if (_.indexOf(Image.validInputFormats, inputFormat) > -1 &&
        _.indexOf(Image.validOutputFormats, outputFormat) > -1) {
      this.outputFormat = outputFormat;
      fileStr = exts.join('.');
    }
  }

  this.image  = fileStr;
};

// Determine the file path for the requested image
Image.prototype.parseUrl = function(request){
  var parts = request.path.replace(/^\//,'').split('/');

  // overwrite the image name with the parsed version so metadata requests do
  // not mess things up
  parts[parts.length - 1] = this.image;

  // if there is a modifier string remove it
  if (this.modifiers.hasModStr) {
    parts.shift();
  }

  this.path = parts.join('/');

  // account for any spaces in the path
  this.path = decodeURI(this.path);
};

Image.prototype.isError = function(){ return this.error !== null; };

Image.prototype.isStream = function(){
  var Stream = require('stream').Stream;
  return !!this.contents && this.contents instanceof Stream;
};

Image.prototype.isBuffer = function(){
  return !!this.contents && Buffer.isBuffer(this.contents);
};

Image.prototype.getFile = function(){
  var sources = require('./streams/sources'),
      excludes = env.EXCLUDE_SOURCES ? env.EXCLUDE_SOURCES.split(',') : [],
      streamType = env.DEFAULT_SOURCE,
      Stream = null;

  // look to see if the request has a specified source
  if (_.has(this.modifiers, 'external')){
    if (_.has(sources, this.modifiers.external) || _.has(env.externalSources, this.modifiers.external)){
      streamType = this.modifiers.external;
    }
  }

  // if this request is for an excluded source create an ErrorStream
  if (excludes.indexOf(streamType) > -1){
    this.error = new Error(streamType + ' is an excluded source');
    Stream = ErrorStream;
  }

  // if all is well find the appropriate stream
  else {
    if (_.has(sources, streamType)){
      this.log.log('new stream created!');
      Stream = sources[streamType];
    } else if (_.has(env.externalSources, streamType)){
      this.log.log('new external stream created!');
      Stream = sources.external;
      return new Stream(this, streamType, env.externalSources[streamType]);
    } else {
      this.error = new Error(streamType + ' is not a valid source');
      Stream = ErrorStream;
    }
  }

  return new Stream(this);
};

Image.prototype.sizeReduction = function(){
  var size = this.contents.length;
  return (this.originalContentLength - size)/1000;
};

Image.prototype.sizeSaving = function(){
  var oCnt = this.originalContentLength,
      size = this.contents.length;
  return ((oCnt - size)/oCnt * 100).toFixed(2);
};

Image.prototype.isFormatValid = function () {
  if (Image.validInputFormats.indexOf(this.format) === -1) {
    this.error = new Error(
      'The listed format (' + this.format + ') is not valid.'
    );
  }
};

// Setter/getter for image format that normalizes jpeg formats
Object.defineProperty(Image.prototype, 'format', {
  get: function () { return this._format; },
  set: function (value) {
    this._format = value.toLowerCase();
    if (this._format === 'jpg') { this._format = 'jpeg'; }
  }
});

// Setter/getter for image contents that determines the format from the content
// of the image to be processed.
Object.defineProperty(Image.prototype, 'contents', {
  get: function () { return this._contents; },
  set: function (data) {
    this._contents = data;

    if (this.isBuffer()) {
      this.format = imgType(data).ext;
      this.isFormatValid();
    }
  }
});

module.exports = Image;
<|endoftext|>


<commit_before>import {select as d3Select} from "d3-selection";
import {transition as d3Transition} from "d3-transition";
const d3 = {
  "select": d3Select,
  "transition": d3Transition
};
import {default as constant} from "./constant";

/**
    The default height accessor function.
    @private
*/
function imageHeight(d) {
  return d.height;
}

/**
    The default URL accessor function.
    @private
*/
function imageUrl(d) {
  return d.url;
}

/**
    The default width accessor function.
    @private
*/
function imageWidth(d) {
  return d.width;
}

/**
    The default x accessor function.
    @private
*/
function imageX(d) {
  return d.x || 0;
}

/**
    The default y accessor function.
    @private
*/
function imageY(d) {
  return d.y || 0;
}

/**
    @function image
    @desc Creates SVG images based on an array of data. If *data* is specified, immediately draws the images based on the specified array and returns this generator. If *data* is not specified on instantiation, it can be passed/updated after instantiation using the [data](#image.data) method.
    @param {Array} [data = []]
    @example
a sample row of data

var data = {"url": "file.png", "width": "100", "height": "50"};
@example
passed to the generator

image([data]);
@example
creates the following

<image class="d3plus-shape-image" opacity="1" href="file.png" width="100" height="50" x="0" y="0"></image>
@example
this is shorthand for the following

image().data([data])();
@example
which also allows a post-draw callback function

image().data([data])(function() { alert("draw complete!"); })
*/
export default function(data = []) {

  let duration = 600,
      height = imageHeight,
      id = imageUrl,
      select,
      url = imageUrl,
      width = imageWidth,
      x = imageX,
      y = imageY;

  /**
      The inner return object and draw function that gets assigned the public methods.
      @private
  */
  function image(callback) {

    if (select === void 0) image.select(d3.select("body").append("svg").style("width", `${window.innerWidth}px`).style("height", `${window.innerHeight}px`).style("display", "block").node());

    const images = select.selectAll(".d3plus-shape-image").data(data, id);

    const enter = images.enter().append("image")
      .attr("class", "d3plus-shape-image")
      .attr("opacity", 0);

    const update = enter.merge(images);

    update.attr("xlink:href", url)
      .transition().duration(duration)
        .attr("opacity", 1)
        .attr("width", (d, i) => width(d, i))
        .attr("height", (d, i) => height(d, i))
        .attr("x", (d, i) => x(d, i))
        .attr("y", (d, i) => y(d, i))
        .each(function(d, i) {
          const image = d3.select(this), link = url(d, i);
          const fullAddress = link.indexOf("http://") === 0 || link.indexOf("https://") === 0;
          if (!fullAddress || link.indexOf(window.location.hostname) === 0) {
            const img = new Image();
            img.src = link;
            img.crossOrigin = "Anonymous";
            img.onload = function() {
              const canvas = document.createElement("canvas");
              canvas.width = this.width;
              canvas.height = this.height;
              const context = canvas.getContext("2d");
              context.drawImage(this, 0, 0);
              image.attr("xlink:href", canvas.toDataURL("image/png"));
            };
          }
        });

    images.exit().transition().duration(duration)
      .attr("opacity", 0).remove();

    if (callback) setTimeout(callback, duration + 100);

    return image;

  }

  /**
      @memberof image
      @desc If *data* is specified, sets the data array to the specified array and returns this generator. If *data* is not specified, returns the current data array. An <image> tag will be drawn for each object in the array.
      @param {Array} [*data* = []]
  */
  image.data = function(_) {
    return arguments.length ? (data = _, image) : data;
  };

  /**
      @memberof image
      @desc If *ms* is specified, sets the animation duration to the specified number and returns this generator. If *ms* is not specified, returns the current animation duration.
      @param {Number} [*ms* = 600]
  */
  image.duration = function(_) {
    return arguments.length ? (duration = _, image) : duration;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the height accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current height accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.height;
}
  */
  image.height = function(_) {
    return arguments.length ? (height = typeof _ === "function" ? _ : constant(_), image) : height;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the id accessor to the specified function and returns this generator. If *value* is not specified, returns the current id accessor. This is useful if you want to duplicate the same image.
      @param {Function} [*value*]
      @example
function(d) {
  return d.url;
}
  */
  image.id = function(_) {
    return arguments.length ? (id = _, image) : id;
  };

  /**
      @memberof image
      @desc If *selector* is specified, sets the SVG container element to the specified d3 selector or DOM element and returns this generator. If *selector* is not specified, returns the current SVG container element.
      @param {String|HTMLElement} [*selector* = d3.select("body").append("svg")]
  */
  image.select = function(_) {
    return arguments.length ? (select = d3.select(_), image) : select;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the URL accessor to the specified function and returns this generator. If *value* is not specified, returns the current URL accessor.
      @param {Function} [*value*]
      @example
function(d) {
  return d.url;
}
  */
  image.url = function(_) {
    return arguments.length ? (url = _, image) : url;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the width accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current width accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.width;
}
  */
  image.width = function(_) {
    return arguments.length ? (width = typeof _ === "function" ? _ : constant(_), image) : width;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the x accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current x accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.x || 0;
}
  */
  image.x = function(_) {
    return arguments.length ? (x = typeof _ === "function" ? _ : constant(_), image) : x;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the y accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current y accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.y || 0;
}
  */
  image.y = function(_) {
    return arguments.length ? (y = typeof _ === "function" ? _ : constant(_), image) : y;
  };

  return data.length ? image() : image;

}
<commit_msg>animates exiting images<commit_after>import {select as d3Select} from "d3-selection";
import {transition as d3Transition} from "d3-transition";
const d3 = {
  "select": d3Select,
  "transition": d3Transition
};
import {default as constant} from "./constant";

/**
    The default height accessor function.
    @private
*/
function imageHeight(d) {
  return d.height;
}

/**
    The default URL accessor function.
    @private
*/
function imageUrl(d) {
  return d.url;
}

/**
    The default width accessor function.
    @private
*/
function imageWidth(d) {
  return d.width;
}

/**
    The default x accessor function.
    @private
*/
function imageX(d) {
  return d.x || 0;
}

/**
    The default y accessor function.
    @private
*/
function imageY(d) {
  return d.y || 0;
}

/**
    @function image
    @desc Creates SVG images based on an array of data. If *data* is specified, immediately draws the images based on the specified array and returns this generator. If *data* is not specified on instantiation, it can be passed/updated after instantiation using the [data](#image.data) method.
    @param {Array} [data = []]
    @example
a sample row of data

var data = {"url": "file.png", "width": "100", "height": "50"};
@example
passed to the generator

image([data]);
@example
creates the following

<image class="d3plus-shape-image" opacity="1" href="file.png" width="100" height="50" x="0" y="0"></image>
@example
this is shorthand for the following

image().data([data])();
@example
which also allows a post-draw callback function

image().data([data])(function() { alert("draw complete!"); })
*/
export default function(data = []) {

  let duration = 600,
      height = imageHeight,
      id = imageUrl,
      select,
      url = imageUrl,
      width = imageWidth,
      x = imageX,
      y = imageY;

  /**
      The inner return object and draw function that gets assigned the public methods.
      @private
  */
  function image(callback) {

    if (select === void 0) image.select(d3.select("body").append("svg").style("width", `${window.innerWidth}px`).style("height", `${window.innerHeight}px`).style("display", "block").node());

    const images = select.selectAll(".d3plus-shape-image").data(data, id);

    const enter = images.enter().append("image")
      .attr("class", "d3plus-shape-image")
      .attr("opacity", 0);

    const update = enter.merge(images);

    update.attr("xlink:href", url)
      .transition().duration(duration)
        .attr("opacity", 1)
        .attr("width", (d, i) => width(d, i))
        .attr("height", (d, i) => height(d, i))
        .attr("x", (d, i) => x(d, i))
        .attr("y", (d, i) => y(d, i))
        .each(function(d, i) {
          const image = d3.select(this), link = url(d, i);
          const fullAddress = link.indexOf("http://") === 0 || link.indexOf("https://") === 0;
          if (!fullAddress || link.indexOf(window.location.hostname) === 0) {
            const img = new Image();
            img.src = link;
            img.crossOrigin = "Anonymous";
            img.onload = function() {
              const canvas = document.createElement("canvas");
              canvas.width = this.width;
              canvas.height = this.height;
              const context = canvas.getContext("2d");
              context.drawImage(this, 0, 0);
              image.attr("xlink:href", canvas.toDataURL("image/png"));
            };
          }
        });

    images.exit().transition().duration(duration)
      .attr("width", (d, i) => width(d, i))
      .attr("height", (d, i) => height(d, i))
      .attr("x", (d, i) => x(d, i))
      .attr("y", (d, i) => y(d, i))
      .attr("opacity", 0).remove();

    if (callback) setTimeout(callback, duration + 100);

    return image;

  }

  /**
      @memberof image
      @desc If *data* is specified, sets the data array to the specified array and returns this generator. If *data* is not specified, returns the current data array. An <image> tag will be drawn for each object in the array.
      @param {Array} [*data* = []]
  */
  image.data = function(_) {
    return arguments.length ? (data = _, image) : data;
  };

  /**
      @memberof image
      @desc If *ms* is specified, sets the animation duration to the specified number and returns this generator. If *ms* is not specified, returns the current animation duration.
      @param {Number} [*ms* = 600]
  */
  image.duration = function(_) {
    return arguments.length ? (duration = _, image) : duration;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the height accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current height accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.height;
}
  */
  image.height = function(_) {
    return arguments.length ? (height = typeof _ === "function" ? _ : constant(_), image) : height;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the id accessor to the specified function and returns this generator. If *value* is not specified, returns the current id accessor. This is useful if you want to duplicate the same image.
      @param {Function} [*value*]
      @example
function(d) {
  return d.url;
}
  */
  image.id = function(_) {
    return arguments.length ? (id = _, image) : id;
  };

  /**
      @memberof image
      @desc If *selector* is specified, sets the SVG container element to the specified d3 selector or DOM element and returns this generator. If *selector* is not specified, returns the current SVG container element.
      @param {String|HTMLElement} [*selector* = d3.select("body").append("svg")]
  */
  image.select = function(_) {
    return arguments.length ? (select = d3.select(_), image) : select;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the URL accessor to the specified function and returns this generator. If *value* is not specified, returns the current URL accessor.
      @param {Function} [*value*]
      @example
function(d) {
  return d.url;
}
  */
  image.url = function(_) {
    return arguments.length ? (url = _, image) : url;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the width accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current width accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.width;
}
  */
  image.width = function(_) {
    return arguments.length ? (width = typeof _ === "function" ? _ : constant(_), image) : width;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the x accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current x accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.x || 0;
}
  */
  image.x = function(_) {
    return arguments.length ? (x = typeof _ === "function" ? _ : constant(_), image) : x;
  };

  /**
      @memberof image
      @desc If *value* is specified, sets the y accessor to the specified function or number and returns this generator. If *value* is not specified, returns the current y accessor.
      @param {Function|Number} [*value*]
      @example
function(d) {
  return d.y || 0;
}
  */
  image.y = function(_) {
    return arguments.length ? (y = typeof _ === "function" ? _ : constant(_), image) : y;
  };

  return data.length ? image() : image;

}
<|endoftext|>


<commit_before>/**
 * Component class
 *
 * @class
 *
 * @author Guillaume Gouessan <guillaume.gouessan@gmail.com>
 *
 * @example
 * // By using an object of definitions
 * const component = new Component(document.body, {SubComponentClass, OtherSubComponentClass})
 * // By using a method that returns a definition from a given string
 * const component = new Component(document.body, name => name === 'SubComponentClass' ? SubComponentClass : OtherSubComponentClass)
 */
export default class Component {
  /**
   * Constructor
   * @param {Node} $el dom element that this component will be built around
   * @param {Object|Function} definitions Optional Object of Class Definitions or function that returns a Class Definitions from a given string
   */
  constructor ($el, definitions = null) {
    this.$el = $el
    this.componentName = ''
    this.parent = null
    this.definitions = []
    this.refs = {}
    this._componentInstances = []

    if (definitions) {
      this.init(definitions)
    }
  }

  /**
   * Returns dom done from css selector inside this component
   * @param {String} selector css selector
   * @returns {HTMLElement}
   */
  $one (selector) {
    return this.$el.querySelector(selector)
  }

  /**
   * Returns an array of all node element matching selector that are inside this component
   * @param {String} selector css selector
   * @returns {Array<HTMLElement>}
   */
  $all (selector) {
    return [].slice.call(this.$el.querySelectorAll(selector))
  }

  /**
   * This will be called either by constructor if definitions are passed or by parent's parse method.
   * Definitions all automatically passed down to children components and you most likely will not need to
   * override or call this function.
   * @param {Object|Function} definitions Object of Class Definitions or function that returns a Class Definitions from a given string
   */
  init (definitions) {
    this.definitions = definitions
    this.parse()
  }

  /**
   * Call this function when you want to remove and destroy a component
   */
  dispose () {
    this.disposeChildren()
    this.destroy()
  }

  /**
   * Call dispose function of each children components
   */
  disposeChildren () {
    this._componentInstances.forEach((component) => {
      component.dispose()
    })
    this._componentInstances = []
    this.refs = {}
  }

  /**
   * This will trigger a total reparsing of this component after killing its current childComponents, use at your own risk
   * @param {String} htmlString new HTML to parse
   */
  replaceContent (htmlString) {
    this.disposeChildren()
    this.$el.innerHTML = htmlString
    this.parse()
  }

  /**
   * Removes component from parent if it exists and deletes dom references
   */
  destroy () {
    if (this.parent && this.$el.parentNode && this.$el.parentNode === this.parent.$el) {
      this.parent.$el.removeChild(this.$el)
    }
    this.parent = null
    this.$el = null
  }

  /**
   * Returns first instance of Component with the name given as parameter
   * @param {String} componentName name of the component to find
   */
  findInstance (componentName) {
    let instance = this._componentInstances.filter(value => value.componentName === componentName)
    if (instance && instance.length) return instance[0]

    for (let i = 0, l = this._componentInstances.length; i < l; i++) {
      instance = this._componentInstances[i].findInstance(componentName)
      if (instance !== undefined) return instance
    }

    return undefined
  }

  /**
   * Returns all instances of Component with the name given as parameter
   * @param {String} componentName name of the component to find
   */
  findAllInstances (componentName) {
    let instances = this._componentInstances.filter(value => value.componentName === componentName)

    for (let i = 0, l = this._componentInstances.length; i < l; i++) {
      instances = instances.concat(this._componentInstances[i].findAllInstances(componentName))
    }

    return instances
  }

  /**
   * Looks into this component children and creates its sub components if any is found.
   * Sub component instances with data-ref attributes will be added to the refs object of current Component.
   */
  parse () {
    findComponents(this.$el, (node) => {
      const componentName = node && node.getAttribute ? node.getAttribute('data-component') : ''
      let Ctor
      let component
      if (node.nodeType === 1 && componentName) {
        if (node.tagName === 'FORM') {
          console.warn(`FORM tag does not support data-component. You should encapsulate the <form> with a <div> in component ${componentName}`)
        }

        if (this.definitions instanceof Function) {
          Ctor = this.definitions(componentName)
        } else if (this.definitions instanceof Object) {
          Ctor = this.definitions[componentName]
        }

        if (Ctor) {
          node.removeAttribute('data-component')
          component = new Ctor(node)
          component.init(this.definitions)
          component.componentName = componentName
          component.parent = this
          this._componentInstances.push(component)

          if (node.getAttribute('data-ref')) {
            this.refs[node.getAttribute('data-ref')] = component
          }
        } else {
          console.warn(`Can't find component '${componentName}'`)
        }
      }
    })

    this.ready()
  }

  /**
   * This is where you want to put the logic of the component
   */
  ready () {}
}

/**
 * Makes sure to put whatever obj is in an array if obj is not an array
 * @param {*} obj
 */
function toArray (obj) {
  return obj == null ? [] : (Array.isArray(obj) ? obj : [obj])
}

/**
 * Recursively Applies a callback on each Node that is found to be a Component
 * @param {Array} nodes an array of Node
 * @param {*} callback function to call on each Node that has data-component
 */
function findComponents (nodes, callback) {
  nodes = toArray(nodes)
  nodes = [].slice.call(nodes)
  let node
  for (let i = 0, l = nodes.length; i < l; i++) {
    node = nodes[i]
    if (node && node.hasAttribute && node.hasAttribute('data-component')) {
      callback(node)
    } else if (node.childNodes && node.childNodes.length) {
      findComponents([].slice.call(node.childNodes), callback)
    }
  }
}
<commit_msg>format<commit_after>/**
 * Component class
 *
 * @class
 *
 * @author Guillaume Gouessan <guillaume.gouessan@gmail.com>
 *
 * @example
 * // By using an object of definitions
 * const component = new Component(document.body, {SubComponentClass, OtherSubComponentClass})
 * // By using a method that returns a definition from a given string
 * const component = new Component(document.body, name => name === 'SubComponentClass' ? SubComponentClass : OtherSubComponentClass)
 */
export default class Component {
  /**
   * Constructor
   * @param {Node} $el dom element that this component will be built around
   * @param {Object|Function} definitions Optional Object of Class Definitions or function that returns a Class Definitions from a given string
   */
  constructor($el, definitions = null) {
    this.$el = $el
    this.componentName = ''
    this.parent = null
    this.definitions = []
    this.refs = {}
    this._componentInstances = []

    if (definitions) {
      this.init(definitions)
    }
  }

  /**
   * Returns dom done from css selector inside this component
   * @param {String} selector css selector
   * @returns {HTMLElement}
   */
  $one(selector) {
    return this.$el.querySelector(selector)
  }

  /**
   * Returns an array of all node element matching selector that are inside this component
   * @param {String} selector css selector
   * @returns {Array<HTMLElement>}
   */
  $all(selector) {
    return [].slice.call(this.$el.querySelectorAll(selector))
  }

  /**
   * This will be called either by constructor if definitions are passed or by parent's parse method.
   * Definitions all automatically passed down to children components and you most likely will not need to
   * override or call this function.
   * @param {Object|Function} definitions Object of Class Definitions or function that returns a Class Definitions from a given string
   */
  init(definitions) {
    this.definitions = definitions
    this.parse()
  }

  /**
   * Call this function when you want to remove and destroy a component
   */
  dispose() {
    this.disposeChildren()
    this.destroy()
  }

  /**
   * Call dispose function of each children components
   */
  disposeChildren() {
    this._componentInstances.forEach(component => {
      component.dispose()
    })
    this._componentInstances = []
    this.refs = {}
  }

  /**
   * This will trigger a total reparsing of this component after killing its current childComponents, use at your own risk
   * @param {String} htmlString new HTML to parse
   */
  replaceContent(htmlString) {
    this.disposeChildren()
    this.$el.innerHTML = htmlString
    this.parse()
  }

  /**
   * Removes component from parent if it exists and deletes dom references
   */
  destroy() {
    if (
      this.parent &&
      this.$el.parentNode &&
      this.$el.parentNode === this.parent.$el
    ) {
      this.parent.$el.removeChild(this.$el)
    }
    this.parent = null
    this.$el = null
  }

  /**
   * Returns first instance of Component with the name given as parameter
   * @param {String} componentName name of the component to find
   */
  findInstance(componentName) {
    let instance = this._componentInstances.filter(
      value => value.componentName === componentName
    )
    if (instance && instance.length) return instance[0]

    for (let i = 0, l = this._componentInstances.length; i < l; i++) {
      instance = this._componentInstances[i].findInstance(componentName)
      if (instance !== undefined) return instance
    }

    return undefined
  }

  /**
   * Returns all instances of Component with the name given as parameter
   * @param {String} componentName name of the component to find
   */
  findAllInstances(componentName) {
    let instances = this._componentInstances.filter(
      value => value.componentName === componentName
    )

    for (let i = 0, l = this._componentInstances.length; i < l; i++) {
      instances = instances.concat(
        this._componentInstances[i].findAllInstances(componentName)
      )
    }

    return instances
  }

  /**
   * Looks into this component children and creates its sub components if any is found.
   * Sub component instances with data-ref attributes will be added to the refs object of current Component.
   */
  parse() {
    findComponents(this.$el, node => {
      const componentName =
        node && node.getAttribute ? node.getAttribute('data-component') : ''
      let Ctor
      let component
      if (node.nodeType === 1 && componentName) {
        if (node.tagName === 'FORM') {
          console.warn(
            `FORM tag does not support data-component. You should encapsulate the <form> with a <div> in component ${componentName}`
          )
        }

        if (this.definitions instanceof Function) {
          Ctor = this.definitions(componentName)
        } else if (this.definitions instanceof Object) {
          Ctor = this.definitions[componentName]
        }

        if (Ctor) {
          node.removeAttribute('data-component')
          component = new Ctor(node)
          component.init(this.definitions)
          component.componentName = componentName
          component.parent = this
          this._componentInstances.push(component)

          if (node.getAttribute('data-ref')) {
            this.refs[node.getAttribute('data-ref')] = component
          }
        } else {
          console.warn(`Can't find component '${componentName}'`)
        }
      }
    })

    this.ready()
  }

  /**
   * This is where you want to put the logic of the component
   */
  ready() {}
}

/**
 * Makes sure to put whatever obj is in an array if obj is not an array
 * @param {*} obj
 */
function toArray(obj) {
  return obj == null ? [] : Array.isArray(obj) ? obj : [obj]
}

/**
 * Recursively Applies a callback on each Node that is found to be a Component
 * @param {Array} nodes an array of Node
 * @param {*} callback function to call on each Node that has data-component
 */
function findComponents(nodes, callback) {
  nodes = toArray(nodes)
  nodes = [].slice.call(nodes)
  let node
  for (let i = 0, l = nodes.length; i < l; i++) {
    node = nodes[i]
    if (node && node.hasAttribute && node.hasAttribute('data-component')) {
      callback(node)
    } else if (node.childNodes && node.childNodes.length) {
      findComponents([].slice.call(node.childNodes), callback)
    }
  }
}
<|endoftext|>


<commit_before>import chalk from "chalk";
import esprima from "esprima";
import yargs from "yargs";
import * as Styx from "../../styx/dist/transpiled/styx";

import fs from "fs";

import { validateCommandLineArgs } from "./argValidator";
import help from "./help";

const commandLineArgs = yargs
    .alias("i", "input")
    .alias("o", "output")
    .alias("f", "format")
    .argv;

runStyx(commandLineArgs);

function runStyx(args) {
    if (args.help) {
        help.show();
        return;
    }

    const argumentErrors = validateCommandLineArgs(args);

    if (argumentErrors.length > 0) {
        const errorMessage = argumentErrors.join("\n");
        exitWithError(errorMessage);
    }

    processInputFile(args);
}

function processInputFile(args) {
    fs.readFile(args.input, "utf-8", function(err, fileContents) {
        if (err) {
            exitWithError(`Couldn't read input file "${err.path}".`);
        }

        const ast = esprima.parse(fileContents);
        const flowProgram = Styx.parse(ast);
        const exportedProgram = Styx.exportProgram(flowProgram, args.format);

        if (args.output) {
            writeExportToFile(exportedProgram, args);
        } else {
            console.log(exportedProgram);
        }
    });
}

function writeExportToFile(exportedProgram, args) {
    fs.writeFile(args.output, exportedProgram, function(err) {
        if (err) {
            exitWithError(err);
        }

        console.log(chalk.bold.green("Export successful."));
    });
}

function displayError(errorMessage) {
    console.log(chalk.red.bold(errorMessage));
}

function exitWithError(errorMessage) {
    displayError(errorMessage);
    process.exit(1);
}
<commit_msg>Passes a control flow graph to `exportDot` (rather than an entire flow program)<commit_after>import chalk from "chalk";
import esprima from "esprima";
import yargs from "yargs";
import * as Styx from "../../styx/dist/transpiled/styx";

import fs from "fs";

import { validateCommandLineArgs } from "./argValidator";
import help from "./help";

const commandLineArgs = yargs
    .alias("i", "input")
    .alias("o", "output")
    .alias("f", "format")
    .argv;

runStyx(commandLineArgs);

function runStyx(args) {
    if (args.help) {
        help.show();
        return;
    }

    const argumentErrors = validateCommandLineArgs(args);

    if (argumentErrors.length > 0) {
        const errorMessage = argumentErrors.join("\n");
        exitWithError(errorMessage);
    }

    processInputFile(args);
}

function processInputFile(args) {
    fs.readFile(args.input, "utf-8", function(err, fileContents) {
        if (err) {
            exitWithError(`Couldn't read input file "${err.path}".`);
        }

        const ast = esprima.parse(fileContents);
        const flowProgram = Styx.parse(ast);
        const exportedProgram = exportAsTargetFormat(flowProgram, args);

        if (args.output) {
            writeExportToFile(exportedProgram, args);
        } else {
            console.log(exportedProgram);
        }
    });
}

function exportAsTargetFormat(flowProgram, args) {
    switch (args.format.trim().toLowerCase()) {
        case "json":
            return Styx.exportJson(flowProgram);

        case "dot":
            let flowGraph = findFlowGraphForId(flowProgram, 0);
            return Styx.exportDot(flowGraph);

        default:
            throw Error(`Encountered unsupported format "${args.format}"`);
    }
}

function findFlowGraphForId(flowProgram, functionId) {
    if (!functionId) {
        return flowProgram.flowGraph;
    }

    for (let fun of flowProgram.functions) {
        if (fun.id === functionId) {
            return fun.flowGraph;
        }
    }

    throw Error(`Couldn't find function with id ${functionId}`);
}

function writeExportToFile(exportedProgram, args) {
    fs.writeFile(args.output, exportedProgram, function(err) {
        if (err) {
            exitWithError(err);
        }

        console.log(chalk.bold.green("Export successful."));
    });
}

function displayError(errorMessage) {
    console.log(chalk.red.bold(errorMessage));
}

function exitWithError(errorMessage) {
    displayError(errorMessage);
    process.exit(1);
}
<|endoftext|>


<commit_before>var express = require('express');
var app = express();

var Notes = require('./domain/Notes');
var notes = new Notes();

var TestData = require('./TestData');
TestData(notes);

var halApi = require('./api/HalApi');
app.use('/api/hal', halApi(notes));

app.listen((process.env.PORT || 3000), (process.env.HOST || '0.0.0.0'));
<commit_msg>Redirect / call to github page<commit_after>var express = require('express');
var app = express();

var Notes = require('./domain/Notes');
var notes = new Notes();

var TestData = require('./TestData');
TestData(notes);

var halApi = require('./api/HalApi');
app.use('/api/hal', halApi(notes));

app.get('/', function(req, res) {
	res.statusCode = 302;
	res.setHeader('Location', 'https://github.com/DracoBlue/hateoas-notes');
	res.end();
});

app.listen((process.env.PORT || 3000), (process.env.HOST || '0.0.0.0'));
<|endoftext|>


<commit_before>import Client from './client';
import jwtPlugin from './plugins/jwt';

export { jwtPlugin };

export default Client;
<commit_msg>Export events and PluginError<commit_after>import Client from './client';
import * as events from './events';
import jwtPlugin from './plugins/jwt';
import PluginError from './plugin-error';

export {
  events,
  jwtPlugin,
  PluginError,
};

export default Client;
<|endoftext|>


<commit_before>var express = require('express');

// Constants
var PORT = 8080;

// App
var app = express();
app.get('/', function (req, res) {
  res.send('Hello world v1\n');
});

app.listen(PORT);
console.log('Running on http://localhost:' + PORT);
<commit_msg>	modified:   src/index.js<commit_after>var express = require('express');

// Constants
var PORT = 8080;

// App
var app = express();
app.get('/', function (req, res) {
  res.send('Hello world v2\n');
});

app.listen(PORT);
console.log('Running on http://localhost:' + PORT);
<|endoftext|>


<commit_before>/**
 * Come from Vue.js v1.0.24
 *
 *   https://github.com/vuejs/vue
 *
 */

import Cache from './cache.js'

const defaultAllowedKeywords =
        'Math,Date,this,true,false,null,undefined,Infinity,NaN,'
      + 'isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,'
      + 'encodeURIComponent,parseInt,parseFloat'

// keywords that don't make sense inside expressions
const improperKeywordsRE =
  new RegExp(
    '^('
    + ('break,case,class,catch,const,continue,debugger,default,'
      + 'delete,do,else,export,extends,finally,for,function,if,'
      + 'import,in,instanceof,let,return,super,switch,throw,try,'
      + 'var,while,with,yield,enum,await,implements,package,'
      + 'protected,static,interface,private,public'
    ).replace(/,/g, '\\b|')
    + '\\b)'
  )

const wsRE = /\s/g
const newlineRE = /\n/g
const saveRE = /[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g
const restoreRE = /"(\d+)"/g
const pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/
const identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g
const booleanLiteralRE = /^(?:true|false)$/

/**
 * Save / Rewrite / Restore
 *
 * When rewriting paths found in an expression, it is
 * possible for the same letter sequences to be found in
 * strings and Object literal property keys. Therefore we
 * remove and store these parts in a temporary array, and
 * restore them after the path rewrite.
 */

let saved = []

/**
 * Save replacer
 *
 * The save regex can match two possible cases:
 * 1. An opening object literal
 * 2. A string
 * If matched as a plain string, we need to escape its
 * newlines, since the string needs to be preserved when
 * generating the function body.
 *
 * @param {String} str
 * @param {String} isString - str if matched as a string
 * @return {String} - placeholder with index
 */

function save (str, isString) {
  let i = saved.length
  saved[i] = isString
    ? str.replace(newlineRE, '\\n')
    : str
  return '"' + i + '"'
}

/**
 * Restore replacer
 *
 * @param {String} str
 * @param {String} i - matched save index
 * @return {String}
 */

function restore (str, i) {
  return saved[i]
}

/**
 * Check if an expression is a simple path.
 *
 * @param {String} expr
 * @return {Boolean}
 */

function isSimplePath (expr) {
  return pathTestRE.test(expr)
    // don't treat true/false as paths
    && !booleanLiteralRE.test(expr)// &&
    // Math constants e.g. Math.PI, Math.E etc.
    // && expr.slice(0, 5) !== 'Math.'
}

/**
 * Check if an expression is a simple path.
 *
 * @param {String} expr
 * @return {Boolean}
 */

function parseKeywordsToRE (keywords) {
  return new RegExp(
        '^(?:'
      + keywords
        .replace(wsRE, '')
        .replace(/\$/g, '\\$')
        .replace(/,/g, '\\b|')
      + '\\b)'
    )
}

/**
 * @param {Object} config
 *   - {Number} cache, default 1000
 *              limited for Cache
 *   - {Array} params, default ['$']
 *              first one is for scope and you can add more params
 * @constructor
 */

export default class Gep {
  constructor ({
    cache = 1000,
    scope = '$',
    scopes,
    params = scopes
           ? Object.keys(scopes).unshift(scope)
           : [scope],
  } = {}) {
    this._cache = new Cache(cache)

    this._funcParams = params.join(',').replace(wsRE, '')
    this._funcBefore = 'function(' + this._funcParams + '){return '

    this.scope = scope

    if (scopes) {
      let keywords
      Object.keys(scopes).forEach(key => {
        keywords = scopes[key]
        if (Array.isArray(keywords)) {
          keywords = keywords.join(',')
        }
        scopes[key] = parseKeywordsToRE(keywords)
      })
      this._scopeREs = scopes
    }

    let paramsPrefix
    if (params.length > 1) {
      params = params.slice(1)
      paramsPrefix = params.join(',')
      this._paramsPrefixRE = parseKeywordsToRE(paramsPrefix)
    }

    let allowedKeywords = paramsPrefix
                        ? paramsPrefix
                          + ','
                          + defaultAllowedKeywords
                        : defaultAllowedKeywords
    this._allowedKeywordsRE = parseKeywordsToRE(allowedKeywords)
  }

  _addScope (expr) {
    if (this._paramsPrefixRE && this._paramsPrefixRE.test(expr)) {
      return expr
    }
    if (this._scopeREs) {
      let keys = Object.keys(this._scopeREs)
      let re
      for (let i = 0, l = keys.length; i < l; i++) {
        re = this._scopeREs[keys[i]]
        if (re.test(expr)) {
          return keys[i] + '.' + expr
        }
      }
    }
    return this.scope + '.' + expr
  }

  /**
   * Rewrite an expression, prefixing all path accessors with
   * `scope.` and return the new expression.
   *
   * @param {String} expr
   * @return {String}
   */

  compile (expr) {
    if (improperKeywordsRE.test(expr)) {
      if (process.env.NODE_ENV !== 'production'
        && console && console.warn
      ) {
        console.warn('Avoid using reserved keywords in expression: ' + expr)
      }
    }
    // reset state
    saved.length = 0
    // save strings and object literal keys
    let body = expr
      .replace(saveRE, save)
      .replace(wsRE, '')
    // rewrite all paths
    // pad 1 space here becaue the regex matches 1 extra char
    body = (' ' + body)
      .replace(identRE, (raw) => {
        let c = raw.charAt(0)
        let path = raw.slice(1)
        if (this._allowedKeywordsRE.test(path)) {
          return raw
        } else {
          path = path.indexOf('"') > -1
            ? path.replace(restoreRE, restore)
            : path
          return c + this._addScope(path)
        }
      })
      .replace(restoreRE, restore)
    return body.slice(1)
  }

  /**
   * Build a getter function. Requires eval.
   *
   * We isolate the try/catch so it doesn't affect the
   * optimization of the parse function when it is not called.
   *
   * @param {String} body
   * @param {String} toStr
   * @return {Function|String|undefined}
   */

  make (body, toStr) {
    if (toStr) {
      return this._funcBefore + body + '}'
    }
    try {
      /* eslint-disable no-new-func */
      return new Function(this._funcParams, 'return ' + body)
      /* eslint-enable no-new-func */
    } catch (e) {
      if (process.env.NODE_ENV !== 'production'
        && console && console.warn
      ) {
        console.warn(
            'Invalid expression. Generated function body: '
          + body
        )
      }
    }
  }

  /**
   * Parse an expression.
   *
   * @param {String} expr
   * @return {Function}
   */

  parse (expr) {
    if (!(expr && (expr = expr.trim()))) {
      return ''
    }
    // try cache
    let hit = this._cache.get(expr)
    if (hit) {
      return hit
    }
    let res = isSimplePath(expr) && expr.indexOf('[') < 0
      ? this._addScope(expr)
      // dynamic getter
      : this.compile(expr)
    this._cache.put(expr, res)
    return res
  }
}
<commit_msg>Fix params<commit_after>/**
 * Come from Vue.js v1.0.24
 *
 *   https://github.com/vuejs/vue
 *
 */

import Cache from './cache.js'

const defaultAllowedKeywords =
        'Math,Date,this,true,false,null,undefined,Infinity,NaN,'
      + 'isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,'
      + 'encodeURIComponent,parseInt,parseFloat'

// keywords that don't make sense inside expressions
const improperKeywordsRE =
  new RegExp(
    '^('
    + ('break,case,class,catch,const,continue,debugger,default,'
      + 'delete,do,else,export,extends,finally,for,function,if,'
      + 'import,in,instanceof,let,return,super,switch,throw,try,'
      + 'var,while,with,yield,enum,await,implements,package,'
      + 'protected,static,interface,private,public'
    ).replace(/,/g, '\\b|')
    + '\\b)'
  )

const wsRE = /\s/g
const newlineRE = /\n/g
const saveRE = /[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g
const restoreRE = /"(\d+)"/g
const pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/
const identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g
const booleanLiteralRE = /^(?:true|false)$/

/**
 * Save / Rewrite / Restore
 *
 * When rewriting paths found in an expression, it is
 * possible for the same letter sequences to be found in
 * strings and Object literal property keys. Therefore we
 * remove and store these parts in a temporary array, and
 * restore them after the path rewrite.
 */

let saved = []

/**
 * Save replacer
 *
 * The save regex can match two possible cases:
 * 1. An opening object literal
 * 2. A string
 * If matched as a plain string, we need to escape its
 * newlines, since the string needs to be preserved when
 * generating the function body.
 *
 * @param {String} str
 * @param {String} isString - str if matched as a string
 * @return {String} - placeholder with index
 */

function save (str, isString) {
  let i = saved.length
  saved[i] = isString
    ? str.replace(newlineRE, '\\n')
    : str
  return '"' + i + '"'
}

/**
 * Restore replacer
 *
 * @param {String} str
 * @param {String} i - matched save index
 * @return {String}
 */

function restore (str, i) {
  return saved[i]
}

/**
 * Check if an expression is a simple path.
 *
 * @param {String} expr
 * @return {Boolean}
 */

function isSimplePath (expr) {
  return pathTestRE.test(expr)
    // don't treat true/false as paths
    && !booleanLiteralRE.test(expr)// &&
    // Math constants e.g. Math.PI, Math.E etc.
    // && expr.slice(0, 5) !== 'Math.'
}

/**
 * Check if an expression is a simple path.
 *
 * @param {String} expr
 * @return {Boolean}
 */

function parseKeywordsToRE (keywords) {
  return new RegExp(
        '^(?:'
      + keywords
        .replace(wsRE, '')
        .replace(/\$/g, '\\$')
        .replace(/,/g, '\\b|')
      + '\\b)'
    )
}

/**
 * @param {Object} config
 *   - {Number} cache, default 1000
 *              limited for Cache
 *   - {Array} params, default ['$']
 *              first one is for scope and you can add more params
 * @constructor
 */

export default class Gep {
  constructor ({
    cache = 1000,
    scope = '$',
    scopes,
    params,
  } = {}) {
    if (!params) {
      if (scopes) {
        params = Object.keys(scopes)
        params.unshift(scope)
      } else {
        params = [scope]
      }
    }
    this._cache = new Cache(cache)

    this._funcParams = params.join(',').replace(wsRE, '')
    this._funcBefore = 'function(' + this._funcParams + '){return '

    this.scope = scope

    if (scopes) {
      let keywords
      Object.keys(scopes).forEach(key => {
        keywords = scopes[key]
        if (Array.isArray(keywords)) {
          keywords = keywords.join(',')
        }
        scopes[key] = parseKeywordsToRE(keywords)
      })
      this._scopeREs = scopes
    }

    let paramsPrefix
    if (params.length > 1) {
      params = params.slice(1)
      paramsPrefix = params.join(',')
      this._paramsPrefixRE = parseKeywordsToRE(paramsPrefix)
    }

    let allowedKeywords = paramsPrefix
                        ? paramsPrefix
                          + ','
                          + defaultAllowedKeywords
                        : defaultAllowedKeywords
    this._allowedKeywordsRE = parseKeywordsToRE(allowedKeywords)
  }

  _addScope (expr) {
    if (this._paramsPrefixRE && this._paramsPrefixRE.test(expr)) {
      return expr
    }
    if (this._scopeREs) {
      let keys = Object.keys(this._scopeREs)
      let re
      for (let i = 0, l = keys.length; i < l; i++) {
        re = this._scopeREs[keys[i]]
        if (re.test(expr)) {
          return keys[i] + '.' + expr
        }
      }
    }
    return this.scope + '.' + expr
  }

  /**
   * Rewrite an expression, prefixing all path accessors with
   * `scope.` and return the new expression.
   *
   * @param {String} expr
   * @return {String}
   */

  compile (expr) {
    if (improperKeywordsRE.test(expr)) {
      if (process.env.NODE_ENV !== 'production'
        && console && console.warn
      ) {
        console.warn('Avoid using reserved keywords in expression: ' + expr)
      }
    }
    // reset state
    saved.length = 0
    // save strings and object literal keys
    let body = expr
      .replace(saveRE, save)
      .replace(wsRE, '')
    // rewrite all paths
    // pad 1 space here becaue the regex matches 1 extra char
    body = (' ' + body)
      .replace(identRE, (raw) => {
        let c = raw.charAt(0)
        let path = raw.slice(1)
        if (this._allowedKeywordsRE.test(path)) {
          return raw
        } else {
          path = path.indexOf('"') > -1
            ? path.replace(restoreRE, restore)
            : path
          return c + this._addScope(path)
        }
      })
      .replace(restoreRE, restore)
    return body.slice(1)
  }

  /**
   * Build a getter function. Requires eval.
   *
   * We isolate the try/catch so it doesn't affect the
   * optimization of the parse function when it is not called.
   *
   * @param {String} body
   * @param {String} toStr
   * @return {Function|String|undefined}
   */

  make (body, toStr) {
    if (toStr) {
      return this._funcBefore + body + '}'
    }
    try {
      /* eslint-disable no-new-func */
      return new Function(this._funcParams, 'return ' + body)
      /* eslint-enable no-new-func */
    } catch (e) {
      if (process.env.NODE_ENV !== 'production'
        && console && console.warn
      ) {
        console.warn(
            'Invalid expression. Generated function body: '
          + body
        )
      }
    }
  }

  /**
   * Parse an expression.
   *
   * @param {String} expr
   * @return {Function}
   */

  parse (expr) {
    if (!(expr && (expr = expr.trim()))) {
      return ''
    }
    // try cache
    let hit = this._cache.get(expr)
    if (hit) {
      return hit
    }
    let res = isSimplePath(expr) && expr.indexOf('[') < 0
      ? this._addScope(expr)
      // dynamic getter
      : this.compile(expr)
    this._cache.put(expr, res)
    return res
  }
}
<|endoftext|>