<commit_before>import 'console-polyfill';
import hoistNonReactStatics from 'hoist-non-react-statics';
import {
  Component,
  createElement,
  PropTypes,
} from 'react';
// import localStorage from './localStorage';

let SHOULD_LOG = true;

// id counter to be used for various things that require unique identifiers
var idCounter = 0;
function nextId() {
  return idCounter++;
}

// a stateful connection id, incremented for each connection.
var connIdCounter = 0;

// map of each connection to an object containing keys:
// - ready (boolean)
// - user (user ident - two-tuple)
// - anonymous (boolean)
var connStatus = {};

// a map of each component ID to it's object
var componentIdx = {};

// for async calls to the worker, maintains the callback to execute with the result, when applicable
var callbackRegistry = {};

// holds worker reference globally. Don't initiate until first request for connection
var fqlWorker;

// worker queue
var workerQueue = [];

// worker initialized
var workerInitialized = false;

// worker.onmessage handler
function workerMessageHandler(e) {
  const msg = e.data;
  var cb;

  SHOULD_LOG && console.log("Worker received message: " + JSON.stringify(msg));

  switch (msg.event) {
    case "connInit":
      workerInitialized = true;
      workerQueue.forEach(workerInvoke);
      workerQueue = [];
      break;

    case "connStatus":
      const response = msg.data || {};
      const statusCode = response.status;
      if (connStatus[msg.conn]) {
        switch (statusCode) {
          case 200:
            connStatus[msg.conn].ready = true;
            break;
          case 401: // authorization error, need to log in
            connStatus[msg.conn].ready = false;
            connStatus[msg.conn].user = null;
            connStatus[msg.conn].anonymous = true;
            break;
          default:
            console.warn("Invalid connection response status: " + JSON.stringify(response));
            break;
        }
      }
      break;

    case "connClosed":
      cb = callbackRegistry[msg.ref];
      if (cb) {
        delete callbackRegistry[msg.ref];
        cb(msg.data);
      }
      break;

    case "connLogout":
      cb = callbackRegistry[msg.ref];
      if (cb) {
        delete callbackRegistry[msg.ref];
        cb(msg.data);
      }
      break;

    case "setState":
      const comp = componentIdx[msg.ref];
      if (comp) {
        comp.setState(msg.data);
      } else {
        SHOULD_LOG && console.warn("Component no longer registered: " + msg.ref);
      }
      break;

    case "remoteInvoke":
      // check for a callback
      cb = callbackRegistry[msg.ref];
      if (cb) {
        delete callbackRegistry[msg.ref];
        cb(msg.data);
      }
      break;

    case "login":
      // if login successful, update conn's connStatus
      if (msg.data.status === 200) {
        connStatus[msg.conn].user = msg.data.body.user;
        connStatus[msg.conn].anonymous = msg.data.body.anonymous;
      }
      // if there was a callback passed to login(), execute
      cb = callbackRegistry[msg.ref];
      if (cb) {
        delete callbackRegistry[msg.ref];
        cb(msg.data);
      }
      break;

    default:
      SHOULD_LOG && console.warn("Unreconized event from worker: " + msg.event + ". Full message: " + JSON.stringify(msg));
      break;
  }
  return;
}

function setStateCb(conn, id, stateUpdate) {
  const comp = componentIdx[id];

  if (comp) {
    comp.setState(stateUpdate);
  } else {
    SHOULD_LOG && console.warn("Component no longer registered: " + compId);
  }
}

// we use a global to track connection state, get method for it
function isReady(connId) {
  return connStatus[connId].ready;
}

// we use a global to track connection state, get method for it
function isClosed(connId) {
  const connObj = connStatus[connId];
  return (connObj && Object.keys(connObj).length === 0);
}

function workerInvoke(obj) {
  console.log('invoke', obj.action, workerInitialized, workerQueue);

  if (obj.cb) {
    obj.ref = obj.ref || nextId();
    callbackRegistry[obj.ref] = obj.cb;
    delete obj.cb;
  }

  if(workerInitialized) {
    fqlWorker.postMessage(obj);
  } else {
    workerQueue.push(obj);
  }

  return true;
}

// Register a query, provide the connection, component ID, query and query options
export function registerQuery(conn, compId, query, opts) {
  const invokeObj = {
    conn: conn.id,
    action: "registerQuery",
    ref: compId,
    params: [compId, query, opts]
  };
  return workerInvoke(invokeObj);
}

// Remove query from registry
export function unregisterQuery(conn, compId) {
  workerInvoke({
    conn: conn.id,
    ref: compId,
    action: "unregisterQuery",
    params: [compId]
  });
}

function workerErrorHandler(error) {
  console.error('Web worker error', JSON.stringify(error));
}

// Create a new connection with settings object.
// need to provide url, instance and token keys at minumum.
export function ReactConnect(connSettings) {
  // initialize worker if not already done
  if (!fqlWorker) {
    fqlWorker = new Worker(connSettings.workerUrl || "/fqlClient.js");
    fqlWorker.onmessage = workerMessageHandler;
    fqlWorker.onerror = workerErrorHandler;
  }

  connIdCounter++;
  const connId = connIdCounter;

  const baseSetting = {
    id: connId,
    log: true,
    removeNamespace: true // by default remove namespace from results
  };

  const settings = Object.assign(baseSetting, connSettings);

  SHOULD_LOG = settings.log;

  const conn = {
    id: connId,
    isReady: () => isReady(connId),
    isClosed: () => isClosed(connId),
    login: function(username, password, cb) {
      return workerInvoke({
        conn: connId,
        action: "login",
        params: [username, password],
        cb: cb
      });
    },
    invoke: function (action, params, cb) {
      const invokeStatment = [action, params];
      return workerInvoke({
        conn: connId,
        action: "remoteInvoke",
        params: [invokeStatment],
        cb: cb
      });
    },
    getUser: function () {
      return connStatus[connId].user;
    },
    getInstance: function () {
      return connSettings.instance;
    },
    isAuthenticated: function () {
      if (connStatus[connId].anonymous === false) {
        return true;
      } else {
        return false;
      }
    },
    reset: function (cb) {
      connStatus[connId] = {
        ready: false,
        user: null,
        anonymous: true
      };
      return workerInvoke({
        conn: connId,
        action: "reset",
        params: [],
        cb: cb
      });
    },
    logout: function (cb) {
      connStatus[connId] = {
        ready: false,
        user: null,
        anonymous: true
      };
      return workerInvoke({
        conn: connId,
        action: "logout",
        params: [],
        cb: cb
      });
    },
    close: function (cb) {
      // clear out connection state held locally
      connStatus[connId] = {};
      return workerInvoke({
        conn: connId,
        action: "close",
        params: [],
        cb: cb
      });
    }
  };

  // initialize connection status, set ready to false
  connStatus[connId] = {
    ready: false,
    // if we already passed in a token, can also pass in the user/anonymous flags for storing
    user: settings.user,
    anonymous: settings.anonymous
  };

  // initiate our connection in the web worker
  workerInvoke({
    conn: 0, // conn 0 means not connection specific
    action: "connect",
    params: [settings]
  });

  // return connection object
  return conn;
}

function getDisplayName(component) {
  return component.displayName || component.name || "Component";
}

// wraps react components that need a particular connection, making the
// connection available via the context to children
export class FlureeProvider extends Component {
  static propTypes = {
    conn: PropTypes.object.isRequired
  };

  static childContextTypes = {
    conn: PropTypes.object.isRequired
  };

  constructor(props, context) {
    super(props, context);

    if (!props.conn) {
      throw "FlureeProvider was not provided a conn prop, which should be a connection object."
    }

    this.conn = props.conn;
  };

  getChildContext() {
    return {
      conn: this.conn
    }
  };

  render() {
    return React.Children.only(this.props.children);
  };
}

// given a query and options, returns a vector of variables that
// were not provided via options. We use this to look for the variables
// in props
function getMissingVars(flurQL, opts) {
  const vars = flurQL.vars;

  if (!vars || !Array.isArray(vars)) {
    return [];
  }

  if (opts && opts.vars) {
    return vars.filter((v) => { return !opts.vars[v]; });
  } else {
    return vars;
  }
}

// Create an empty map of the top level query nodes so less
// boilerplate is required to test if a property exists in the
// wrapped component
function fillDefaultResult(query) {
  if (!query) return {};

  const graph = query.graph || query;

  if (!Array.isArray(graph)) { // invalid graph
    return;
  }

  var defaultResult = {};

  graph.map(([stream, opts]) => {
    if (opts.as) {
      defaultResult[opts.as] = null;
    } else {
      defaultResult[stream] = null;
    }
  });

  return defaultResult;
}

function queryIsValid(query) {
  if (query !== null && (Array.isArray(query) || typeof query === "object")) {
    const graph = Array.isArray(query) ? query : query.graph;
    if (Array.isArray(graph) && graph.length > 0) {
      return true;
    } else {
      return false;
    }
  } else {
    return false;
  }
}

function wrapComponent(WrappedComponent, query, opts) {
  const flurQLDisplayName = `Fluree(${getDisplayName(WrappedComponent)})`;

  class FlurQL extends Component {
    static displayName = flurQLDisplayName;
    static WrappedComponent = WrappedComponent;
    static contextTypes = {
      conn: PropTypes.object.isRequired
    };

    constructor(props, context) {
      super(props, context);
      this.conn = context.conn;
      this.opts = Object.assign({ vars: {} }, opts);
      this.id = nextId();
      this.queryIsFunction = (typeof query === "function")
      this.query = this.queryIsFunction ? query(props, this.context) : query;
      this.isValidQuery = this.query && queryIsValid(this.query);
      this.missingVars = this.isValidQuery ? getMissingVars(this.query, this.opts) : []; // list of vars we need to check props for
      this.state = {
        result: this.isValidQuery ? fillDefaultResult(this.query) : {},
        error: this.query && !this.isValidQuery ? { status: 400, message: "Query is not valid: " + JSON.stringify(this.query) } : null,
        warning: this.query ? null : "No query yet, waiting...",
        status: "pending",
        loading: true
      };

      if (!this.conn) {
        throw "Could not find a Fluree connection (conn) in the context of " + flurQLDisplayName + ".";
      }
    }

    componentWillMount() {
      // get any missing vars from props and update this.opts with them
      if (this.missingVars.length !== 0) {
        this.missingVars.forEach((v) => {
          if('currentUser' === v) {
            this.opts.vars[v] = this.conn.getUser();
          } else {
            this.opts.vars[v] = this.props[v];
          }
        });
      }

      // register this component for later re-render calling, etc.
      componentIdx[this.id] = this;

      if (this.query && this.isValidQuery) {
        registerQuery(this.conn, this.id, this.query, this.opts);
      }
    }

    componentWillUnmount() {
      unregisterQuery(this.conn, this.id);
      delete componentIdx[this.id];
    }

    componentWillReceiveProps(nextProps) {
      if (this.queryIsFunction) {
        const newQuery = query(nextProps, this.context);
        this.query = newQuery;
        this.isValidQuery = queryIsValid(this.query);
        if (this.query && this.isValidQuery) {
          registerQuery(this.conn, this.id, this.query, this.opts);
        }
      } else {
        // check if any of the missing vars changed with the new props
        let didMissingVarsChange = false;

        for (let i = 0; i < this.missingVars.length; i++) {
          const varName = this.missingVars[i];
          if (this.props[varName] !== nextProps[varName]) {
            didMissingVarsChange = true;
          }
        }

        if (didMissingVarsChange === true) {
          this.missingVars.forEach((v) => {
            if('currentUser' === v) {
              this.opts.vars[v] = this.conn.getUser();
            } else {
              this.opts.vars[v] = nextProps[v];
            }
          });

          registerQuery(this.conn, this.id, this.query, this.opts);
        }
      }
    }

    render() {
      const result = this.state.result;
      const data = {
        id: this.id,
        result: result,
        error: this.state.error,
        warning: this.state.warning,
        status: this.state.status,
        loading: !(this.state.status === "loaded" || this.state.status === "error"),
        get: function get(keySeq, defaultValue) {
          keySeq = Array.isArray(keySeq) ? keySeq : [keySeq];
          let obj = result;
          let idx = 0;
          const length = keySeq.length;

          while (obj != null && idx < length) {
            obj = obj[keySeq[idx++]];
          }

          return (idx == length && obj != null) ? obj : ((defaultValue === undefined) ? obj : defaultValue);
        }
      };

      const childProps = Object.assign({}, this.props, { data: data, invoke: this.conn.invoke });

      return createElement(WrappedComponent, childProps);
    }
  }

  return hoistNonReactStatics(FlurQL, WrappedComponent, {});
}

export function flureeQL(query, opts) {
  return function (WrappedComponent) {
    return wrapComponent(WrappedComponent, query, opts);
  }
}
<commit_msg>Pass deltas through from fql-client<commit_after>import 'console-polyfill';
import hoistNonReactStatics from 'hoist-non-react-statics';
import {
  Component,
  createElement,
  PropTypes,
} from 'react';
// import localStorage from './localStorage';

let SHOULD_LOG = true;

// id counter to be used for various things that require unique identifiers
var idCounter = 0;
function nextId() {
  return idCounter++;
}

// a stateful connection id, incremented for each connection.
var connIdCounter = 0;

// map of each connection to an object containing keys:
// - ready (boolean)
// - user (user ident - two-tuple)
// - anonymous (boolean)
var connStatus = {};

// a map of each component ID to it's object
var componentIdx = {};

// for async calls to the worker, maintains the callback to execute with the result, when applicable
var callbackRegistry = {};

// holds worker reference globally. Don't initiate until first request for connection
var fqlWorker;

// worker queue
var workerQueue = [];

// worker initialized
var workerInitialized = false;

// worker.onmessage handler
function workerMessageHandler(e) {
  const msg = e.data;
  var cb;

  SHOULD_LOG && console.log("Worker received message: " + JSON.stringify(msg));

  switch (msg.event) {
    case "connInit":
      workerInitialized = true;
      workerQueue.forEach(workerInvoke);
      workerQueue = [];
      break;

    case "connStatus":
      const response = msg.data || {};
      const statusCode = response.status;
      if (connStatus[msg.conn]) {
        switch (statusCode) {
          case 200:
            connStatus[msg.conn].ready = true;
            break;
          case 401: // authorization error, need to log in
            connStatus[msg.conn].ready = false;
            connStatus[msg.conn].user = null;
            connStatus[msg.conn].anonymous = true;
            break;
          default:
            console.warn("Invalid connection response status: " + JSON.stringify(response));
            break;
        }
      }
      break;

    case "connClosed":
      cb = callbackRegistry[msg.ref];
      if (cb) {
        delete callbackRegistry[msg.ref];
        cb(msg.data);
      }
      break;

    case "connLogout":
      cb = callbackRegistry[msg.ref];
      if (cb) {
        delete callbackRegistry[msg.ref];
        cb(msg.data);
      }
      break;

    case "setState":
      const comp = componentIdx[msg.ref];
      if (comp) {
        comp.setState(msg.data);
      } else {
        SHOULD_LOG && console.warn("Component no longer registered: " + msg.ref);
      }
      break;

    case "remoteInvoke":
      // check for a callback
      cb = callbackRegistry[msg.ref];
      if (cb) {
        delete callbackRegistry[msg.ref];
        cb(msg.data);
      }
      break;

    case "login":
      // if login successful, update conn's connStatus
      if (msg.data.status === 200) {
        connStatus[msg.conn].user = msg.data.body.user;
        connStatus[msg.conn].anonymous = msg.data.body.anonymous;
      }
      // if there was a callback passed to login(), execute
      cb = callbackRegistry[msg.ref];
      if (cb) {
        delete callbackRegistry[msg.ref];
        cb(msg.data);
      }
      break;

    default:
      SHOULD_LOG && console.warn("Unreconized event from worker: " + msg.event + ". Full message: " + JSON.stringify(msg));
      break;
  }
  return;
}

function setStateCb(conn, id, stateUpdate) {
  const comp = componentIdx[id];

  if (comp) {
    comp.setState(stateUpdate);
  } else {
    SHOULD_LOG && console.warn("Component no longer registered: " + compId);
  }
}

// we use a global to track connection state, get method for it
function isReady(connId) {
  return connStatus[connId].ready;
}

// we use a global to track connection state, get method for it
function isClosed(connId) {
  const connObj = connStatus[connId];
  return (connObj && Object.keys(connObj).length === 0);
}

function workerInvoke(obj) {
  // console.log('invoke', obj.action, workerInitialized, workerQueue);

  if (obj.cb) {
    obj.ref = obj.ref || nextId();
    callbackRegistry[obj.ref] = obj.cb;
    delete obj.cb;
  }

  if(workerInitialized) {
    fqlWorker.postMessage(obj);
  } else {
    workerQueue.push(obj);
  }

  return true;
}

// Register a query, provide the connection, component ID, query and query options
export function registerQuery(conn, compId, query, opts) {
  const invokeObj = {
    conn: conn.id,
    action: "registerQuery",
    ref: compId,
    params: [compId, query, opts]
  };
  return workerInvoke(invokeObj);
}

// Remove query from registry
export function unregisterQuery(conn, compId) {
  workerInvoke({
    conn: conn.id,
    ref: compId,
    action: "unregisterQuery",
    params: [compId]
  });
}

function workerErrorHandler(error) {
  console.error('Web worker error', JSON.stringify(error));
}

// Create a new connection with settings object.
// need to provide url, instance and token keys at minumum.
export function ReactConnect(connSettings) {
  // initialize worker if not already done
  if (!fqlWorker) {
    fqlWorker = new Worker(connSettings.workerUrl || "/fqlClient.js");
    fqlWorker.onmessage = workerMessageHandler;
    fqlWorker.onerror = workerErrorHandler;
  }

  connIdCounter++;
  const connId = connIdCounter;

  const baseSetting = {
    id: connId,
    log: true,
    removeNamespace: true // by default remove namespace from results
  };

  const settings = Object.assign(baseSetting, connSettings);

  SHOULD_LOG = settings.log;

  const conn = {
    id: connId,
    isReady: () => isReady(connId),
    isClosed: () => isClosed(connId),
    login: function(username, password, cb) {
      return workerInvoke({
        conn: connId,
        action: "login",
        params: [username, password],
        cb: cb
      });
    },
    invoke: function (action, params, cb) {
      const invokeStatment = [action, params];
      return workerInvoke({
        conn: connId,
        action: "remoteInvoke",
        params: [invokeStatment],
        cb: cb
      });
    },
    getUser: function () {
      return connStatus[connId].user;
    },
    getInstance: function () {
      return connSettings.instance;
    },
    isAuthenticated: function () {
      if (connStatus[connId].anonymous === false) {
        return true;
      } else {
        return false;
      }
    },
    reset: function (cb) {
      connStatus[connId] = {
        ready: false,
        user: null,
        anonymous: true
      };
      return workerInvoke({
        conn: connId,
        action: "reset",
        params: [],
        cb: cb
      });
    },
    logout: function (cb) {
      connStatus[connId] = {
        ready: false,
        user: null,
        anonymous: true
      };
      return workerInvoke({
        conn: connId,
        action: "logout",
        params: [],
        cb: cb
      });
    },
    close: function (cb) {
      // clear out connection state held locally
      connStatus[connId] = {};
      return workerInvoke({
        conn: connId,
        action: "close",
        params: [],
        cb: cb
      });
    }
  };

  // initialize connection status, set ready to false
  connStatus[connId] = {
    ready: false,
    // if we already passed in a token, can also pass in the user/anonymous flags for storing
    user: settings.user,
    anonymous: settings.anonymous
  };

  // initiate our connection in the web worker
  workerInvoke({
    conn: 0, // conn 0 means not connection specific
    action: "connect",
    params: [settings]
  });

  // return connection object
  return conn;
}

function getDisplayName(component) {
  return component.displayName || component.name || "Component";
}

// wraps react components that need a particular connection, making the
// connection available via the context to children
export class FlureeProvider extends Component {
  static propTypes = {
    conn: PropTypes.object.isRequired
  };

  static childContextTypes = {
    conn: PropTypes.object.isRequired
  };

  constructor(props, context) {
    super(props, context);

    if (!props.conn) {
      throw "FlureeProvider was not provided a conn prop, which should be a connection object."
    }

    this.conn = props.conn;
  };

  getChildContext() {
    return {
      conn: this.conn
    }
  };

  render() {
    return React.Children.only(this.props.children);
  };
}

// given a query and options, returns a vector of variables that
// were not provided via options. We use this to look for the variables
// in props
function getMissingVars(flurQL, opts) {
  const vars = flurQL.vars;

  if (!vars || !Array.isArray(vars)) {
    return [];
  }

  if (opts && opts.vars) {
    return vars.filter((v) => { return !opts.vars[v]; });
  } else {
    return vars;
  }
}

// Create an empty map of the top level query nodes so less
// boilerplate is required to test if a property exists in the
// wrapped component
function fillDefaultResult(query) {
  if (!query) return {};

  const graph = query.graph || query;

  if (!Array.isArray(graph)) { // invalid graph
    return;
  }

  var defaultResult = {};

  graph.map(([stream, opts]) => {
    if (opts.as) {
      defaultResult[opts.as] = null;
    } else {
      defaultResult[stream] = null;
    }
  });

  return defaultResult;
}

function queryIsValid(query) {
  if (query !== null && (Array.isArray(query) || typeof query === "object")) {
    const graph = Array.isArray(query) ? query : query.graph;
    if (Array.isArray(graph) && graph.length > 0) {
      return true;
    } else {
      return false;
    }
  } else {
    return false;
  }
}

function wrapComponent(WrappedComponent, query, opts) {
  const flurQLDisplayName = `Fluree(${getDisplayName(WrappedComponent)})`;

  class FlurQL extends Component {
    static displayName = flurQLDisplayName;
    static WrappedComponent = WrappedComponent;
    static contextTypes = {
      conn: PropTypes.object.isRequired
    };

    constructor(props, context) {
      super(props, context);
      this.conn = context.conn;
      this.opts = Object.assign({ vars: {} }, opts);
      this.id = nextId();
      this.queryIsFunction = (typeof query === "function")
      this.query = this.queryIsFunction ? query(props, this.context) : query;
      this.isValidQuery = this.query && queryIsValid(this.query);
      this.missingVars = this.isValidQuery ? getMissingVars(this.query, this.opts) : []; // list of vars we need to check props for
      this.state = {
        result: this.isValidQuery ? fillDefaultResult(this.query) : {},
        error: this.query && !this.isValidQuery ? { status: 400, message: "Query is not valid: " + JSON.stringify(this.query) } : null,
        warning: this.query ? null : "No query yet, waiting...",
        status: "pending",
        loading: true
      };

      if (!this.conn) {
        throw "Could not find a Fluree connection (conn) in the context of " + flurQLDisplayName + ".";
      }
    }

    componentWillMount() {
      // get any missing vars from props and update this.opts with them
      if (this.missingVars.length !== 0) {
        this.missingVars.forEach((v) => {
          if('currentUser' === v) {
            this.opts.vars[v] = this.conn.getUser();
          } else {
            this.opts.vars[v] = this.props[v];
          }
        });
      }

      // register this component for later re-render calling, etc.
      componentIdx[this.id] = this;

      if (this.query && this.isValidQuery) {
        registerQuery(this.conn, this.id, this.query, this.opts);
      }
    }

    componentWillUnmount() {
      unregisterQuery(this.conn, this.id);
      delete componentIdx[this.id];
    }

    componentWillReceiveProps(nextProps) {
      if (this.queryIsFunction) {
        const newQuery = query(nextProps, this.context);
        this.query = newQuery;
        this.isValidQuery = queryIsValid(this.query);
        if (this.query && this.isValidQuery) {
          registerQuery(this.conn, this.id, this.query, this.opts);
        }
      } else {
        // check if any of the missing vars changed with the new props
        let didMissingVarsChange = false;

        for (let i = 0; i < this.missingVars.length; i++) {
          const varName = this.missingVars[i];
          if (this.props[varName] !== nextProps[varName]) {
            didMissingVarsChange = true;
          }
        }

        if (didMissingVarsChange === true) {
          this.missingVars.forEach((v) => {
            if('currentUser' === v) {
              this.opts.vars[v] = this.conn.getUser();
            } else {
              this.opts.vars[v] = nextProps[v];
            }
          });

          registerQuery(this.conn, this.id, this.query, this.opts);
        }
      }
    }

    render() {
      const result = this.state.result;
      const data = {
        id: this.id,
        result: result,
        deltas: this.state.deltas,
        error: this.state.error,
        warning: this.state.warning,
        status: this.state.status,
        loading: !(this.state.status === "loaded" || this.state.status === "error"),
        get: function get(keySeq, defaultValue) {
          keySeq = Array.isArray(keySeq) ? keySeq : [keySeq];
          let obj = result;
          let idx = 0;
          const length = keySeq.length;

          while (obj != null && idx < length) {
            obj = obj[keySeq[idx++]];
          }

          return (idx == length && obj != null) ? obj : ((defaultValue === undefined) ? obj : defaultValue);
        }
      };

      const childProps = Object.assign({}, this.props, { data: data, invoke: this.conn.invoke });

      return createElement(WrappedComponent, childProps);
    }
  }

  return hoistNonReactStatics(FlurQL, WrappedComponent, {});
}

export function flureeQL(query, opts) {
  return function (WrappedComponent) {
    return wrapComponent(WrappedComponent, query, opts);
  }
}
<|endoftext|>


<commit_before>const WIN_PARAMS = 'scrollbars=0, resizable=1, menubar=0, left=100, top=100, width=550, height=440, toolbar=0, status=0';

const encodeParams = (obj) =>
  Object.keys(obj)
  .filter(k => typeof(obj[k]) != 'undefined')
  .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`)
  .join('&');

const vk = (options = {}) => {
  const { url, image, isVkParse } = options;
  let { description, title } = options;

  if (description && description.length > 80) {
    description = `${description.substr(0, 80)}...`;
  }

  if (title && title.length > 80) {
    title = `${title.substr(0, 80)}...`;
  }

  let params = encodeParams({ url, title, description, image, noparse: true });
  if (isVkParse) {
    params = encodeParams({ url });
  }

  return window.open(`https://vk.com/share.php?${params}`, '_blank', WIN_PARAMS);
};

const ok = (options = {}) => {
  const { url, title } = options;

  return window.open(`https://ok.ru/dk?st.cmd=addShare&st._surl=${url}&title=${title}`, '_blank', WIN_PARAMS);
};

const fb = (options = {}) => {
  const { fbAppId, description, title, url, image, redirectUri } = options;

  if (!fbAppId) {
    throw new Error('fbAppId is not defined');
  }

  let params = `app_id=${fbAppId}&display=popup&redirect_uri=${redirectUri}`;
  params = `${params}&link=${url}&name=${title}&description=${description}&picture=${image}`;

  return window.open(`https://www.facebook.com/dialog/feed?${params}`, '_blank', WIN_PARAMS);
};

const gp = (options = {}) => {
  const { url } = options;

  return window.open(`https://plus.google.com/share?url=${url}`, '_blank', WIN_PARAMS);
};

const tw = (options = {}) => {
  const { title, url } = options;

  return window.open(`https://twitter.com/intent/tweet?text=${title}&url=${url}`, '_blank', WIN_PARAMS);
};

const mail = (options = {}) => {
  const { url, title, description, image } = options;

  return window.open(`http://connect.mail.ru/share?share_url=${url}&title=${title}&description=${description}&imageurl=${image}`, '_blank', WIN_PARAMS);
};

export {
  vk,
  ok,
  fb,
  gp,
  tw,
  mail,
};
<commit_msg>Fix lint errors<commit_after>const WIN_PARAMS = 'scrollbars=0, resizable=1, menubar=0, left=100, top=100, width=550, height=440, toolbar=0, status=0';

const encodeParams = obj =>
  Object.keys(obj)
    .filter(k => typeof obj[k] !== 'undefined')
    .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`)
    .join('&');

const vk = (options = {}) => {
  const { url, image, isVkParse } = options;
  let { description, title } = options;

  if (description && description.length > 80) {
    description = `${description.substr(0, 80)}...`;
  }

  if (title && title.length > 80) {
    title = `${title.substr(0, 80)}...`;
  }

  let params = encodeParams({ url, title, description, image, noparse: true });
  if (isVkParse) {
    params = encodeParams({ url });
  }

  return window.open(`https://vk.com/share.php?${params}`, '_blank', WIN_PARAMS);
};

const ok = (options = {}) => {
  const { url, title } = options;

  return window.open(`https://ok.ru/dk?st.cmd=addShare&st._surl=${url}&title=${title}`, '_blank', WIN_PARAMS);
};

const fb = (options = {}) => {
  const { fbAppId, description, title, url, image, redirectUri } = options;

  if (!fbAppId) {
    throw new Error('fbAppId is not defined');
  }

  let params = `app_id=${fbAppId}&display=popup&redirect_uri=${redirectUri}`;
  params = `${params}&link=${url}&name=${title}&description=${description}&picture=${image}`;

  return window.open(`https://www.facebook.com/dialog/feed?${params}`, '_blank', WIN_PARAMS);
};

const gp = (options = {}) => {
  const { url } = options;

  return window.open(`https://plus.google.com/share?url=${url}`, '_blank', WIN_PARAMS);
};

const tw = (options = {}) => {
  const { title, url } = options;

  return window.open(`https://twitter.com/intent/tweet?text=${title}&url=${url}`, '_blank', WIN_PARAMS);
};

const mail = (options = {}) => {
  const { url, title, description, image } = options;

  return window.open(`http://connect.mail.ru/share?share_url=${url}&title=${title}&description=${description}&imageurl=${image}`, '_blank', WIN_PARAMS);
};

export {
  vk,
  ok,
  fb,
  gp,
  tw,
  mail,
};
<|endoftext|>


<commit_before>import React, { Component } from "react";
import {
  Animated,
  DeviceEventEmitter,
  Dimensions,
  KeyboardAvoidingView,
  Modal,
  PanResponder,
  Platform,
  TouchableWithoutFeedback
} from "react-native";
import PropTypes from "prop-types";
import * as animatable from "react-native-animatable";
import * as ANIMATION_DEFINITIONS from "./animations";

import styles from "./index.style.js";

// Override default react-native-animatable animations
animatable.initializeRegistryWithDefinitions(ANIMATION_DEFINITIONS);

// Utility for creating our own custom react-native-animatable animations
const makeAnimation = (name, obj) => {
  animatable.registerAnimation(name, animatable.createAnimation(obj));
};

const isObject = obj => {
  return obj !== null && typeof obj === "object";
};

class ReactNativeModal extends Component {
  static propTypes = {
    animationIn: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
    animationInTiming: PropTypes.number,
    animationOut: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
    animationOutTiming: PropTypes.number,
    avoidKeyboard: PropTypes.bool,
    hasBackdrop: PropTypes.bool,
    backdropColor: PropTypes.string,
    backdropOpacity: PropTypes.number,
    backdropTransitionInTiming: PropTypes.number,
    backdropTransitionOutTiming: PropTypes.number,
    children: PropTypes.node.isRequired,
    deviceHeight: PropTypes.number,
    deviceWidth: PropTypes.number,
    isVisible: PropTypes.bool.isRequired,
    hideModalContentWhileAnimating: PropTypes.bool,
    propagateSwipe: PropTypes.bool,
    onModalShow: PropTypes.func,
    onModalWillShow: PropTypes.func,
    onModalHide: PropTypes.func,
    onModalWillHide: PropTypes.func,
    onBackButtonPress: PropTypes.func,
    onBackdropPress: PropTypes.func,
    onSwipeStart: PropTypes.func,
    onSwipeMove: PropTypes.func,
    onSwipeComplete: PropTypes.func,
    onSwipeCancel: PropTypes.func,
    swipeThreshold: PropTypes.number,
    swipeDirection: PropTypes.oneOf(["up", "down", "left", "right"]),
    useNativeDriver: PropTypes.bool,
    style: PropTypes.any,
    scrollTo: PropTypes.func,
    scrollOffset: PropTypes.number,
    scrollOffsetMax: PropTypes.number,
    supportedOrientations: PropTypes.arrayOf(
      PropTypes.oneOf([
        "portrait",
        "portrait-upside-down",
        "landscape",
        "landscape-left",
        "landscape-right"
      ])
    )
  };

  static defaultProps = {
    animationIn: "slideInUp",
    animationInTiming: 300,
    animationOut: "slideOutDown",
    animationOutTiming: 300,
    avoidKeyboard: false,
    hasBackdrop: true,
    backdropColor: "black",
    backdropOpacity: 0.7,
    backdropTransitionInTiming: 300,
    backdropTransitionOutTiming: 300,
    onModalShow: () => null,
    onModalWillShow: () => null,
    deviceHeight: null,
    deviceWidth: null,
    onModalHide: () => null,
    onModalWillHide: () => null,
    isVisible: false,
    hideModalContentWhileAnimating: false,
    propagateSwipe: PropTypes.false,
    onBackdropPress: () => null,
    onBackButtonPress: () => null,
    swipeThreshold: 100,
    useNativeDriver: false,
    scrollTo: null,
    scrollOffset: 0,
    scrollOffsetMax: 0,
    supportedOrientations: ["portrait", "landscape"]
  };

  // We use an internal state for keeping track of the modal visibility: this allows us to keep
  // the modal visibile during the exit animation, even if the user has already change the
  // isVisible prop to false.
  // We store in the state the device width and height so that we can update the modal on
  // device rotation.
  state = {
    showContent: true,
    isVisible: false,
    deviceWidth: Dimensions.get("window").width,
    deviceHeight: Dimensions.get("window").height,
    isSwipeable: this.props.swipeDirection ? true : false,
    pan: null
  };

  transitionLock = null;
  inSwipeClosingState = false;

  constructor(props) {
    super(props);
    this.buildAnimations(props);
    if (this.state.isSwipeable) {
      this.state = { ...this.state, pan: new Animated.ValueXY() };
      this.buildPanResponder();
    }
    if (this.props.isVisible) {
      this.state = {
        ...this.state,
        isVisible: true,
        showContent: true
      };
    }
  }

  UNSAFE_componentWillReceiveProps(nextProps) {
    if (!this.state.isVisible && nextProps.isVisible) {
      this.setState({ isVisible: true, showContent: true });
    }
    if (
      this.props.animationIn !== nextProps.animationIn ||
      this.props.animationOut !== nextProps.animationOut
    ) {
      this.buildAnimations(nextProps);
    }
    if (
      this.props.backdropOpacity !== nextProps.backdropOpacity &&
      this.backdropRef
    ) {
      this.backdropRef.transitionTo(
        { opacity: nextProps.backdropOpacity },
        this.props.backdropTransitionInTiming
      );
    }
  }

  componentDidMount() {
    // Show deprecation message
    if (this.props.onSwipe) {
      console.warn(
        '`<Modal onSwipe="..." />` is deprecated. Use `<Modal onSwipeComplete="..." />` instead.'
      );
    }
    if (this.state.isVisible) {
      this.open();
    }
    DeviceEventEmitter.addListener(
      "didUpdateDimensions",
      this.handleDimensionsUpdate
    );
  }

  componentWillUnmount() {
    DeviceEventEmitter.removeListener(
      "didUpdateDimensions",
      this.handleDimensionsUpdate
    );
  }

  componentDidUpdate(prevProps, prevState) {
    // On modal open request, we slide the view up and fade in the backdrop
    if (this.props.isVisible && !prevProps.isVisible) {
      this.open();
    } else if (!this.props.isVisible && prevProps.isVisible) {
      // On modal close request, we slide the view down and fade out the backdrop
      this.close();
    }
  }

  buildPanResponder = () => {
    let animEvt = null;

    if (
      this.props.swipeDirection === "right" ||
      this.props.swipeDirection === "left"
    ) {
      animEvt = Animated.event([null, { dx: this.state.pan.x }]);
    } else {
      animEvt = Animated.event([null, { dy: this.state.pan.y }]);
    }

    this.panResponder = PanResponder.create({
      onMoveShouldSetPanResponder: (evt, gestureState) => {
        // Use propagateSwipe to allow inner content to scroll. See PR:
        // https://github.com/react-native-community/react-native-modal/pull/246
        if (!this.props.propagateSwipe) {
          // The number "4" is just a good tradeoff to make the panResponder
          // work correctly even when the modal has touchable buttons.
          // For reference:
          // https://github.com/react-native-community/react-native-modal/pull/197
          const shouldSetPanResponder =
            Math.abs(gestureState.dx) >= 4 || Math.abs(gestureState.dy) >= 4;
          if (shouldSetPanResponder && this.props.onSwipeStart) {
            this.props.onSwipeStart();
          }
          return shouldSetPanResponder;
        }
      },
      onStartShouldSetPanResponder: () => {
        if (this.props.scrollTo && this.props.scrollOffset > 0) {
          return false; // user needs to be able to scroll content back up
        }
        if (this.props.onSwipeStart) {
          this.props.onSwipeStart();
        }
        return true;
      },
      onPanResponderMove: (evt, gestureState) => {
        // Dim the background while swiping the modal
        const deviceWidth = this.props.deviceWidth || this.state.deviceWidth;
        const accDistance = this.getAccDistancePerDirection(gestureState);
        const newOpacityFactor = 1 - accDistance / deviceWidth;
        if (this.isSwipeDirectionAllowed(gestureState)) {
          this.backdropRef &&
            this.backdropRef.transitionTo({
              opacity: this.props.backdropOpacity * newOpacityFactor
            });
          animEvt(evt, gestureState);
          if (this.props.onSwipeMove) {
            this.props.onSwipeMove(newOpacityFactor);
          }
        } else {
          if (this.props.scrollTo) {
            let offsetY = -gestureState.dy;
            if (offsetY > this.props.scrollOffsetMax) {
              offsetY -= (offsetY - this.props.scrollOffsetMax) / 2;
            }
            this.props.scrollTo({ y: offsetY, animated: false });
          }
        }
      },
      onPanResponderRelease: (evt, gestureState) => {
        // Call the onSwipe prop if the threshold has been exceeded
        const accDistance = this.getAccDistancePerDirection(gestureState);
        if (accDistance > this.props.swipeThreshold) {
          if (this.props.onSwipeComplete) {
            this.inSwipeClosingState = true;
            this.props.onSwipeComplete();
            return;
          }
          // Deprecated. Remove later.
          if (this.props.onSwipe) {
            this.inSwipeClosingState = true;
            this.props.onSwipe();
            return;
          }
        }
        //Reset backdrop opacity and modal position
        if (this.props.onSwipeCancel) {
          this.props.onSwipeCancel();
        }
        if (this.backdropRef) {
          this.backdropRef.transitionTo(
            { opacity: this.props.backdropOpacity },
            this.props.backdropTransitionInTiming
          );
        }
        Animated.spring(this.state.pan, {
          toValue: { x: 0, y: 0 },
          bounciness: 0
        }).start();
        if (this.props.scrollOffset > this.props.scrollOffsetMax) {
          this.props.scrollTo({
            y: this.props.scrollOffsetMax,
            animated: true
          });
        }
      }
    });
  };

  getAccDistancePerDirection = gestureState => {
    switch (this.props.swipeDirection) {
      case "up":
        return -gestureState.dy;
      case "down":
        return gestureState.dy;
      case "right":
        return gestureState.dx;
      case "left":
        return -gestureState.dx;
      default:
        return 0;
    }
  };

  isSwipeDirectionAllowed = ({ dy, dx }) => {
    const draggedDown = dy > 0;
    const draggedUp = dy < 0;
    const draggedLeft = dx < 0;
    const draggedRight = dx > 0;

    if (this.props.swipeDirection === "up" && draggedUp) {
      return true;
    } else if (this.props.swipeDirection === "down" && draggedDown) {
      return true;
    } else if (this.props.swipeDirection === "right" && draggedRight) {
      return true;
    } else if (this.props.swipeDirection === "left" && draggedLeft) {
      return true;
    }
    return false;
  };

  // User can define custom react-native-animatable animations, see PR #72
  buildAnimations = props => {
    let animationIn = props.animationIn;
    let animationOut = props.animationOut;

    if (isObject(animationIn)) {
      const animationName = JSON.stringify(animationIn);
      makeAnimation(animationName, animationIn);
      animationIn = animationName;
    }

    if (isObject(animationOut)) {
      const animationName = JSON.stringify(animationOut);
      makeAnimation(animationName, animationOut);
      animationOut = animationName;
    }

    this.animationIn = animationIn;
    this.animationOut = animationOut;
  };

  handleDimensionsUpdate = dimensionsUpdate => {
    if (!this.props.deviceHeight && !this.props.deviceWidth) {
      // Here we update the device dimensions in the state if the layout changed
      // (triggering a render)
      const deviceWidth = Dimensions.get("window").width;
      const deviceHeight = Dimensions.get("window").height;
      if (
        deviceWidth !== this.state.deviceWidth ||
        deviceHeight !== this.state.deviceHeight
      ) {
        this.setState({ deviceWidth, deviceHeight });
      }
    }
  };

  open = () => {
    if (this.transitionLock) return;
    this.transitionLock = true;
    if (this.backdropRef) {
      this.backdropRef.transitionTo(
        { opacity: this.props.backdropOpacity },
        this.props.backdropTransitionInTiming
      );
    }

    // This is for reset the pan position, if not modal get stuck
    // at the last release position when you try to open it.
    // Could certainly be improved - no idea for the moment.
    if (this.state.isSwipeable) {
      this.state.pan.setValue({ x: 0, y: 0 });
    }

    if (this.contentRef) {
      this.props.onModalWillShow && this.props.onModalWillShow();
      this.contentRef[this.animationIn](this.props.animationInTiming).then(
        () => {
          this.transitionLock = false;
          if (!this.props.isVisible) {
            this.close();
          } else {
            this.props.onModalShow();
          }
        }
      );
    }
  };

  close = () => {
    if (this.transitionLock) return;
    this.transitionLock = true;
    if (this.backdropRef) {
      this.backdropRef.transitionTo(
        { opacity: 0 },
        this.props.backdropTransitionOutTiming
      );
    }

    let animationOut = this.animationOut;

    if (this.inSwipeClosingState) {
      this.inSwipeClosingState = false;
      if (this.props.swipeDirection === "up") {
        animationOut = "slideOutUp";
      } else if (this.props.swipeDirection === "down") {
        animationOut = "slideOutDown";
      } else if (this.props.swipeDirection === "right") {
        animationOut = "slideOutRight";
      } else if (this.props.swipeDirection === "left") {
        animationOut = "slideOutLeft";
      }
    }

    if (this.contentRef) {
      this.props.onModalWillHide && this.props.onModalWillHide();
      this.contentRef[animationOut](this.props.animationOutTiming).then(() => {
        this.transitionLock = false;
        if (this.props.isVisible) {
          this.open();
        } else {
          this.setState(
            {
              showContent: false
            },
            () => {
              this.setState({
                isVisible: false
              });
            }
          );
          this.props.onModalHide();
        }
      });
    }
  };

  render() {
    const {
      animationIn,
      animationInTiming,
      animationOut,
      animationOutTiming,
      avoidKeyboard,
      hasBackdrop,
      backdropColor,
      backdropOpacity,
      backdropTransitionInTiming,
      backdropTransitionOutTiming,
      children,
      deviceHeight: deviceHeightProp,
      deviceWidth: deviceWidthProp,
      isVisible,
      onModalShow,
      onBackdropPress,
      onBackButtonPress,
      useNativeDriver,
      propagateSwipe,
      style,
      ...otherProps
    } = this.props;
    const deviceWidth = deviceWidthProp || this.state.deviceWidth;
    const deviceHeight = deviceHeightProp || this.state.deviceHeight;

    const computedStyle = [
      { margin: deviceWidth * 0.05, transform: [{ translateY: 0 }] },
      styles.content,
      style
    ];

    let panHandlers = {};
    let panPosition = {};
    if (this.state.isSwipeable) {
      panHandlers = { ...this.panResponder.panHandlers };

      if (useNativeDriver) {
        panPosition = {
          transform: this.state.pan.getTranslateTransform()
        };
      } else {
        panPosition = this.state.pan.getLayout();
      }
    }

    const _children =
      this.props.hideModalContentWhileAnimating &&
      this.props.useNativeDriver &&
      !this.state.showContent ? (
        <animatable.View />
      ) : (
        children
      );
    const containerView = (
      <animatable.View
        {...panHandlers}
        ref={ref => (this.contentRef = ref)}
        style={[panPosition, computedStyle]}
        pointerEvents="box-none"
        useNativeDriver={useNativeDriver}
        {...otherProps}
      >
        {_children}
      </animatable.View>
    );

    return (
      <Modal
        transparent={true}
        animationType={"none"}
        visible={this.state.isVisible}
        onRequestClose={onBackButtonPress}
        {...otherProps}
      >
        {hasBackdrop && (
          <TouchableWithoutFeedback onPress={onBackdropPress}>
            <animatable.View
              ref={ref => (this.backdropRef = ref)}
              useNativeDriver={true}
              style={[
                styles.backdrop,
                {
                  backgroundColor: this.state.showContent
                    ? backdropColor
                    : "transparent",
                  width: deviceWidth,
                  height: deviceHeight
                }
              ]}
            />
          </TouchableWithoutFeedback>
        )}

        {avoidKeyboard && (
          <KeyboardAvoidingView
            behavior={Platform.OS === "ios" ? "padding" : null}
            pointerEvents="box-none"
            style={computedStyle.concat([{ margin: 0 }])}
          >
            {containerView}
          </KeyboardAvoidingView>
        )}

        {!avoidKeyboard && containerView}
      </Modal>
    );
  }
}

export default ReactNativeModal;
export { ReactNativeModal };
<commit_msg>transitionLock -> isTransitioning<commit_after>import React, { Component } from "react";
import {
  Animated,
  DeviceEventEmitter,
  Dimensions,
  KeyboardAvoidingView,
  Modal,
  PanResponder,
  Platform,
  TouchableWithoutFeedback
} from "react-native";
import PropTypes from "prop-types";
import * as animatable from "react-native-animatable";
import * as ANIMATION_DEFINITIONS from "./animations";

import styles from "./index.style.js";

// Override default react-native-animatable animations
animatable.initializeRegistryWithDefinitions(ANIMATION_DEFINITIONS);

// Utility for creating our own custom react-native-animatable animations
const makeAnimation = (name, obj) => {
  animatable.registerAnimation(name, animatable.createAnimation(obj));
};

const isObject = obj => {
  return obj !== null && typeof obj === "object";
};

class ReactNativeModal extends Component {
  static propTypes = {
    animationIn: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
    animationInTiming: PropTypes.number,
    animationOut: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
    animationOutTiming: PropTypes.number,
    avoidKeyboard: PropTypes.bool,
    hasBackdrop: PropTypes.bool,
    backdropColor: PropTypes.string,
    backdropOpacity: PropTypes.number,
    backdropTransitionInTiming: PropTypes.number,
    backdropTransitionOutTiming: PropTypes.number,
    children: PropTypes.node.isRequired,
    deviceHeight: PropTypes.number,
    deviceWidth: PropTypes.number,
    isVisible: PropTypes.bool.isRequired,
    hideModalContentWhileAnimating: PropTypes.bool,
    propagateSwipe: PropTypes.bool,
    onModalShow: PropTypes.func,
    onModalWillShow: PropTypes.func,
    onModalHide: PropTypes.func,
    onModalWillHide: PropTypes.func,
    onBackButtonPress: PropTypes.func,
    onBackdropPress: PropTypes.func,
    onSwipeStart: PropTypes.func,
    onSwipeMove: PropTypes.func,
    onSwipeComplete: PropTypes.func,
    onSwipeCancel: PropTypes.func,
    swipeThreshold: PropTypes.number,
    swipeDirection: PropTypes.oneOf(["up", "down", "left", "right"]),
    useNativeDriver: PropTypes.bool,
    style: PropTypes.any,
    scrollTo: PropTypes.func,
    scrollOffset: PropTypes.number,
    scrollOffsetMax: PropTypes.number,
    supportedOrientations: PropTypes.arrayOf(
      PropTypes.oneOf([
        "portrait",
        "portrait-upside-down",
        "landscape",
        "landscape-left",
        "landscape-right"
      ])
    )
  };

  static defaultProps = {
    animationIn: "slideInUp",
    animationInTiming: 300,
    animationOut: "slideOutDown",
    animationOutTiming: 300,
    avoidKeyboard: false,
    hasBackdrop: true,
    backdropColor: "black",
    backdropOpacity: 0.7,
    backdropTransitionInTiming: 300,
    backdropTransitionOutTiming: 300,
    onModalShow: () => null,
    onModalWillShow: () => null,
    deviceHeight: null,
    deviceWidth: null,
    onModalHide: () => null,
    onModalWillHide: () => null,
    isVisible: false,
    hideModalContentWhileAnimating: false,
    propagateSwipe: PropTypes.false,
    onBackdropPress: () => null,
    onBackButtonPress: () => null,
    swipeThreshold: 100,
    useNativeDriver: false,
    scrollTo: null,
    scrollOffset: 0,
    scrollOffsetMax: 0,
    supportedOrientations: ["portrait", "landscape"]
  };

  // We use an internal state for keeping track of the modal visibility: this allows us to keep
  // the modal visibile during the exit animation, even if the user has already change the
  // isVisible prop to false.
  // We store in the state the device width and height so that we can update the modal on
  // device rotation.
  state = {
    showContent: true,
    isVisible: false,
    deviceWidth: Dimensions.get("window").width,
    deviceHeight: Dimensions.get("window").height,
    isSwipeable: this.props.swipeDirection ? true : false,
    pan: null
  };

  isTransitioning = false;
  inSwipeClosingState = false;

  constructor(props) {
    super(props);
    this.buildAnimations(props);
    if (this.state.isSwipeable) {
      this.state = { ...this.state, pan: new Animated.ValueXY() };
      this.buildPanResponder();
    }
    if (this.props.isVisible) {
      this.state = {
        ...this.state,
        isVisible: true,
        showContent: true
      };
    }
  }

  UNSAFE_componentWillReceiveProps(nextProps) {
    if (!this.state.isVisible && nextProps.isVisible) {
      this.setState({ isVisible: true, showContent: true });
    }
    if (
      this.props.animationIn !== nextProps.animationIn ||
      this.props.animationOut !== nextProps.animationOut
    ) {
      this.buildAnimations(nextProps);
    }
    if (
      this.props.backdropOpacity !== nextProps.backdropOpacity &&
      this.backdropRef
    ) {
      this.backdropRef.transitionTo(
        { opacity: nextProps.backdropOpacity },
        this.props.backdropTransitionInTiming
      );
    }
  }

  componentDidMount() {
    // Show deprecation message
    if (this.props.onSwipe) {
      console.warn(
        '`<Modal onSwipe="..." />` is deprecated. Use `<Modal onSwipeComplete="..." />` instead.'
      );
    }
    if (this.state.isVisible) {
      this.open();
    }
    DeviceEventEmitter.addListener(
      "didUpdateDimensions",
      this.handleDimensionsUpdate
    );
  }

  componentWillUnmount() {
    DeviceEventEmitter.removeListener(
      "didUpdateDimensions",
      this.handleDimensionsUpdate
    );
  }

  componentDidUpdate(prevProps, prevState) {
    // On modal open request, we slide the view up and fade in the backdrop
    if (this.props.isVisible && !prevProps.isVisible) {
      this.open();
    } else if (!this.props.isVisible && prevProps.isVisible) {
      // On modal close request, we slide the view down and fade out the backdrop
      this.close();
    }
  }

  buildPanResponder = () => {
    let animEvt = null;

    if (
      this.props.swipeDirection === "right" ||
      this.props.swipeDirection === "left"
    ) {
      animEvt = Animated.event([null, { dx: this.state.pan.x }]);
    } else {
      animEvt = Animated.event([null, { dy: this.state.pan.y }]);
    }

    this.panResponder = PanResponder.create({
      onMoveShouldSetPanResponder: (evt, gestureState) => {
        // Use propagateSwipe to allow inner content to scroll. See PR:
        // https://github.com/react-native-community/react-native-modal/pull/246
        if (!this.props.propagateSwipe) {
          // The number "4" is just a good tradeoff to make the panResponder
          // work correctly even when the modal has touchable buttons.
          // For reference:
          // https://github.com/react-native-community/react-native-modal/pull/197
          const shouldSetPanResponder =
            Math.abs(gestureState.dx) >= 4 || Math.abs(gestureState.dy) >= 4;
          if (shouldSetPanResponder && this.props.onSwipeStart) {
            this.props.onSwipeStart();
          }
          return shouldSetPanResponder;
        }
      },
      onStartShouldSetPanResponder: () => {
        if (this.props.scrollTo && this.props.scrollOffset > 0) {
          return false; // user needs to be able to scroll content back up
        }
        if (this.props.onSwipeStart) {
          this.props.onSwipeStart();
        }
        return true;
      },
      onPanResponderMove: (evt, gestureState) => {
        // Dim the background while swiping the modal
        const deviceWidth = this.props.deviceWidth || this.state.deviceWidth;
        const accDistance = this.getAccDistancePerDirection(gestureState);
        const newOpacityFactor = 1 - accDistance / deviceWidth;
        if (this.isSwipeDirectionAllowed(gestureState)) {
          this.backdropRef &&
            this.backdropRef.transitionTo({
              opacity: this.props.backdropOpacity * newOpacityFactor
            });
          animEvt(evt, gestureState);
          if (this.props.onSwipeMove) {
            this.props.onSwipeMove(newOpacityFactor);
          }
        } else {
          if (this.props.scrollTo) {
            let offsetY = -gestureState.dy;
            if (offsetY > this.props.scrollOffsetMax) {
              offsetY -= (offsetY - this.props.scrollOffsetMax) / 2;
            }
            this.props.scrollTo({ y: offsetY, animated: false });
          }
        }
      },
      onPanResponderRelease: (evt, gestureState) => {
        // Call the onSwipe prop if the threshold has been exceeded
        const accDistance = this.getAccDistancePerDirection(gestureState);
        if (accDistance > this.props.swipeThreshold) {
          if (this.props.onSwipeComplete) {
            this.inSwipeClosingState = true;
            this.props.onSwipeComplete();
            return;
          }
          // Deprecated. Remove later.
          if (this.props.onSwipe) {
            this.inSwipeClosingState = true;
            this.props.onSwipe();
            return;
          }
        }
        //Reset backdrop opacity and modal position
        if (this.props.onSwipeCancel) {
          this.props.onSwipeCancel();
        }
        if (this.backdropRef) {
          this.backdropRef.transitionTo(
            { opacity: this.props.backdropOpacity },
            this.props.backdropTransitionInTiming
          );
        }
        Animated.spring(this.state.pan, {
          toValue: { x: 0, y: 0 },
          bounciness: 0
        }).start();
        if (this.props.scrollOffset > this.props.scrollOffsetMax) {
          this.props.scrollTo({
            y: this.props.scrollOffsetMax,
            animated: true
          });
        }
      }
    });
  };

  getAccDistancePerDirection = gestureState => {
    switch (this.props.swipeDirection) {
      case "up":
        return -gestureState.dy;
      case "down":
        return gestureState.dy;
      case "right":
        return gestureState.dx;
      case "left":
        return -gestureState.dx;
      default:
        return 0;
    }
  };

  isSwipeDirectionAllowed = ({ dy, dx }) => {
    const draggedDown = dy > 0;
    const draggedUp = dy < 0;
    const draggedLeft = dx < 0;
    const draggedRight = dx > 0;

    if (this.props.swipeDirection === "up" && draggedUp) {
      return true;
    } else if (this.props.swipeDirection === "down" && draggedDown) {
      return true;
    } else if (this.props.swipeDirection === "right" && draggedRight) {
      return true;
    } else if (this.props.swipeDirection === "left" && draggedLeft) {
      return true;
    }
    return false;
  };

  // User can define custom react-native-animatable animations, see PR #72
  buildAnimations = props => {
    let animationIn = props.animationIn;
    let animationOut = props.animationOut;

    if (isObject(animationIn)) {
      const animationName = JSON.stringify(animationIn);
      makeAnimation(animationName, animationIn);
      animationIn = animationName;
    }

    if (isObject(animationOut)) {
      const animationName = JSON.stringify(animationOut);
      makeAnimation(animationName, animationOut);
      animationOut = animationName;
    }

    this.animationIn = animationIn;
    this.animationOut = animationOut;
  };

  handleDimensionsUpdate = dimensionsUpdate => {
    if (!this.props.deviceHeight && !this.props.deviceWidth) {
      // Here we update the device dimensions in the state if the layout changed
      // (triggering a render)
      const deviceWidth = Dimensions.get("window").width;
      const deviceHeight = Dimensions.get("window").height;
      if (
        deviceWidth !== this.state.deviceWidth ||
        deviceHeight !== this.state.deviceHeight
      ) {
        this.setState({ deviceWidth, deviceHeight });
      }
    }
  };

  open = () => {
    if (this.isTransitioning) return;
    this.isTransitioning = true;
    if (this.backdropRef) {
      this.backdropRef.transitionTo(
        { opacity: this.props.backdropOpacity },
        this.props.backdropTransitionInTiming
      );
    }

    // This is for reset the pan position, if not modal get stuck
    // at the last release position when you try to open it.
    // Could certainly be improved - no idea for the moment.
    if (this.state.isSwipeable) {
      this.state.pan.setValue({ x: 0, y: 0 });
    }

    if (this.contentRef) {
      this.props.onModalWillShow && this.props.onModalWillShow();
      this.contentRef[this.animationIn](this.props.animationInTiming).then(
        () => {
          this.isTransitioning = false;
          if (!this.props.isVisible) {
            this.close();
          } else {
            this.props.onModalShow();
          }
        }
      );
    }
  };

  close = () => {
    if (this.isTransitioning) return;
    this.isTransitioning = true;
    if (this.backdropRef) {
      this.backdropRef.transitionTo(
        { opacity: 0 },
        this.props.backdropTransitionOutTiming
      );
    }

    let animationOut = this.animationOut;

    if (this.inSwipeClosingState) {
      this.inSwipeClosingState = false;
      if (this.props.swipeDirection === "up") {
        animationOut = "slideOutUp";
      } else if (this.props.swipeDirection === "down") {
        animationOut = "slideOutDown";
      } else if (this.props.swipeDirection === "right") {
        animationOut = "slideOutRight";
      } else if (this.props.swipeDirection === "left") {
        animationOut = "slideOutLeft";
      }
    }

    if (this.contentRef) {
      this.props.onModalWillHide && this.props.onModalWillHide();
      this.contentRef[animationOut](this.props.animationOutTiming).then(() => {
        this.isTransitioning = false;
        if (this.props.isVisible) {
          this.open();
        } else {
          this.setState(
            {
              showContent: false
            },
            () => {
              this.setState({
                isVisible: false
              });
            }
          );
          this.props.onModalHide();
        }
      });
    }
  };

  render() {
    const {
      animationIn,
      animationInTiming,
      animationOut,
      animationOutTiming,
      avoidKeyboard,
      hasBackdrop,
      backdropColor,
      backdropOpacity,
      backdropTransitionInTiming,
      backdropTransitionOutTiming,
      children,
      deviceHeight: deviceHeightProp,
      deviceWidth: deviceWidthProp,
      isVisible,
      onModalShow,
      onBackdropPress,
      onBackButtonPress,
      useNativeDriver,
      propagateSwipe,
      style,
      ...otherProps
    } = this.props;
    const deviceWidth = deviceWidthProp || this.state.deviceWidth;
    const deviceHeight = deviceHeightProp || this.state.deviceHeight;

    const computedStyle = [
      { margin: deviceWidth * 0.05, transform: [{ translateY: 0 }] },
      styles.content,
      style
    ];

    let panHandlers = {};
    let panPosition = {};
    if (this.state.isSwipeable) {
      panHandlers = { ...this.panResponder.panHandlers };

      if (useNativeDriver) {
        panPosition = {
          transform: this.state.pan.getTranslateTransform()
        };
      } else {
        panPosition = this.state.pan.getLayout();
      }
    }

    const _children =
      this.props.hideModalContentWhileAnimating &&
      this.props.useNativeDriver &&
      !this.state.showContent ? (
        <animatable.View />
      ) : (
        children
      );
    const containerView = (
      <animatable.View
        {...panHandlers}
        ref={ref => (this.contentRef = ref)}
        style={[panPosition, computedStyle]}
        pointerEvents="box-none"
        useNativeDriver={useNativeDriver}
        {...otherProps}
      >
        {_children}
      </animatable.View>
    );

    return (
      <Modal
        transparent={true}
        animationType={"none"}
        visible={this.state.isVisible}
        onRequestClose={onBackButtonPress}
        {...otherProps}
      >
        {hasBackdrop && (
          <TouchableWithoutFeedback onPress={onBackdropPress}>
            <animatable.View
              ref={ref => (this.backdropRef = ref)}
              useNativeDriver={true}
              style={[
                styles.backdrop,
                {
                  backgroundColor: this.state.showContent
                    ? backdropColor
                    : "transparent",
                  width: deviceWidth,
                  height: deviceHeight
                }
              ]}
            />
          </TouchableWithoutFeedback>
        )}

        {avoidKeyboard && (
          <KeyboardAvoidingView
            behavior={Platform.OS === "ios" ? "padding" : null}
            pointerEvents="box-none"
            style={computedStyle.concat([{ margin: 0 }])}
          >
            {containerView}
          </KeyboardAvoidingView>
        )}

        {!avoidKeyboard && containerView}
      </Modal>
    );
  }
}

export default ReactNativeModal;
export { ReactNativeModal };
<|endoftext|>


<commit_before>import {Route, Redirect, Switch, Prompt, withRouter} from 'react-router'
import {Link, NavLink} from 'react-router-dom'
import {connect} from 'react-redux'
import model from './model'
import {actions} from './actions'
import render from './render'
import hook from './hook'
import Router from './router'
import defaults from './defaults'

module.exports = {
  model,
  actions,
  hook,
  defaults,
  connect,
  render,

  Router,
  Route,
  Link,
  NavLink,
  Switch,
  Redirect,
  Prompt,
  withRouter
}
<commit_msg>Update to ES6 modules for entry<commit_after>import {Route, Redirect, Switch, Prompt, withRouter} from 'react-router'
import {Link, NavLink} from 'react-router-dom'
import {connect} from 'react-redux'
import model from './model'
import {actions} from './actions'
import render from './render'
import hook from './hook'
import Router from './router'
import defaults from './defaults'

export default {
  model,
  actions,
  hook,
  defaults,
  connect,
  render,

  Router,
  Route,
  Link,
  NavLink,
  Switch,
  Redirect,
  Prompt,
  withRouter
}

export {
  model,
  actions,
  hook,
  defaults,
  connect,
  render,

  Router,
  Route,
  Link,
  NavLink,
  Switch,
  Redirect,
  Prompt,
  withRouter
}
<|endoftext|>


<commit_before>import StateMachine from 'javascript-state-machine';
import map from 'lodash/map';
import zipObject from 'lodash/zipObject';
import fill from 'lodash/fill';

export const initial = 'INIT';
export const defaultState =
  {
    status: initial,
    error: null,
    [initial]: true,
    event: null
  };

/**
 * @param {Object} fsmConfig - Config as for javascript-state-machine
 */
const reducerBuilder = (fsmConfig) =>
{
  const fsm = StateMachine.create(Object.assign({}, fsmConfig, { initial }));
  const { events } = fsmConfig;
  const eventNames = map(events, 'name');
  const eventsCount = eventNames.length;
  const existEvent = zipObject(eventNames, fill(Array(eventsCount),
    true, 0, eventsCount));

  return (state = defaultState, action) =>
  {
    if (typeof existEvent[action.type] === 'undefined')
    {
      // Ignore not event actions
      return state;
    }

    if (fsm.cannot(action.type))
    {
      // Set error if transition imposible
      return {
        status: state.status,
        error: true,
        [state.status]: true,
        action
      };
    }

    fsm[action.type](action);
    return {
      status: fsm.current,
      error: null,
      [fsm.current]: true,
      action
    };
  };
};

export default reducerBuilder;<commit_msg>Replace Object.assign to lodash/assign<commit_after>import StateMachine from 'javascript-state-machine';
import map from 'lodash/map';
import assign from 'lodash/assign';
import zipObject from 'lodash/zipObject';
import fill from 'lodash/fill';

export const initial = 'INIT';
export const defaultState =
  {
    status: initial,
    error: null,
    [initial]: true,
    event: null
  };

/**
 * @param {Object} fsmConfig - Config as for javascript-state-machine
 */
const reducerBuilder = (fsmConfig) =>
{
  const fsm = StateMachine.create(assign({}, fsmConfig, { initial }));
  const { events } = fsmConfig;
  const eventNames = map(events, 'name');
  const eventsCount = eventNames.length;
  const existEvent = zipObject(eventNames, fill(Array(eventsCount),
    true, 0, eventsCount));

  return (state = defaultState, action) =>
  {
    if (typeof existEvent[action.type] === 'undefined')
    {
      // Ignore not event actions
      return state;
    }

    if (fsm.cannot(action.type))
    {
      // Set error if transition imposible
      return {
        status: state.status,
        error: true,
        [state.status]: true,
        action
      };
    }

    fsm[action.type](action);
    return {
      status: fsm.current,
      error: null,
      [fsm.current]: true,
      action
    };
  };
};

export default reducerBuilder;<|endoftext|>


<commit_before>'use strict'

const typesMatch = require('./types-match')
const printValue = require('./print-value')

//=========================================== Error Stack Transforms ===========

/**
 * Alters a value to make it more suitable for printing to the console.
 * @param {*} rq
 * @return {*}
 */
function transformForConsole(rq) {
  if (typeof rq === 'function') return rq.name
  if (typeof rq === 'object' && rq.constructor === Object) {
    Object.keys(rq).forEach(key => rq[key] = transformForConsole(rq[key]))
  }
  return rq
}

/**
 * Zaps first line of the stack, as it points to TYPEOF, not the error line.
 * @param {*} stack
 * @return {String|Object} - if stack is not a string, it is returned unchanged
 */
function cleanStack(stack) {
  if (typeof stack !== 'string') return stack
  const lines = stack.split('\n')
  let cleaned = false
  return lines.filter(ln => {
    if (ln.indexOf('.js') !== -1 && !cleaned) {
      cleaned = true
      return false
    }
    return true
  }).join('\n')
}

function message(rq, arg, argNum) {
  return ('\n' +
    `    Value (${argNum}):\n` +
    `     Required: ${printValue.rq(rq)}\n` +
    `     Provided: ${printValue.arg(arg)}\n`
  )
}

//============================================================ API =============

/**
 * Accept any argument(s) and return a function which accepts the requirement(s)
 * that will be used to check the argument(s).
 * @param {...*}
 * @return {Function} check function
 */
function TYPEOF(...args) {
  return function(...rqs) {
    if (state.off) return args[0]
    let msg = ''
    let pass = true

    // Must require something.
    if (!rqs.length) pass = false

    // If void and no arguments provided, pass.
    if (rqs[0] === 'void' && !args.length) return

    // Check arity.
    if (rqs.length !== args.length) pass = false

    // Defined types.
    rqs = rqs.map(rq => (
      typeof rq === 'string' && rq in state.definedTypes
        ? state.definedTypes[rq]
        : rq
    ))

    // Check values.
    const len = rqs.length >= args.length ? rqs.length : args.length
    for (let i = 0; i < len; i++) {
      const rq = rqs.length < (i + 1) ? '__VOID' : rqs[i]
      const arg = args.length < (i + 1) ? '__VOID' : args[i]
      if (!typesMatch(rq, arg)) {
        pass = false
        msg += message(rq, arg, i + 1)
      }
    }

    // Throw and/or report a TypeError.
    if (!pass) {
      const err = new TypeError('TYPEOF\n ' + msg.replace(/"/g, ''))
      err.stack = cleanStack(err.stack)
      if (state.onFail) state.onFail(err)
      if (state.warn) console.log(err)
      else throw err
    }
    return args[0]
  }
}

//====================================================== API Methods ===========

const state = {
  onFail: null,
  warn: false,
  off: false,
  definedTypes: {}
}

TYPEOF.ONFAIL = callback => state.onFail = callback
TYPEOF.WARN = ()=> state.warn = true
TYPEOF.OFF = ()=> state.off = true
TYPEOF.DFN = (name, desc, invoke = false) => {
  if (invoke) {
    desc.__INVOKE = true
    desc.__NAME = name
  }
  state.definedTypes[name] = desc
}

//================================================ Define 'any' and '*' ========

TYPEOF.DFN('any', function() {return arguments.length > 0}, true)
TYPEOF.DFN('*', function() {return arguments.length > 0}, true)

//==============================================================================

module.exports = TYPEOF
<commit_msg>Separated check function<commit_after>'use strict'

const typesMatch = require('./types-match')
const printValue = require('./print-value')

//=========================================== Error Stack Transforms ===========

/**
 * Alters a value to make it more suitable for printing to the console.
 * @param {*} rq
 * @return {*}
 */
function transformForConsole(rq) {
  if (typeof rq === 'function') return rq.name
  if (typeof rq === 'object' && rq.constructor === Object) {
    Object.keys(rq).forEach(key => rq[key] = transformForConsole(rq[key]))
  }
  return rq
}

/**
 * Zaps first line of the stack, as it points to TYPEOF, not the error line.
 * @param {*} stack
 * @return {String|Object} - if stack is not a string, it is returned unchanged
 */
function cleanStack(stack) {
  if (typeof stack !== 'string') return stack
  const lines = stack.split('\n')
  let cleaned = false
  return lines.filter(ln => {
    if (ln.indexOf('.js') !== -1 && !cleaned) {
      cleaned = true
      return false
    }
    return true
  }).join('\n')
}

function message(rq, arg, argNum) {
  return ('\n' +
    `    Value (${argNum}):\n` +
    `     Required: ${printValue.rq(rq)}\n` +
    `     Provided: ${printValue.arg(arg)}\n`
  )
}

//======================================================== Checker =============

/**
 * Compares the types of the given arguments to the given requirements and
 * throws/reports an informative if there is a mismatch.
 * @param {Array} args
 * @param {Array} rqs
 * @return {*} - The first value being checked
 */
function check(args, rqs) {
  let msg = ''
  let pass = true

  // Must require something.
  if (!rqs.length) pass = false

  // If void and no arguments provided, pass.
  if (rqs[0] === 'void' && !args.length) return

  // Defined types.
  rqs = rqs.map(rq => (typeof rq === 'string' && rq in state.definedTypes)
    ? state.definedTypes[rq]
    : rq
  )

  // Check values.
  const len = rqs.length >= args.length ? rqs.length : args.length
  for (let i = 0; i < len; i++) {
    const rq = rqs.length < (i + 1) ? '__VOID' : rqs[i]
    const arg = args.length < (i + 1) ? '__VOID' : args[i]
    if (!typesMatch(rq, arg)) {
      pass = false
      msg += message(rq, arg, i + 1)
    }
  }
  if (pass) return args[0]

  // Throw and/or report a TypeError.
  const err = new TypeError('TYPEOF\n ' + msg.replace(/"/g, ''))
  err.stack = cleanStack(err.stack)
  if (state.onFail) state.onFail(err)
  if (state.warn) console.log(err)
  else throw err
}

//============================================================ API =============

/**
 * Accept any argument(s) and return a function which accepts the requirement(s)
 * that will be used to check the argument(s).
 * @param {...*} args
 * @return {Function} -  A dummy function or the check function
 */
function TYPEOF(...args) {
  return state.off
    ? ()=> args[0]
    : (...rqs) => check(args, rqs)
}

//====================================================== API Methods ===========

const state = {
  onFail: null,
  warn: false,
  off: false,
  definedTypes: {}
}

TYPEOF.ONFAIL = callback => state.onFail = callback
TYPEOF.WARN = ()=> state.warn = true
TYPEOF.OFF = ()=> state.off = true
TYPEOF.DFN = (name, desc, invoke = false) => {
  if (invoke) {
    desc.__INVOKE = true
    desc.__NAME = name
  }
  state.definedTypes[name] = desc
}

//================================================ Define 'any' and '*' ========

TYPEOF.DFN('any', function() {return arguments.length > 0}, true)
TYPEOF.DFN('*', function() {return arguments.length > 0}, true)

//==============================================================================

module.exports = TYPEOF
<|endoftext|>


<commit_before>var config = require('./bin/config');
var mysql = require('mysql');
var http = require('http');

var connection = mysql.createConnection({
    host: config.host,
    user: 'root',
    password: ''
});

var sql = 'Mysql none.';

connection.connect(function(err) {
    if (err) {
        sql = 'error connecting: '+ err.stack;
        return;
    }

    sql = 'connected as id '+ connection.threadId;
});

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('2Всем привет!\n' + '
sql: '+ sql);
}).listen(process.env.PORT||80, process.env.IP||null);

console.log('Server running at.');
<commit_msg>"update"<commit_after>var config = require('./bin/config');
var mysql = require('mysql');
var http = require('http');

var connection = mysql.createConnection({
    host: config.host,
    user: 'root',
    password: '',
    database: 'Test'
});

var sql = 'Mysql none.';

connection.connect(function(err) {
    if (err) {
        sql = 'error connecting: '+ err.stack;
        return;
    }

    sql = 'connected as id '+ connection.threadId;
});

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('2Всем привет!\n' + '
sql: '+ sql);
}).listen(process.env.PORT||80, process.env.IP||null);

console.log('Server running at.');
<|endoftext|>


<commit_before>const Promise     = require('bluebird');
const _           = require('lodash');
const assert      = require('assert');

const EventQueue = require('./event-queue');

const telegramPlatform  = require('./platforms/telegram');
const ircPlatform       = require('./platforms/irc');

const log = require('./logger')(__filename);

class Core {
  constructor() {
    // The handlers
    this._monitors = [];
    this._commanders = [];
    this._responders = [];

    // The platforms
    this._availablePlatforms = [
      telegramPlatform,
      ircPlatform
    ];

    // Event queue
    this._queue = new EventQueue();

    // The bots (instances of platforms)
    this._bots = {};
  }

  // Use this if you don't want to use "new" keyword
  static create() {
    return new Core();
  }

  createBot(type, name, options) {

    let chosenPlatform = _.find(this._availablePlatforms, platform => platform.type === type);

    // Platform not found
    if (!chosenPlatform) {
      return Promise.reject(`Platform ${type} not available`);
    }

    // Invalid name
    if (!name) {
      return Promise.reject('Missing parameter "name"');
    }

    // Allow only unique names
    if (this._bots[name]) {
      return Promise.reject(`Bot with name "${name}" has already been created.`);
    }

    // All ok, create bot
    let newBot = new chosenPlatform(name, options);
    this._bots[name] = newBot;

    // Subscribe to pipeline
    newBot.on('event', event => this.processEvent(event));

    log.debug(`Bot "${name}" created from ${type} platform succesfully`);

    // Returns the underlying library so developer can interact
    // with it directly if necessary (setting webhooks etc.)
    return Promise.resolve(newBot);

  }

  getAvailablePlatforms() {
    return this._availablePlatforms.map(platform => platform.type);
  }

  addPlatfrom(platform) {
    if (!_.isFunction(platform.onMessage)) {
      return Promise.reject('Missing required function onMessage()');

    } else if (!_.isFunction(platform.handleActions)) {
      return Promise.reject('Missing required function handleActions()');

    } else if (_.isUndefined(platform.type)) {
      return Promise.reject('Platform type is undefined');

    } else if (_.find(this._availablePlatforms.map(oldPlat => oldPlat.type), platform.type)) {
      return Promise.reject(`Platform with type ${platform.type} already exists`);

    // TODO: Check rest of the error cases (if there is)

    } else {
      this._availablePlatforms.push(platform);
      return Promise.resolve(platform);
    }
  }

  addResponder(responderInstance) {
    if (!_.isFunction(responderInstance.handleEvent)) {
      log.error(`No handleEvent defined for ${responderInstance.constructor.name} ignoring!`);
    } else {
      this._responders.push(responderInstance);
    }
  }

  addCommander(commanderInstance) {
    if (!_.isFunction(commanderInstance.getBidForEvent)) {
      log.error(`No getBidForEvent defined for ${commanderInstance.constructor.name}!`);
    } else if (!_.isFunction(commanderInstance.handleEvent)) {
      log.error(`No handleEvent defined for ${commanderInstance.constructor.name} ignoring!`);
    } else {
      this._commanders.push(commanderInstance);
    }
  }

  addMonitor(monitorInstance) {
    if (!_.isFunction(monitorInstance.handleEvent)) {
      log.error(`No handleEvent defined for ${monitorInstance.constructor.name} ignoring!`);
    } else {
      this._monitors.push(monitorInstance);
    }
  }

  // "The start of the Pipeline"
  processEvent(event) {
    if (this._queue.hasEvent(event.eventId, event.fromBot)) {
      // this event is already in progress -> do nothing
      return Promise.resolve();
    }

    // Check for possible existing conflict situattions
    // TODO: implement

    // Send event to all monitors
    this.sendEventToMonitors(event);

    // Send message to "Pipeline"
    return Promise.resolve()
      .then(() => this.getActions(event))
      .then(actions => this.executeActions(actions, event))
      .catch(e => {
        log.error('Error occurred on the pipeline: ', e);
        return Promise.resolve();
      });
  }

  sendEventToMonitors(event) {
    this._monitors.forEach(monitor => monitor.handleEvent(event));
  }

  getActions(event) {
    return Promise.resolve()
      .then(() => this.getActionsFromCommanders(event))
      .then(commanderActions => {
        // If only one action is returned, wrap it into array
        commanderActions = !_.isArray(commanderActions)
         ? [commanderActions] : commanderActions;

        assert(_.isArray(commanderActions)); // Remove from production

        if (commanderActions.length > 0) {
          return commanderActions;

        } else {
          // No actions from Commanders - ask from Responders
          return this.getActionsFromResponders(event);
        }
      });
  }

  executeActions(actions, event) {
    // In here we should have an array of "actions".
    // These actions may have come from Commanders or Responders,
    // it shouldn't matter at this point.
    //
    // (The actions of Responders will have that "priority" thing since all Responders
    //  will handle all the events, but let's figure out that later)
    //
    // In here the actions should be "cleared" by using whatever bot platform we
    // currently are using.

    assert(_.isArray(actions)); // Remove from production

    log.debug('Got actions', actions);

    // Fill the missing attributes
    actions.forEach(action => {
      action.toBot = action.toBot || event.fromBot; // TODO: How users can decide the bot via actionCreator?
      action.payload.target = action.payload.target || event.userId;
    });

    // Send actions to bots so they can execute the required actions
    _.forEach(
      _.groupBy(actions, 'toBot'),
      (botActions, bot) => this._bots[bot].handleActions(botActions)
    );

    return actions;
  }

  getActionsFromCommanders(event) {
    // # Command & Respond -loop
    const commandHandlerCandidates = _.map(this._commanders, cmdr =>
      cmdr.getBidForEvent(event)
    );

    // Hack to get only solved promises
    // (http://stackoverflow.com/questions/30309273/keep-the-values-only-from-the-promises-that-resolve-and-ignore-the-rejected)
    return Promise.all(commandHandlerCandidates.map(promise => promise.reflect()))
    .filter(promise => promise.isFulfilled())
    .map(promise => promise.value())
    .then(function solveHandlerCommander(handlerBids) {
      let winningBid = null;

      if (handlerBids.length > 1) {
        // More than one commander was interested in the event,
        // tell user about this conflict

        // TODO: Make user to decide which commander will take the control
        //  (this will require the Core to have some internal state which
        //   will listen for the user's response, and block those messages
        //   from going on. How to do that platform agnostically?)
        log.error('Got more bids from commanders than one, fallbacking to the first one');

        winningBid = handlerBids[0]; // TODO temp hack

      } else if (handlerBids.length === 1) {
        // Take the only bid we got
        winningBid = handlerBids[0];
      }

      // Do we have a winner or not?
      // TODO: does the "bid conflict case" need some attention/branch in here?
      if (!_.isNull(winningBid)) {
        // Get actions from the "winning Commander"
        return winningBid.commander.handleEvent(event);

      } else {
        // There was no interested from commanders, send empty array to
        // state that there are no actions from commanders
        return [];
      }
    });
  }

  getActionsFromResponders(event) {
    const responderActionPromises = _.map(this._responders, rspndr =>
      rspndr.handleEvent(event)
    );

    return Promise.all(responderActionPromises.map(promise => promise.reflect()))
      .filter(promise => promise.isFulfilled())
      .map(reflectedPromise => reflectedPromise.value())
      .then(responderActionCandidates => {

        // TODO: in here we have 0-n arrays of actions for each responder which resolved
        // our handleEvent call. Implement some fancy logic for selecting which responder
        // to use or combine them or whatever!
        //
        // For now we just select the first one.
        if (_.isArray(responderActionCandidates) && responderActionCandidates.length > 0) {
          return Promise.resolve(responderActionCandidates[0]);
        } else {
          return Promise.resolve([]);
        }
      });
  }
}

module.exports = Core;
<commit_msg>Minor optimizations<commit_after>const Promise     = require('bluebird');
const _           = require('lodash');
const assert      = require('assert');

const EventQueue = require('./event-queue');

const telegramPlatform  = require('./platforms/telegram');
const ircPlatform       = require('./platforms/irc');

const log = require('./logger')(__filename);

class Core {
  constructor() {
    // The handlers
    this._monitors = [];
    this._commanders = [];
    this._responders = [];

    // The platforms
    this._availablePlatforms = [
      telegramPlatform,
      ircPlatform
    ];

    // Event queue
    this._queue = new EventQueue();

    // The bots (instances of platforms)
    this._bots = {};
  }

  // Use this if you don't want to use "new" keyword
  static create() {
    return new Core();
  }

  createBot(type, name, options) {

    let errMsg = null;
    let chosenPlatform = _.find(this._availablePlatforms, platform => platform.type === type);

    // Platform not found
    if (!chosenPlatform) {
      errMsg = `Platform ${type} not available`;
    }

    // Invalid name
    else if (!name) {
      errMsg = 'Missing parameter "name"';
    }

    // Allow only unique names
    else if (this._bots[name]) {
      errMsg = `Bot with name "${name}" has already been created.`;
    }

    // There was an error
    if (errMsg) {
      return Promise.reject(errMsg);

    // All ok, create a new bot
    } else {

      let newBot = new chosenPlatform(name, options);
      this._bots[name] = newBot;

      // Subscribe to pipeline
      newBot.on('event', event => this.processEvent(event));
      log.debug(`Bot "${name}" created from ${type} platform succesfully`);

      return Promise.resolve(newBot);

    }
  }

  getAvailablePlatforms() {
    return this._availablePlatforms.map(platform => platform.type);
  }

  addPlatfrom(platform) {
    let errMsg = null;

    if (!_.isFunction(platform.onMessage)) {
      errMsg = 'Missing required function onMessage()';

    } else if (!_.isFunction(platform.handleActions)) {
      errMsg = 'Missing required function handleActions()';

    } else if (_.isUndefined(platform.type)) {
      errMsg = 'Platform type is undefined';

    } else if (_.find(this._availablePlatforms.map(oldPlat => oldPlat.type), platform.type)) {
      errMsg = `Platform with type ${platform.type} already exists`;

    }
    // TODO: Check rest of the error cases (if there is any)

    if (errMsg) {
      return Promise.reject(errMsg);

    } else {
      this._availablePlatforms.push(platform);
      return Promise.resolve(platform);
    }
  }

  addResponder(responderInstance) {
    if (!_.isFunction(responderInstance.handleEvent)) {
      log.error(`No handleEvent defined for ${responderInstance.constructor.name} ignoring!`);
    } else {
      this._responders.push(responderInstance);
    }
  }

  addCommander(commanderInstance) {
    if (!_.isFunction(commanderInstance.getBidForEvent)) {
      log.error(`No getBidForEvent defined for ${commanderInstance.constructor.name}!`);
    } else if (!_.isFunction(commanderInstance.handleEvent)) {
      log.error(`No handleEvent defined for ${commanderInstance.constructor.name} ignoring!`);
    } else {
      this._commanders.push(commanderInstance);
    }
  }

  addMonitor(monitorInstance) {
    if (!_.isFunction(monitorInstance.handleEvent)) {
      log.error(`No handleEvent defined for ${monitorInstance.constructor.name} ignoring!`);
    } else {
      this._monitors.push(monitorInstance);
    }
  }

  // "The start of the Pipeline"
  processEvent(event) {
    if (this._queue.hasEvent(event.eventId, event.fromBot)) {
      // this event is already in progress -> do nothing
      return Promise.resolve();
    }

    // Check for possible existing conflict situattions
    // TODO: implement

    // Send event to all monitors
    this.sendEventToMonitors(event);

    // Send message to "Pipeline"
    return Promise.resolve()
      .then(() => this.getActions(event))
      .then(actions => this.executeActions(actions, event))
      .catch(e => {
        log.error('Error occurred on the pipeline: ', e);
        return Promise.resolve();
      });
  }

  sendEventToMonitors(event) {
    this._monitors.forEach(monitor => monitor.handleEvent(event));
  }

  getActions(event) {
    return Promise.resolve()
      .then(() => this.getActionsFromCommanders(event))
      .then(commanderActions => {
        // If only one action is returned, wrap it into array
        commanderActions = !_.isArray(commanderActions)
         ? [commanderActions] : commanderActions;

        assert(_.isArray(commanderActions)); // Remove from production

        if (commanderActions.length > 0) {
          return commanderActions;

        } else {
          // No actions from Commanders - ask from Responders
          return this.getActionsFromResponders(event);
        }
      });
  }

  executeActions(actions, event) {
    // In here we should have an array of "actions".
    // These actions may have come from Commanders or Responders,
    // it shouldn't matter at this point.
    //
    // (The actions of Responders will have that "priority" thing since all Responders
    //  will handle all the events, but let's figure out that later)
    //
    // In here the actions should be "cleared" by using whatever bot platform we
    // currently are using.

    assert(_.isArray(actions)); // Remove from production

    log.debug('Got actions', actions);

    // Fill the missing attributes
    actions.forEach(action => {
      action.toBot = action.toBot || event.fromBot; // TODO: How users can decide the bot via actionCreator?
      action.payload.target = action.payload.target || event.userId;
    });

    // Send actions to bots so they can execute the required actions
    _.forEach(
      _.groupBy(actions, 'toBot'),
      (botActions, bot) => this._bots[bot].handleActions(botActions)
    );

    return actions;
  }

  getActionsFromCommanders(event) {

    // No commanders set to core, skip
    if (_.isEmpty(this._commanders)) {
      return Promise.resolve([]);
    }

    // # Command & Respond -loop
    const commandHandlerCandidates = _.map(this._commanders, cmdr =>
      cmdr.getBidForEvent(event)
    );

    // Hack to get only solved promises
    // (http://stackoverflow.com/questions/30309273/keep-the-values-only-from-the-promises-that-resolve-and-ignore-the-rejected)
    return Promise.all(commandHandlerCandidates.map(promise => promise.reflect()))
    .filter(promise => promise.isFulfilled())
    .map(promise => promise.value())
    .then(function solveHandlerCommander(handlerBids) {
      let winningBid = null;

      if (handlerBids.length > 1) {
        // More than one commander was interested in the event,
        // tell user about this conflict

        // TODO: Make user to decide which commander will take the control
        //  (this will require the Core to have some internal state which
        //   will listen for the user's response, and block those messages
        //   from going on. How to do that platform agnostically?)
        log.error('Got more bids from commanders than one, fallbacking to the first one');

        winningBid = handlerBids[0]; // TODO temp hack

      } else if (handlerBids.length === 1) {
        // Take the only bid we got
        winningBid = handlerBids[0];
      }

      // Do we have a winner or not?
      // TODO: does the "bid conflict case" need some attention/branch in here?
      if (!_.isNull(winningBid)) {
        // Get actions from the "winning Commander"
        return winningBid.commander.handleEvent(event);

      } else {
        // There was no interested from commanders, send empty array to
        // state that there are no actions from commanders
        return Promise.resolve([]);
      }
    });
  }

  getActionsFromResponders(event) {

    // No responders set to core, skip
    if (_.isEmpty(this._responders)) {
      return Promise.resolve([]);
    }

    const responderActionPromises = _.map(this._responders, rspndr =>
      rspndr.handleEvent(event)
    );

    return Promise.all(responderActionPromises.map(promise => promise.reflect()))
      .filter(promise => promise.isFulfilled())
      .map(reflectedPromise => reflectedPromise.value())
      .then(responderActionCandidates => {

        // TODO: in here we have 0-n arrays of actions for each responder which resolved
        // our handleEvent call. Implement some fancy logic for selecting which responder
        // to use or combine them or whatever!
        //
        // For now we just select the first one.
        if (_.isArray(responderActionCandidates) && responderActionCandidates.length > 0) {
          return Promise.resolve(responderActionCandidates[0]);
        } else {
          return Promise.resolve([]);
        }
      });
  }
}

module.exports = Core;
<|endoftext|>