<commit_before>'use strict';

const _ = require('underscore');
const AbstractSyntaxTree = require('@buxlabs/ast');

class Module extends AbstractSyntaxTree {

    convert () {
        var pairs = this.getDependencyPairs();
        if (pairs.length > 0) {
            this.remove({ type: 'ImportDeclaration' });
            this.prependUseStrict();
            if (this.has('ExportDefaultDeclaration')) {
                this.convertExportDefaultToReturn();
            } else if (this.has('ExportNamedDeclaration')) {
                this.convertExportNamedToReturn();
            }
            this.normalizeIdentifiers(pairs);
            this.wrapWithDefineWithPairs(_.unique(pairs, item => item.element + item.param));
        } else if (this.has('ExportDefaultDeclaration')) {
            this.convertExportDefaultDeclarationToDefine();
        } else if (this.has('ExportNamedDeclaration')) {
            this.convertExportNamedDeclarationToDefine();
        }
    }

    prependUseStrict () {
        this.prepend({
            type: 'ExpressionStatement',
            expression: {
                type: 'Literal',
                value: 'use strict'
            }
        });
    }

    getIdentifiers () {
        return _.unique(this.find('Identifier').map(item => item.name));
    }

    generateFreeIdentifier (takenIdentifiers) {
        var identifiers = _.unique(_.flatten(this.getIdentifiers())).concat(takenIdentifiers);
        var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
        var index = 0;
        while (identifiers.indexOf(alphabet[index]) !== -1) {
            index += 1;
            if (index === alphabet.length) {
                index = 0;
                alphabet = alphabet.map(character => '_' + character);
            }
        }
        return alphabet[index];
    }

    getDependencyPairs () {
        var dependencyToIdentifierMap = {};
        return _.flatten(this.ast.body.map(function (node) {
            if (node.type === 'ImportDeclaration') {
                if (node.source &&
                    node.source.type === 'Literal' &&
                    node.specifiers.length === 0) {
                    return {
                        element: node.source.value
                    };
                }
                return node.specifiers.map(function (specifier) {

                    if (specifier.type === 'ImportSpecifier') {
                        var identifier;
                        var value = node.source.value;
                        if (dependencyToIdentifierMap.hasOwnProperty(value)) {
                            identifier = dependencyToIdentifierMap[value];
                        } else {
                            identifier = this.generateFreeIdentifier(Object.values(dependencyToIdentifierMap));
                            dependencyToIdentifierMap[value] = identifier;
                        }
                        return {
                            element: node.source.value,
                            param: identifier,
                            name: specifier.local.name
                        };
                    }
                    return {
                        element: node.source.value,
                        param: specifier.local.name
                    };
                }.bind(this));
            }
            return null;
        }.bind(this)).filter(node => !!node));
    }

    convertExportDefaultToReturn () {
        this.ast.body = this.ast.body.map(function (node) {
            if (node.type === 'ExportDefaultDeclaration') {
                return {
                    type: 'ReturnStatement',
                    argument: node.declaration
                };
            }
            return node;
        });
    }

    convertExportNamedToReturn () {
        var declarations = this.getExportNamedDeclarations();
        this.replace({
            enter: function (node) {
                if (node.type === 'ExportNamedDeclaration') {
                    return node.declaration;
                }
            }
        });
        this.append({
            type: 'ReturnStatement',
            argument: this.getObjectExpression(declarations)
        });
    }

    convertExportDefaultDeclarationToDefine () {
        this.prependUseStrict();
        this.replace({
            enter: function (node) {
                if (node.type === 'ExportDefaultDeclaration') {
                    node.type = 'ReturnStatement';
                    node.argument = node.declaration;
                    return node;
                }
            }
        });
        var body = this.ast.body;
        this.ast.body = [this.getDefine([{
            type: 'FunctionExpression',
            params: [],
            body: {
                type: 'BlockStatement',
                body: body
            }
        }])];
    }

    getDefine (nodes) {
        return {
            type: 'ExpressionStatement',
            expression: {
                type: 'CallExpression',
                callee: { type: 'Identifier', name: 'define' },
                arguments: nodes
            }
        };
    }

    getExportNamedDeclarations () {
        return this.ast.body.filter(node => {
            return node.type === 'ExportNamedDeclaration';
        });
    }

    convertExportNamedDeclarationToDefine () {
        this.ast.body = [
            this.getDefine([
                this.getExportNamedDeclarationsBody()
            ])
        ];
    }

    getExportNamedDeclarationsBody () {
        var declarations = this.getExportNamedDeclarations();
        this.ast.body = this.ast.body.filter(node => {
            return node.type !== 'ExportNamedDeclaration';
        });
        return this.convertExportNamedDeclarationToBody(declarations);
    }

    convertExportNamedDeclarationToBody (declarations) {
        this.prependUseStrict();
        return this.getFunctionExpression([], this.ast.body.concat(
            declarations.map(declaration => declaration.declaration),
            [{
                type: 'ReturnStatement',
                argument: this.getObjectExpression(declarations)
            }]
        ));
    }

    getFunctionExpression (params, body) {
        return {
            type: 'FunctionExpression',
            params: params,
            body: {
                type: 'BlockStatement',
                body: body
            }
        };
    }

    getObjectExpression (declarations) {
        return {
            "type": "ObjectExpression",
            "properties": declarations.map(declaration => {
                if (declaration.declaration.type === "VariableDeclaration") {
                    return {
                        type: "Property",
                        "key": declaration.declaration.declarations[0].id,
                        "value": declaration.declaration.declarations[0].id,
                        "kind": "init"
                    };
                }
                return {
                    type: "Property",
                    "key": declaration.declaration.id,
                    "value": declaration.declaration.id,
                    "kind": "init"
                };

            })
        };
    }

    normalizeIdentifiers (pairs) {
        let nodes = pairs.filter(pair => !!pair.name);
        let names = nodes.map(node => node.name);
        this.replace({
            leave: function (current, parent) {
                if (current.type === 'Identifier') {
                    let index = names.indexOf(current.name);
                    if (index !== -1) {
                        let pair = nodes[index];
                        return {
                            type: 'MemberExpression',
                            object: {
                                type: 'Identifier',
                                name: pair.param
                            },
                            property: {
                                type: 'Identifier',
                                name: pair.name
                            }
                        };
                    }
                }
                return current;
            }
        });
    }

    wrapWithDefineWithPairs (pairs) {
        var body = this.ast.body;
        var elements = pairs.map(pair => pair.element)
        .map(function (element) {
            return { type: 'Literal', value: element };
        });
        var params = pairs.filter(pair => pair.param).map(pair => pair.param)
        .map(function(param) {
            return { type: 'Identifier', name: param };
        });
        this.ast.body = [this.getDefine([
            {
                type: 'ArrayExpression',
                elements: elements
            },
            {
                type: 'FunctionExpression',
                params: params,
                body: {
                    type: 'BlockStatement',
                    body: body
                }
            }
        ])];
    }

}

module.exports = Module;
<commit_msg>First pass of code simplification<commit_after>'use strict';

const _ = require('underscore');
const AbstractSyntaxTree = require('@buxlabs/ast');

class Module extends AbstractSyntaxTree {

    convert () {
        if (this.has('ImportDeclaration')) {
            var pairs = this.getDependencyPairs();
            this.remove({ type: 'ImportDeclaration' });
            this.normalizePairs(pairs);
            if (this.has('ExportDefaultDeclaration')) {
                this.convertExportDefaultDeclarationToReturn();
            } else if (this.has('ExportNamedDeclaration')) {
                this.convertExportNamedDeclarations();
            }
            this.prependUseStrictLiteral();
            this.wrapWithDefineWithArrayExpression(pairs);
        } else if (this.has('ExportDefaultDeclaration')) {
            this.convertExportDefaultDeclarationToDefine();
        } else if (this.has('ExportNamedDeclaration')) {
            this.convertExportNamedDeclarationToDefine();
        }
    }

    prependUseStrictLiteral () {
        this.prepend({
            type: 'ExpressionStatement',
            expression: {
                type: 'Literal',
                value: 'use strict'
            }
        });
    }

    getIdentifiers () {
        return this.find('Identifier');
    }

    generateFreeIdentifier (takenIdentifiers) {
        var ids = _.unique(this.getIdentifiers().map(item => item.name));
        var identifiers = _.unique(_.flatten(ids)).concat(takenIdentifiers);
        var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
        var index = 0;
        while (identifiers.indexOf(alphabet[index]) !== -1) {
            index += 1;
            if (index === alphabet.length) {
                index = 0;
                alphabet = alphabet.map(character => '_' + character);
            }
        }
        return alphabet[index];
    }

    isSideEffectImportDeclaration(node) {
        return node.source && node.source.type === 'Literal' && node.specifiers.length === 0;
    }

    isImportDefaultSpecifier(specifier) {
        return specifier.type === 'ImportDefaultSpecifier';
    }

    getDependencyPairs () {
        var dependencyToIdentifierMap = {};
        var imports = this.find('ImportDeclaration');
        return _.flatten(imports.map(node => {
            if (this.isSideEffectImportDeclaration(node)) {
                return {
                    element: node.source.value
                };
            }
            return node.specifiers.map(function (specifier) {
                if (this.isImportDefaultSpecifier(specifier)) {
                    return {
                        element: node.source.value,
                        param: specifier.local.name
                    };
                }
                if (specifier.type === 'ImportSpecifier') {
                    var identifier;
                    var value = node.source.value;
                    if (dependencyToIdentifierMap.hasOwnProperty(value)) {
                        identifier = dependencyToIdentifierMap[value];
                    } else {
                        identifier = this.generateFreeIdentifier(Object.values(dependencyToIdentifierMap));
                        dependencyToIdentifierMap[value] = identifier;
                    }
                    return {
                        element: node.source.value,
                        param: identifier,
                        name: specifier.local.name
                    };
                }
            }.bind(this));
        }));
    }

    convertExportNamedDeclarations () {
        var declarations = this.find('ExportNamedDeclaration');
        this.convertExportNamedDeclarationToDeclaration();
        this.append({
            type: 'ReturnStatement',
            argument: this.getObjectExpression(declarations)
        });
    }

    convertExportNamedDeclarationToDeclaration () {
        this.replace({
            enter: function (node) {
                if (node.type === 'ExportNamedDeclaration') {
                    return node.declaration;
                }
            }
        });
    }

    convertExportDefaultDeclarationToDefine () {
        this.prependUseStrictLiteral();
        this.convertExportDefaultDeclarationToReturn();
        this.wrap(body => {
            return [this.getDefineWithFunctionExpression(body)];
        });
    }

    getDefineWithFunctionExpression (body) {
        return this.getDefine([this.getFunctionExpression([], body)]);
    }

    convertExportDefaultDeclarationToReturn () {
        this.replace({
            enter: node => {
                if (node.type === 'ExportDefaultDeclaration') {
                    node.type = 'ReturnStatement';
                    node.argument = node.declaration;
                    return node;
                }
            }
        });

    }

    getDefine (nodes) {
        return {
            type: 'ExpressionStatement',
            expression: {
                type: 'CallExpression',
                callee: { type: 'Identifier', name: 'define' },
                arguments: nodes
            }
        };
    }

    convertExportNamedDeclarationToDefine () {
        this.prependUseStrictLiteral();
        this.convertExportNamedDeclarations();
        this.wrap(body => {
            return [this.getDefineWithFunctionExpression(body)];
        });
    }

    getFunctionExpression (params, body) {
        return {
            type: 'FunctionExpression',
            params: params,
            body: {
                type: 'BlockStatement',
                body: body
            }
        };
    }

    getObjectExpression (declarations) {
        return {
            "type": "ObjectExpression",
            "properties": declarations.map(declaration => {
                if (declaration.declaration.type === "VariableDeclaration") {
                    return {
                        type: "Property",
                        "key": declaration.declaration.declarations[0].id,
                        "value": declaration.declaration.declarations[0].id,
                        "kind": "init"
                    };
                }
                return {
                    type: "Property",
                    "key": declaration.declaration.id,
                    "value": declaration.declaration.id,
                    "kind": "init"
                };

            })
        };
    }

    normalizePairs (pairs) {
        let nodes = pairs.filter(pair => !!pair.name);
        let names = nodes.map(node => node.name);
        this.replace({
            leave: function (current, parent) {
                if (current.type === 'Identifier') {
                    let index = names.indexOf(current.name);
                    if (index !== -1) {
                        let pair = nodes[index];
                        return {
                            type: 'MemberExpression',
                            object: {
                                type: 'Identifier',
                                name: pair.param
                            },
                            property: {
                                type: 'Identifier',
                                name: pair.name
                            }
                        };
                    }
                }
                return current;
            }
        });
    }

    getArrayExpression (elements) {
        return { type: 'ArrayExpression', elements: elements };
    }

    wrapWithDefineWithArrayExpression (pairs) {
        pairs = _.unique(pairs, item => item.element + item.param);
        var elements = pairs.map(pair => pair.element)
        .map(function (element) {
            return { type: 'Literal', value: element };
        });
        var params = pairs.filter(pair => pair.param).map(pair => pair.param)
        .map(function(param) {
            return { type: 'Identifier', name: param };
        });
        this.wrap(body => {
            return [this.getDefine([
                this.getArrayExpression(elements),
                this.getFunctionExpression(params, body)
            ])];
        });
    }

}

module.exports = Module;
<|endoftext|>


<commit_before>import bs from 'stamp'
import { send, meta, sendSubscriptions } from './send'
import WebSocket from './websocket'
import {
  create,
  parse,
  subscribe,
  struct,
  emitterProperty,
  puid,
  getKeys
} from 'brisky-struct'
import serialize from '../subscription/serialize'
import hash from 'string-hash'
import createClient from './create'
import { receiveLarge } from '../size'
import hub from '../hub'

const isNode = typeof window === 'undefined'

// want to use for upsteream
const next = isNode
  ? fn => setTimeout(fn, 18)
  : global.requestAnimationFrame

// const cancel = isNode
//   ? clearTimeout
//   : global.cancelAnimationFrame

const connect = (hub, url, reconnect) => {
  // use outside function non anon since its slower (according to uws)

  const socket = new WebSocket(url)
  const client = hub.client || createClient(hub, {}, false)
  // var inProgress, queue

  hub.set({ client }, false)

  hub.reconnect = null

  const close = () => {
    const stamp = bs.create()
    hub.socket = false
    hub.set({ connected: false }, stamp)
    bs.close()
    if (!socket.blockReconnect && hub._url_) {
      reconnect = Math.min((reconnect * 1.5), 2000)
      hub.reconnect = setTimeout(connect, reconnect, hub, url, reconnect)
    }
  }

  socket.onclose = close

  socket.onerror = isNode ? close : () => socket.close()

  socket.onopen = () => {
    hub.socket = socket
    if (hub.emitters && hub.emitters.incoming) {
      enableIncomingListener(socket, hub)
    }
  }

  socket.onmessage = (data) => {
    data = data.data

    if (
      typeof data !== 'string' &&
      (data instanceof ArrayBuffer ||
        (!isNode &&
          ((('Blob' in global) && data instanceof Blob) || // eslint-disable-line
          (('WebkitBlob' in global) && data instanceof WebkitBlob)) // eslint-disable-line
        )
      )
    ) {
      receiveLarge(data, set)
    // just use array! remove this nonsense
    } else if (data[0] === '#') {
      if (data[1] === '1') {
        // same here
        sendSubscriptions(socket, JSON.parse(data.slice(2)), hub)
      } else {
        // call it events -- emit {} etc
        // need to fix this on send used in phoenix else it breaks
        // [ 1 ] emit: { [type]: [], }
        // [ 1 ] subscriptions: { [type]: [] }
        hub.emit('error', JSON.parse(data.slice(1)))
      }
    } else {
      // the result of a context switch
      set(data)
    }
  }

  const set = data => receive(hub, JSON.parse(data)[0], JSON.parse(data)[1])
}

const ownListeners = struct => struct !== hub && (struct.emitters || (ownListeners(struct.inherits)))

const removePaths = (struct, list, stamp, data) => {
  var keep = true
  const keys = getKeys(struct)
  if (keys) {
    let i = keys.length
    keep = i
    while (i--) {
      if (removePaths(struct.get(keys[i]), list, stamp, data && data[keys[i]])) {
        keep--
      }
    }
  }
  if (struct.val !== void 0) {
    if (list[puid(struct)] && (!data || data.val == void 0)) {
      if (ownListeners(struct)) {
        // console.log('soft removing', struct.path())
        delete struct.val
        struct.stamp = 0
        struct.emit('data', null, stamp)
      } else {
        // console.log('hard removing', struct.path())
        struct.set(null, stamp)
        return true
      }
    }
  } else if (!keep && !ownListeners(struct)) {
    // console.log('hard removing', struct.path())
    struct.set(null, stamp)
    return true
  }
}

// raf
const receive = (hub, data, info) => {
  bs.setOffset(bs.offset + (info.stamp | 0) - (bs.create() | 0))

  if (info && info.connect) {
    hub.set({ connected: true }, bs.create())
    meta(hub)
    bs.close()
  }
  // hub._receiving =  handle this!
  // this will help /w heavy computation on incoming
  if (data) {
    next(() => {
      const stamp = bs.create()
      if (!hub.receiveOnly) {
        hub.receiveOnly = true
        if (info.remove) {
          removePaths(hub, info.remove, stamp, data)
        }
        hub.set(data, stamp, void 0, !!info.remove)
        hub.receiveOnly = null
      } else {
        if (info.remove) {
          removePaths(hub, info.remove, stamp, data)
        }
        hub.set(data, stamp, void 0, !!info.remove)
      }
      bs.close()
    })
  }
}

const removeUrl = hub => {
  hub.url = hub._url_ = hub.urlIndex = null
  hub.emitters.set({ data: { url$: null } }, false)
}

const removeSocket = hub => {
  if (hub.reconnect) {
    clearTimeout(hub.reconnect)
    hub.reconnect = null
  }
  if (hub.socket) {
    hub.socket.blockReconnect = true
    hub.socket.close()
  }
  hub.socket = false
}

const url = (hub, val, key, stamp) => {
  hub.on((val, stamp, t) => {
    if (val === null && !t._c && t === hub) {
      hub.subscriptions = []
      removeUrl(hub)
      removeSocket(hub)
    }
  }, 'url$')

  if (!val) val = null
  if ((!hub.url && val) || (hub.url.compute() !== val)) {
    removeSocket(hub)
    if (!val) {
      hub.set({ connected: false }, stamp)
      hub._url_ = null
      if (hub.url) hub.url.set(null, stamp)
    } else {
      if (!hub.url) {
        create({
          on: {
            data: {
              url: (val, stamp, struct) => {
                val = struct.compute()
                if (val) {
                  hub.set({ connected: false }, stamp)
                  let i = -1
                  if (hub.key) i++
                  hub.parent(() => { i++ })
                  hub.urlIndex = i // use this for checks
                  hub._url_ = val
                  connect(hub, val, 50)
                } else {
                  hub._url_ = null
                }
              }
            }
          }
        }, stamp, struct, hub, key)
      }
      if (/^https?/.test(val)) val = val.replace(/^http/, 'ws')
      hub.url.set(val, stamp)
    }
  }
}

const removeClients = (hub, stamp) => {
  const clients = hub.clients
  if (clients && clients.keys().length > 1) {
    clients.forEach((client, key) => {
      if (
        client.val !== null &&
        client !== hub.client
      ) {
        client.set(null, -stamp)
        delete clients[key]
      }
    })
  }
}

const connectedStruct = create({
  on: {
    data: {
      removeClients: (val, stamp, t) => {
        if (t.compute() === false) {
          removeClients(t._p, stamp)
        }
      }
    }
  }
})

const connected = (hub, val, key, stamp) => {
  if (!hub.connected) {
    create(val, stamp, connectedStruct, hub, key)
  } else {
    hub.connected.set(val, stamp)
  }
}

const contextStruct = struct.create({
  props: {
    default: {
      on: {
        data: {
          updateParent: (val, stamp, t) => {
            t.parent().emit('data', val, stamp)
          }
        }
      }
    }
  }
})

const contextIsNotEqual = (val, context) => {
  if (val && typeof val === 'object') {
    for (let field in val) {
      if (!context[field] || (context[field].compute && context[field].compute()) !== val[field]) {
        return true
      }
    }
  } else {
    return val !== context.compute()
  }
}

const context = (hub, val, key, stamp) => {
  if ((!hub.context && val) || (hub.context && contextIsNotEqual(val, hub.context))) {
    if (!hub.context) {
      create(val, stamp, contextStruct, hub, key)
    } else {
      removeClients(hub, stamp)
      hub.context.set(val, stamp)
    }
    if (hub.connected && hub.connected.compute() === true) meta(hub)
  }
}

const props = {
  reconnect: true,
  socket: true,
  urlIndex: true,
  upstreamSubscriptions: true,
  receiveOnly: true,
  url,
  context,
  connected
}

const stub = () => {}

const define = {
  subscribe (subs, cb, raw, tree, forceUpstream) {
    if (!raw) subs = parse(subs)
    if (!this.receiveOnly || forceUpstream) {
      const parsed = serialize(this, subs)
      if (parsed) {
        if (forceUpstream) {
          parsed.__force__ = true
        }
        // why not keep it stringified? -- saves lots of speed
        const key = hash(JSON.stringify(parsed))
        if (!this.upstreamSubscriptions) {
          this.upstreamSubscriptions = {}
          this.upstreamSubscriptions[key] = parsed
          if (this.url) meta(this)
        } else if (!this.upstreamSubscriptions[key]) {
          this.upstreamSubscriptions[key] = parsed
          if (this.url) meta(this)
        }
      }
    }
    return subscribe(this, subs, cb || stub, tree)
  }
}

const enableIncomingListener = (socket, hub) => {
  if (!socket.incomingOverride) {
    socket.incomingOverride = true
    const field = typeof window === 'undefined'
      ? 'internalOnMessage'
      : 'onmessage'
    const msg = hub.socket[field]
    socket[field] = (data) => {
      hub.emit('incoming', data)
      msg(data)
    }
  }
}

const on = {
  data: { send },
  props: {
    incoming: (t, val, key, stamp) => {
      const hub = t._p
      if (hub.socket) enableIncomingListener(hub.socket, hub)
      return emitterProperty(t, val, key, stamp)
    }
  }
}

export { props, on, define }
<commit_msg>standard<commit_after>import bs from 'stamp'
import { send, meta, sendSubscriptions } from './send'
import WebSocket from './websocket'
import {
  create,
  parse,
  subscribe,
  struct,
  emitterProperty,
  puid,
  getKeys
} from 'brisky-struct'
import serialize from '../subscription/serialize'
import hash from 'string-hash'
import createClient from './create'
import { receiveLarge } from '../size'
import hub from '../hub'

const isNode = typeof window === 'undefined'

// want to use for upsteream
const next = isNode
  ? fn => setTimeout(fn, 18)
  : global.requestAnimationFrame

// const cancel = isNode
//   ? clearTimeout
//   : global.cancelAnimationFrame

const connect = (hub, url, reconnect) => {
  // use outside function non anon since its slower (according to uws)

  const socket = new WebSocket(url)
  const client = hub.client || createClient(hub, {}, false)
  // var inProgress, queue

  hub.set({ client }, false)

  hub.reconnect = null

  const close = () => {
    const stamp = bs.create()
    hub.socket = false
    hub.set({ connected: false }, stamp)
    bs.close()
    if (!socket.blockReconnect && hub._url_) {
      reconnect = Math.min((reconnect * 1.5), 2000)
      hub.reconnect = setTimeout(connect, reconnect, hub, url, reconnect)
    }
  }

  socket.onclose = close

  socket.onerror = isNode ? close : () => socket.close()

  socket.onopen = () => {
    hub.socket = socket
    if (hub.emitters && hub.emitters.incoming) {
      enableIncomingListener(socket, hub)
    }
  }

  socket.onmessage = (data) => {
    data = data.data

    if (
      typeof data !== 'string' &&
      (data instanceof ArrayBuffer ||
        (!isNode &&
          ((('Blob' in global) && data instanceof Blob) || // eslint-disable-line
          (('WebkitBlob' in global) && data instanceof WebkitBlob)) // eslint-disable-line
        )
      )
    ) {
      receiveLarge(data, set)
    // just use array! remove this nonsense
    } else if (data[0] === '#') {
      if (data[1] === '1') {
        // same here
        sendSubscriptions(socket, JSON.parse(data.slice(2)), hub)
      } else {
        // call it events -- emit {} etc
        // need to fix this on send used in phoenix else it breaks
        // [ 1 ] emit: { [type]: [], }
        // [ 1 ] subscriptions: { [type]: [] }
        hub.emit('error', JSON.parse(data.slice(1)))
      }
    } else {
      // the result of a context switch
      set(data)
    }
  }

  const set = data => receive(hub, JSON.parse(data)[0], JSON.parse(data)[1])
}

const ownListeners = struct => struct !== hub && (struct.emitters || (ownListeners(struct.inherits)))

const removePaths = (struct, list, stamp, data) => {
  var keep = true
  const keys = getKeys(struct)
  if (keys) {
    let i = keys.length
    keep = i
    while (i--) {
      if (removePaths(struct.get(keys[i]), list, stamp, data && data[keys[i]])) {
        keep--
      }
    }
  }
  if (struct.val !== void 0) {
    if (list[puid(struct)] && (!data || data.val === void 0)) {
      if (ownListeners(struct)) {
        // console.log('soft removing', struct.path())
        delete struct.val
        struct.stamp = 0
        struct.emit('data', null, stamp)
      } else {
        // console.log('hard removing', struct.path())
        struct.set(null, stamp)
        return true
      }
    }
  } else if (!keep && !ownListeners(struct)) {
    // console.log('hard removing', struct.path())
    struct.set(null, stamp)
    return true
  }
}

// raf
const receive = (hub, data, info) => {
  bs.setOffset(bs.offset + (info.stamp | 0) - (bs.create() | 0))

  if (info && info.connect) {
    hub.set({ connected: true }, bs.create())
    meta(hub)
    bs.close()
  }
  // hub._receiving =  handle this!
  // this will help /w heavy computation on incoming
  if (data) {
    next(() => {
      const stamp = bs.create()
      if (!hub.receiveOnly) {
        hub.receiveOnly = true
        if (info.remove) {
          removePaths(hub, info.remove, stamp, data)
        }
        hub.set(data, stamp, void 0, !!info.remove)
        hub.receiveOnly = null
      } else {
        if (info.remove) {
          removePaths(hub, info.remove, stamp, data)
        }
        hub.set(data, stamp, void 0, !!info.remove)
      }
      bs.close()
    })
  }
}

const removeUrl = hub => {
  hub.url = hub._url_ = hub.urlIndex = null
  hub.emitters.set({ data: { url$: null } }, false)
}

const removeSocket = hub => {
  if (hub.reconnect) {
    clearTimeout(hub.reconnect)
    hub.reconnect = null
  }
  if (hub.socket) {
    hub.socket.blockReconnect = true
    hub.socket.close()
  }
  hub.socket = false
}

const url = (hub, val, key, stamp) => {
  hub.on((val, stamp, t) => {
    if (val === null && !t._c && t === hub) {
      hub.subscriptions = []
      removeUrl(hub)
      removeSocket(hub)
    }
  }, 'url$')

  if (!val) val = null
  if ((!hub.url && val) || (hub.url.compute() !== val)) {
    removeSocket(hub)
    if (!val) {
      hub.set({ connected: false }, stamp)
      hub._url_ = null
      if (hub.url) hub.url.set(null, stamp)
    } else {
      if (!hub.url) {
        create({
          on: {
            data: {
              url: (val, stamp, struct) => {
                val = struct.compute()
                if (val) {
                  hub.set({ connected: false }, stamp)
                  let i = -1
                  if (hub.key) i++
                  hub.parent(() => { i++ })
                  hub.urlIndex = i // use this for checks
                  hub._url_ = val
                  connect(hub, val, 50)
                } else {
                  hub._url_ = null
                }
              }
            }
          }
        }, stamp, struct, hub, key)
      }
      if (/^https?/.test(val)) val = val.replace(/^http/, 'ws')
      hub.url.set(val, stamp)
    }
  }
}

const removeClients = (hub, stamp) => {
  const clients = hub.clients
  if (clients && clients.keys().length > 1) {
    clients.forEach((client, key) => {
      if (
        client.val !== null &&
        client !== hub.client
      ) {
        client.set(null, -stamp)
        delete clients[key]
      }
    })
  }
}

const connectedStruct = create({
  on: {
    data: {
      removeClients: (val, stamp, t) => {
        if (t.compute() === false) {
          removeClients(t._p, stamp)
        }
      }
    }
  }
})

const connected = (hub, val, key, stamp) => {
  if (!hub.connected) {
    create(val, stamp, connectedStruct, hub, key)
  } else {
    hub.connected.set(val, stamp)
  }
}

const contextStruct = struct.create({
  props: {
    default: {
      on: {
        data: {
          updateParent: (val, stamp, t) => {
            t.parent().emit('data', val, stamp)
          }
        }
      }
    }
  }
})

const contextIsNotEqual = (val, context) => {
  if (val && typeof val === 'object') {
    for (let field in val) {
      if (!context[field] || (context[field].compute && context[field].compute()) !== val[field]) {
        return true
      }
    }
  } else {
    return val !== context.compute()
  }
}

const context = (hub, val, key, stamp) => {
  if ((!hub.context && val) || (hub.context && contextIsNotEqual(val, hub.context))) {
    if (!hub.context) {
      create(val, stamp, contextStruct, hub, key)
    } else {
      removeClients(hub, stamp)
      hub.context.set(val, stamp)
    }
    if (hub.connected && hub.connected.compute() === true) meta(hub)
  }
}

const props = {
  reconnect: true,
  socket: true,
  urlIndex: true,
  upstreamSubscriptions: true,
  receiveOnly: true,
  url,
  context,
  connected
}

const stub = () => {}

const define = {
  subscribe (subs, cb, raw, tree, forceUpstream) {
    if (!raw) subs = parse(subs)
    if (!this.receiveOnly || forceUpstream) {
      const parsed = serialize(this, subs)
      if (parsed) {
        if (forceUpstream) {
          parsed.__force__ = true
        }
        // why not keep it stringified? -- saves lots of speed
        const key = hash(JSON.stringify(parsed))
        if (!this.upstreamSubscriptions) {
          this.upstreamSubscriptions = {}
          this.upstreamSubscriptions[key] = parsed
          if (this.url) meta(this)
        } else if (!this.upstreamSubscriptions[key]) {
          this.upstreamSubscriptions[key] = parsed
          if (this.url) meta(this)
        }
      }
    }
    return subscribe(this, subs, cb || stub, tree)
  }
}

const enableIncomingListener = (socket, hub) => {
  if (!socket.incomingOverride) {
    socket.incomingOverride = true
    const field = typeof window === 'undefined'
      ? 'internalOnMessage'
      : 'onmessage'
    const msg = hub.socket[field]
    socket[field] = (data) => {
      hub.emit('incoming', data)
      msg(data)
    }
  }
}

const on = {
  data: { send },
  props: {
    incoming: (t, val, key, stamp) => {
      const hub = t._p
      if (hub.socket) enableIncomingListener(hub.socket, hub)
      return emitterProperty(t, val, key, stamp)
    }
  }
}

export { props, on, define }
<|endoftext|>


<commit_before>import 'styles/bootstrap.scss'
import 'app.scss'

//to use generators
import 'regenerator-runtime/runtime'
// import 'core-js/library/es6/promise' //required for IE11
import 'isomorphic-fetch' //required for IE11 and Safary

//favicons
import 'public/img/favicon.ico'
import 'public/img/apple-touch-icon-152x152.png'
import 'public/img/apple-touch-icon-180x180.png'
import 'public/img/safari-pinned-tab.svg'

// import React from 'react'
// if (process.env.NODE_ENV !== 'production') {
//   const {whyDidYouUpdate} = require('why-did-you-update')
//   whyDidYouUpdate(React, { exclude: /.+Connect|Col|Week|Row|Day/})
//   // whyDidYouUpdate(React, { exclude: /.+Calendar|Connect|TopNavbar|/ })
//   // whyDidYouUpdate(React, { exclude: /.+Connect|TopNavbar|Week|Row|Col|/ })
// }

import 'utils/polyfills'
import { render } from 'react-dom'
import Grid from 'client/styles/grid.js'
import { getConfiguredWithStoreRouter } from 'client/get-router.js'

let prevContainerWidth

let renderApp = function() {
  const browserWidth = window.document.body.offsetWidth
  const containerWidth = Grid.getContainerWidth(browserWidth)

  if (containerWidth === prevContainerWidth) {
    return
  }

  prevContainerWidth = containerWidth

  render(getConfiguredWithStoreRouter(containerWidth), document.getElementById('root'))
}

window.addEventListener('resize', renderApp)

//first time render
renderApp()
<commit_msg>added dynamic require to whyDidyYouUpdate<commit_after>import 'styles/bootstrap.scss'
import 'app.scss'

//to use generators
import 'regenerator-runtime/runtime'
// import 'core-js/library/es6/promise' //required for IE11
import 'isomorphic-fetch' //required for IE11 and Safary

//favicons
import 'public/img/favicon.ico'
import 'public/img/apple-touch-icon-152x152.png'
import 'public/img/apple-touch-icon-180x180.png'
import 'public/img/safari-pinned-tab.svg'

// // import React from 'react'
// if (process.env.NODE_ENV !== 'production') {
//   const {whyDidYouUpdate} = require('why-did-you-update')
//   whyDidYouUpdate(require('react'), { exclude: /.+Connect|Col|Week|Row|Day/})
//   // whyDidYouUpdate(React, { exclude: /.+Calendar|Connect|TopNavbar|/ })
//   // whyDidYouUpdate(React, { exclude: /.+Connect|TopNavbar|Week|Row|Col|/ })
// }

import 'utils/polyfills'
import { render } from 'react-dom'
import Grid from 'client/styles/grid.js'
import { getConfiguredWithStoreRouter } from 'client/get-router.js'

let prevContainerWidth

let renderApp = function() {
  const browserWidth = window.document.body.offsetWidth
  const containerWidth = Grid.getContainerWidth(browserWidth)

  if (containerWidth === prevContainerWidth) {
    return
  }

  prevContainerWidth = containerWidth

  render(getConfiguredWithStoreRouter(containerWidth), document.getElementById('root'))
}

window.addEventListener('resize', renderApp)

//first time render
renderApp()
<|endoftext|>


<commit_before>/**
 * straps.js - Class inheritance library with support for bean-style accessors
 *
 * Copyright (c) 2006 - 2013 Juerg Lehni
 * http://lehni.org/
 *
 * Distributed under the MIT license.
 *
 * straps.js was created by extracting and simplifying the inheritance framework
 * from boostrap.js, a JavaScript DOM library, also created by Juerg Lehni:
 * https://github.com/lehni/bootstrap.js
 *
 * Inspirations:
 * http://dean.edwards.name/weblog/2006/03/base/
 * http://dev.helma.org/Wiki/JavaScript+Inheritance+Sugar/
 */

var Base = new function() {
	var hidden = /^(statics|preserve|enumerable|prototype|toString|valueOf)$/,

		forEach = [].forEach || function(iter, bind) {
			for (var i = 0, l = this.length; i < l; i++)
				iter.call(bind, this[i], i, this);
		},

		forIn = function(iter, bind) {
			// Do not use Object.keys for iteration as iterators might modify
			// the object we're iterating over, making the hasOwnProperty still
			// necessary.
			for (var i in this)
				if (this.hasOwnProperty(i))
					iter.call(bind, this[i], i, this);
		},

		// A short-cut to a simplified version of Object.create that only
		// supports the first parameter (in the emulation):
		create = Object.create || function(proto) {
			// From all browsers that do not offer Object.create(), we only
			// support Firefox 3.5 & 3.6, and this hack works there:
			return { __proto__: proto };
		},

		describe = Object.getOwnPropertyDescriptor || function(obj, name) {
			// Emulate Object.getOwnPropertyDescriptor for outdated browsers
			var get = obj.__lookupGetter__ && obj.__lookupGetter__(name);
			return get
					? { get: get, set: obj.__lookupSetter__(name),
						enumerable: true, configurable: true }
					: obj.hasOwnProperty(name)
						? { value: obj[name], enumerable: true,
							configurable: true, writable: true }
						: null;
		},

		_define = Object.defineProperty || function(obj, name, desc) {
			// Emulate Object.defineProperty for outdated browsers
			if ((desc.get || desc.set) && obj.__defineGetter__) {
				if (desc.get)
					obj.__defineGetter__(name, desc.get);
				if (desc.set)
					obj.__defineSetter__(name, desc.set);
			} else {
				obj[name] = desc.value;
			}
			return obj;
		},

		define = function(obj, name, desc) {
			// Both Safari and Chrome at one point ignored configurable = true
			// and did not allow overriding of existing properties:
			// https://code.google.com/p/chromium/issues/detail?id=72736
			// https://bugs.webkit.org/show_bug.cgi?id=54289
			// The workaround is to delete the property first.
			// TODO: Remove this fix in July 2014, and use _define directly.
			delete obj[name];
			return _define(obj, name, desc);
		};

	/**
	 * Private function that injects functions from src into dest, overriding
	 * (and inherinting from) base.
	 */
	function inject(dest, src, enumerable, base, preserve) {
		var beans;

		/**
		 * Private function that injects one field with given name and checks if
		 * the field is a function that needs to be wrapped for calls of base().
		 * This is only needed if the function in base is different from the one
		 * in src, and if the one in src is actually calling base through base.
		 * The string of the function is parsed for this.base to detect calls.
		 */
		function field(name, val, dontCheck) {
			// This does even work for prop: 0, as it will just be looked up
			// again through describe.
			var val = val || (val = describe(src, name))
					&& (val.get ? val : val.value);
			// Allow aliases to properties with different names, by having
			// string values starting with '#'
			if (typeof val === 'string' && val[0] === '#')
				val = dest[val.substring(1)] || val;
			var isFunc = typeof val === 'function',
				res = val,
				// Only lookup previous value if we preserve or define a
				// function that might need it for this.base(). If we're
				// defining a getter, don't lookup previous value, but look if
				// the property exists (name in dest) and store result in prev
				prev = preserve || isFunc
					? (val && val.get ? name in dest : dest[name]) : null,
				bean;
			if ((dontCheck || val !== undefined && src.hasOwnProperty(name))
					&& (!preserve || !prev)) {
				// Expose the 'super' function (meaning the one this function is
				// overriding) through #base:
				if (isFunc && prev)
					val.base = prev;
				// Produce bean properties if getters or setters are specified.
				// Just collect potential beans for now, and look them up in
				// dest at the end of fields injection. This ensures base works
				// for beans too, and inherits setters for redefined getters in
				// subclasses.
				if (isFunc && beans
						&& (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/)))
					beans[bean[3].toLowerCase() + bean[4]] = bean[2];
				// No need to create accessor description if it is one already.
				// It is considered a description if it is an object with a get
				// function that has zero parameters.
				if (!res || isFunc || !res.get || typeof res.get !== 'function'
						|| res.get.length !== 0)
					res = { value: res, writable: true };
				// Only set/change configurable and enumerable if this field is
				// configurable
				if ((describe(dest, name)
						|| { configurable: true }).configurable) {
					res.configurable = true;
					res.enumerable = enumerable;
				}
				define(dest, name, res);
			}
		}
		// Iterate through all definitions in src now and call field() for each.
		if (src) {
			beans = {};
			for (var name in src)
				if (src.hasOwnProperty(name) && !hidden.test(name))
					field(name, null, true);
			// IE (and some other browsers?) never enumerate these, even  if
			// they are simply set on an object. Force their creation. Ccheck
			// them for not being defined (by passing undefined for dontCheck).
			field('toString');
			field('valueOf');
			// Now finally define beans as well. Look up methods on dest, for
			// support of this.base() (See above).
			for (var name in beans) {
				var part = beans[name],
					set = dest['set' + part],
					// Convention: 'isName' is only considered a bean if there
					// is also a setter for it!
					get = dest['get' + part] || set && dest['is' + part];
				// Convention: Assume that if a potential getter has no
				// arguments and there is no setter, it is a read-only bean.
				// If there is both a getter and a setter, do not look at
				// arguments count at all.
				if (get && (get.length === 0 || set))
					field(name, { get: get, set: set }, true);
			}
		}
		return dest;
	}

	function each(obj, iter, bind) {
		// To support both array-like structures and plain objects, use a simple
		// hack to filter out Base objects with #length getters in the
		// #length-base array-like check for now, by assuming these getters are
		// produced by #getLength beans.
		// NOTE: The correct check would call describe(obj, 'length') and look
		// for typeof res.value === 'number', but it's twice as slow on Chrome
		// and Firefox (WebKit does well), so this should do for now.
		if (obj)
			('length' in obj && !obj.getLength
					&& typeof obj.length === 'number'
				? forEach
				: forIn).call(obj, iter, bind = bind || obj);
		return bind;
	}

	function copy(dest, source) {
		for (var i in source)
			if (source.hasOwnProperty(i))
				dest[i] = source[i];
		return dest;
	}

	// Inject into new ctor object that's passed to inject(), and then returned
	// as the Base class.
	return inject(function Base() {
		// Define a constructor that merges in all the fields of the passed
		// objects using copy()
		for (var i = 0, l = arguments.length; i < l; i++)
			copy(this, arguments[i]);
	}, {
		inject: function(src/*, ... */) {
			if (src) {
				var proto = this.prototype,
					base = Object.getPrototypeOf(proto).constructor,
					// Allow the whole scope to just define statics by defining
					// statics: true.
					statics = src.statics === true ? src : src.statics;
				if (statics != src)
					inject(proto, src, src.enumerable, base && base.prototype,
							src.preserve);
				// Define new static fields as enumerable, and inherit from
				// base. enumerable is necessary so they can be copied over from
				// base, and it does not harm to have enumerable properties in
				// the constructor. Use the preserve setting in src.preserve for
				// statics too, not their own.
				inject(this, statics, true, base, src.preserve);
			}
			// If there are more than one argument, loop through them and call
			// inject again. Do not simple inline the above code in one loop,
			// since each of the passed objects might override this.inject.
			for (var i = 1, l = arguments.length; i < l; i++)
				this.inject(arguments[i]);
			return this;
		},

		extend: function(/* src, ... */) {
			var base = this,
				ctor;
			// Look for an initialize function in all injection objects and use
			// it directly as the actual constructor.
			for (var i = 0, l = arguments.length; i < l; i++)
				if (ctor = arguments[i].initialize)
					break;
			// If no initialize function is provided, create a constructor that
			// simply calls the base constructor.
			ctor = ctor || function() {
				base.apply(this, arguments);
			};
			ctor.prototype = create(this.prototype);
			// Expose base property on constructor functions as well.
			ctor.base = base;
			// The new prototype extends the constructor on which extend is
			// called. Fix constructor.
			define(ctor.prototype, 'constructor',
					{ value: ctor, writable: true, configurable: true });
			// Copy over static fields, as prototype-like inheritance
			// is not possible for static fields. Mark them as enumerable
			// so they can be copied over again.
			inject(ctor, this, true);
			// Inject all the definitions in src. Use the new inject instead of
			// the one in ctor, in case it was overriden. this is needed when
			// overriding the static .inject(). But only inject if there's
			// something to actually inject.
			return arguments.length ? this.inject.apply(ctor, arguments) : ctor;
		}
		// Pass true for enumerable, so inject() and extend() can be passed on
		// to subclasses of Base through Base.inject() / extend().
	}, true).inject({
		/**
		 * Injects the fields from the given object, adding this.base()
		 * functionality
		 */
		inject: function(/* src, ... */) {
			for (var i = 0, l = arguments.length; i < l; i++)
				inject(this, arguments[i], arguments[i].enumerable);
			return this;
		},

		/**
		 * Returns a new object that inherits all properties from "this",
		 * through proper JS inheritance, not copying.
		 * Optionally, src and hide parameters can be passed to fill in the
		 * newly created object just like in inject(), to copy the behavior
		 * of Function.prototype.extend.
		 */
		extend: function(/* src, ... */) {
			var res = create(this);
			return res.inject.apply(res, arguments);
		},

		each: function(iter, bind) {
			return each(this, iter, bind);
		},

		/**
		 * General purpose clone function that delegates cloning to the
		 * constructor that receives the object to be cloned as the first
		 * argument.
		 * NOTE: #clone() needs to be overridden in any class that requires
		 * other cloning behavior.
		 */
		clone: function() {
			return new this.constructor(this);
		},

		statics: {
			// Expose some local privates as static functions on Base.
			each: each,
			create: create,
			define: define,
			describe: describe,
			copy: copy,

			clone: function(obj) {
				return copy(new obj.constructor(), obj);
			},

			/**
			 * Returns true if obj is a plain JavaScript object literal, or a
			 * plain Base object, as produced by Base.merge().
			 */
			isPlainObject: function(obj) {
				var ctor = obj != null && obj.constructor;
				// We also need to check for ctor.name === 'Object', in case
				// this is an object from another global scope (e.g. an iframe,
				// or another vm context in node.js).
				return ctor && (ctor === Object || ctor === Base
						|| ctor.name === 'Object');
			},

			/**
			 * Returns the first argument that is defined. null is counted as
			 * defined too, as !== undefined is used for comparisons.
			 */
			pick: function() {
				for (var i = 0, l = arguments.length; i < l; i++)
					if (arguments[i] !== undefined)
						return arguments[i];
				return null;
			}
		}
	});
};

// Export Base class for node
if (typeof module !== 'undefined')
	module.exports = Base;
<commit_msg>Improve check for object literal accessor in inject()<commit_after>/**
 * straps.js - Class inheritance library with support for bean-style accessors
 *
 * Copyright (c) 2006 - 2013 Juerg Lehni
 * http://lehni.org/
 *
 * Distributed under the MIT license.
 *
 * straps.js was created by extracting and simplifying the inheritance framework
 * from boostrap.js, a JavaScript DOM library, also created by Juerg Lehni:
 * https://github.com/lehni/bootstrap.js
 *
 * Inspirations:
 * http://dean.edwards.name/weblog/2006/03/base/
 * http://dev.helma.org/Wiki/JavaScript+Inheritance+Sugar/
 */

var Base = new function() {
	var hidden = /^(statics|preserve|enumerable|prototype|toString|valueOf)$/,

		forEach = [].forEach || function(iter, bind) {
			for (var i = 0, l = this.length; i < l; i++)
				iter.call(bind, this[i], i, this);
		},

		forIn = function(iter, bind) {
			// Do not use Object.keys for iteration as iterators might modify
			// the object we're iterating over, making the hasOwnProperty still
			// necessary.
			for (var i in this)
				if (this.hasOwnProperty(i))
					iter.call(bind, this[i], i, this);
		},

		// A short-cut to a simplified version of Object.create that only
		// supports the first parameter (in the emulation):
		create = Object.create || function(proto) {
			// From all browsers that do not offer Object.create(), we only
			// support Firefox 3.5 & 3.6, and this hack works there:
			return { __proto__: proto };
		},

		describe = Object.getOwnPropertyDescriptor || function(obj, name) {
			// Emulate Object.getOwnPropertyDescriptor for outdated browsers
			var get = obj.__lookupGetter__ && obj.__lookupGetter__(name);
			return get
					? { get: get, set: obj.__lookupSetter__(name),
						enumerable: true, configurable: true }
					: obj.hasOwnProperty(name)
						? { value: obj[name], enumerable: true,
							configurable: true, writable: true }
						: null;
		},

		_define = Object.defineProperty || function(obj, name, desc) {
			// Emulate Object.defineProperty for outdated browsers
			if ((desc.get || desc.set) && obj.__defineGetter__) {
				if (desc.get)
					obj.__defineGetter__(name, desc.get);
				if (desc.set)
					obj.__defineSetter__(name, desc.set);
			} else {
				obj[name] = desc.value;
			}
			return obj;
		},

		define = function(obj, name, desc) {
			// Both Safari and Chrome at one point ignored configurable = true
			// and did not allow overriding of existing properties:
			// https://code.google.com/p/chromium/issues/detail?id=72736
			// https://bugs.webkit.org/show_bug.cgi?id=54289
			// The workaround is to delete the property first.
			// TODO: Remove this fix in July 2014, and use _define directly.
			delete obj[name];
			return _define(obj, name, desc);
		};

	/**
	 * Private function that injects functions from src into dest, overriding
	 * (and inherinting from) base.
	 */
	function inject(dest, src, enumerable, base, preserve) {
		var beans;

		/**
		 * Private function that injects one field with given name and checks if
		 * the field is a function that needs to be wrapped for calls of base().
		 * This is only needed if the function in base is different from the one
		 * in src, and if the one in src is actually calling base through base.
		 * The string of the function is parsed for this.base to detect calls.
		 */
		function field(name, val, dontCheck) {
			// This does even work for prop: 0, as it will just be looked up
			// again through describe.
			var val = val || (val = describe(src, name))
					&& (val.get ? val : val.value);
			// Allow aliases to properties with different names, by having
			// string values starting with '#'
			if (typeof val === 'string' && val[0] === '#')
				val = dest[val.substring(1)] || val;
			var isFunc = typeof val === 'function',
				res = val,
				// Only lookup previous value if we preserve or define a
				// function that might need it for this.base(). If we're
				// defining a getter, don't lookup previous value, but look if
				// the property exists (name in dest) and store result in prev
				prev = preserve || isFunc
					? (val && val.get ? name in dest : dest[name]) : null,
				bean;
			if ((dontCheck || val !== undefined && src.hasOwnProperty(name))
					&& (!preserve || !prev)) {
				// Expose the 'super' function (meaning the one this function is
				// overriding) through #base:
				if (isFunc && prev)
					val.base = prev;
				// Produce bean properties if getters or setters are specified.
				// Just collect potential beans for now, and look them up in
				// dest at the end of fields injection. This ensures base works
				// for beans too, and inherits setters for redefined getters in
				// subclasses.
				if (isFunc && beans
						&& (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/)))
					beans[bean[3].toLowerCase() + bean[4]] = bean[2];
				// No need to create accessor description if it is one already.
				// It is considered a description if it is a plain object with a
				// get function.
				if (!res || isFunc || !res.get || typeof res.get !== 'function'
						|| !Base.isPlainObject(res))
					res = { value: res, writable: true };
				// Only set/change configurable and enumerable if this field is
				// configurable
				if ((describe(dest, name)
						|| { configurable: true }).configurable) {
					res.configurable = true;
					res.enumerable = enumerable;
				}
				define(dest, name, res);
			}
		}
		// Iterate through all definitions in src now and call field() for each.
		if (src) {
			beans = {};
			for (var name in src)
				if (src.hasOwnProperty(name) && !hidden.test(name))
					field(name, null, true);
			// IE (and some other browsers?) never enumerate these, even  if
			// they are simply set on an object. Force their creation. Ccheck
			// them for not being defined (by passing undefined for dontCheck).
			field('toString');
			field('valueOf');
			// Now finally define beans as well. Look up methods on dest, for
			// support of this.base() (See above).
			for (var name in beans) {
				var part = beans[name],
					set = dest['set' + part],
					// Convention: 'isName' is only considered a bean if there
					// is also a setter for it!
					get = dest['get' + part] || set && dest['is' + part];
				// Convention: Assume that if a potential getter has no
				// arguments and there is no setter, it is a read-only bean.
				// If there is both a getter and a setter, do not look at
				// arguments count at all.
				if (get && (get.length === 0 || set))
					field(name, { get: get, set: set }, true);
			}
		}
		return dest;
	}

	function each(obj, iter, bind) {
		// To support both array-like structures and plain objects, use a simple
		// hack to filter out Base objects with #length getters in the
		// #length-base array-like check for now, by assuming these getters are
		// produced by #getLength beans.
		// NOTE: The correct check would call describe(obj, 'length') and look
		// for typeof res.value === 'number', but it's twice as slow on Chrome
		// and Firefox (WebKit does well), so this should do for now.
		if (obj)
			('length' in obj && !obj.getLength
					&& typeof obj.length === 'number'
				? forEach
				: forIn).call(obj, iter, bind = bind || obj);
		return bind;
	}

	function copy(dest, source) {
		for (var i in source)
			if (source.hasOwnProperty(i))
				dest[i] = source[i];
		return dest;
	}

	// Inject into new ctor object that's passed to inject(), and then returned
	// as the Base class.
	return inject(function Base() {
		// Define a constructor that merges in all the fields of the passed
		// objects using copy()
		for (var i = 0, l = arguments.length; i < l; i++)
			copy(this, arguments[i]);
	}, {
		inject: function(src/*, ... */) {
			if (src) {
				var proto = this.prototype,
					base = Object.getPrototypeOf(proto).constructor,
					// Allow the whole scope to just define statics by defining
					// statics: true.
					statics = src.statics === true ? src : src.statics;
				if (statics != src)
					inject(proto, src, src.enumerable, base && base.prototype,
							src.preserve);
				// Define new static fields as enumerable, and inherit from
				// base. enumerable is necessary so they can be copied over from
				// base, and it does not harm to have enumerable properties in
				// the constructor. Use the preserve setting in src.preserve for
				// statics too, not their own.
				inject(this, statics, true, base, src.preserve);
			}
			// If there are more than one argument, loop through them and call
			// inject again. Do not simple inline the above code in one loop,
			// since each of the passed objects might override this.inject.
			for (var i = 1, l = arguments.length; i < l; i++)
				this.inject(arguments[i]);
			return this;
		},

		extend: function(/* src, ... */) {
			var base = this,
				ctor;
			// Look for an initialize function in all injection objects and use
			// it directly as the actual constructor.
			for (var i = 0, l = arguments.length; i < l; i++)
				if (ctor = arguments[i].initialize)
					break;
			// If no initialize function is provided, create a constructor that
			// simply calls the base constructor.
			ctor = ctor || function() {
				base.apply(this, arguments);
			};
			ctor.prototype = create(this.prototype);
			// Expose base property on constructor functions as well.
			ctor.base = base;
			// The new prototype extends the constructor on which extend is
			// called. Fix constructor.
			define(ctor.prototype, 'constructor',
					{ value: ctor, writable: true, configurable: true });
			// Copy over static fields, as prototype-like inheritance
			// is not possible for static fields. Mark them as enumerable
			// so they can be copied over again.
			inject(ctor, this, true);
			// Inject all the definitions in src. Use the new inject instead of
			// the one in ctor, in case it was overriden. this is needed when
			// overriding the static .inject(). But only inject if there's
			// something to actually inject.
			return arguments.length ? this.inject.apply(ctor, arguments) : ctor;
		}
		// Pass true for enumerable, so inject() and extend() can be passed on
		// to subclasses of Base through Base.inject() / extend().
	}, true).inject({
		/**
		 * Injects the fields from the given object, adding this.base()
		 * functionality
		 */
		inject: function(/* src, ... */) {
			for (var i = 0, l = arguments.length; i < l; i++)
				inject(this, arguments[i], arguments[i].enumerable);
			return this;
		},

		/**
		 * Returns a new object that inherits all properties from "this",
		 * through proper JS inheritance, not copying.
		 * Optionally, src and hide parameters can be passed to fill in the
		 * newly created object just like in inject(), to copy the behavior
		 * of Function.prototype.extend.
		 */
		extend: function(/* src, ... */) {
			var res = create(this);
			return res.inject.apply(res, arguments);
		},

		each: function(iter, bind) {
			return each(this, iter, bind);
		},

		/**
		 * General purpose clone function that delegates cloning to the
		 * constructor that receives the object to be cloned as the first
		 * argument.
		 * NOTE: #clone() needs to be overridden in any class that requires
		 * other cloning behavior.
		 */
		clone: function() {
			return new this.constructor(this);
		},

		statics: {
			// Expose some local privates as static functions on Base.
			each: each,
			create: create,
			define: define,
			describe: describe,
			copy: copy,

			clone: function(obj) {
				return copy(new obj.constructor(), obj);
			},

			/**
			 * Returns true if obj is a plain JavaScript object literal, or a
			 * plain Base object, as produced by Base.merge().
			 */
			isPlainObject: function(obj) {
				var ctor = obj != null && obj.constructor;
				// We also need to check for ctor.name === 'Object', in case
				// this is an object from another global scope (e.g. an iframe,
				// or another vm context in node.js).
				return ctor && (ctor === Object || ctor === Base
						|| ctor.name === 'Object');
			},

			/**
			 * Returns the first argument that is defined. null is counted as
			 * defined too, as !== undefined is used for comparisons.
			 */
			pick: function() {
				for (var i = 0, l = arguments.length; i < l; i++)
					if (arguments[i] !== undefined)
						return arguments[i];
				return null;
			}
		}
	});
};

// Export Base class for node
if (typeof module !== 'undefined')
	module.exports = Base;
<|endoftext|>


<commit_before>/** @const */ var BOOL=0;
/** @const */ var I8=1;
/** @const */ var U8=2;
/** @const */ var I16=3;
/** @const */ var U16=4;
/** @const */ var I32=5;
/** @const */ var U32=6;
/** @const */ var F32=7;
/** @const */ var F64=8;
/** @const */ var DATE=9;
/** @const */ var UTF8=10;

/**
 * Creates binary stream reader with the following API:
 * - stream.read(type) - reads specified type from the stream,
 *   where type could be one of the following values:
 *     BOOL - to read boolean value,
 *     I8 - to read signed 8-bit integer number,
 *     U8 - to read unsigned 8-bit integer number,
 *     I16 - to read signed 16-bit integer number,
 *     U16 - to read unsigned 16-bit integer number,
 *     I32 - to read signed 32-bit integer number,
 *     U32 - to read unsigned 32-bit integer number,
 *     F32 - to read 32-bit floating number,
 *     F64 - to read 64-bit floating number,
 *     UTF8 - to read UTF8 string sequence terminated by zero,
 *     object - to read struct as sequence of primitive types or structs.
 */
function Stream(buffer, offset, length, le){
	if (arguments.length <= 1) offset = 0;
	if (arguments.length <= 2) length = buffer.byteLength;
	if (arguments.length <= 3) le = true;

	var start = offset;
	var pos = offset;
	var view = new DataView(buffer, offset, length);
	var stream = {
		length: length,
		position: function() { return pos; }
	};

	function isObject(v) { return !!(v && (typeof v == 'object')); }

	if (Object.keys === undefined) {
		Object.keys = function(v) {
			var arr = [];
			for (var k in v) {
				arr.push(k);
			}
			return arr;
		};
	}

	stream.read = function(type) {
		if (isObject(type)) return readSeq(type);
		return readPrimitive(type);
	};

	stream.readBytes = function(count) {
		// TODO find a way to read buffer with one call
		var bytes = new Array(count);
		for (var i = 0; i < count; i++)
			bytes[i] = readByte();
		return bytes;
	};

	// TODO origin parameter
	stream.seek = function(offset) {
		pos = offset;
		return stream;
	};

	stream.skip = function(bytes) {
		pos += bytes;
		return stream;
	};

	stream.slice = function(position, size) {
		return Stream(buffer, offset + position, size, le);
	};

	function NotImplemented() { return new Error("not implemented yet!"); }

	function readSeq(type) {
		var keys = Object.keys(type);
		var result = {};
		for (var i = 0; i < keys.length; i++) {
			var k = keys[i];
			var t = type[k];
			var v = stream.read(t);
			result[k] = v;
		}
		return result;
	}

	function readPrimitive(type) {
		var v;
		switch (type){
			case BOOL:
				return !!view.getInt8(pos++);
			case I8:
				return view.getInt8(pos++);
			case U8:
				return view.getUint8(pos++);
			case I16:
				v = view.getInt16(pos, le);
				pos += 2;
				break;
			case U16:
				v = view.getUint16(pos, le);
				pos += 2;
				break;
			case I32:
				v = view.getInt32(pos, le);
				pos += 4;
				break;
			case U32:
				v = view.getUint32(pos, le);
				pos += 4;
				break;
			case F32:
				v = view.getFloat32(pos, le);
				pos += 4;
				break;
			case F64:
				v = view.getFloat64(pos, le);
				pos += 8;
				break;
			case UTF8:
				return readUtf8();
			case DATE:
				throw NotImplemented();
		}
		return v;
	}

	function readByte(){
		return view.getUint8(pos++);
	}

	// TODO: find native way to read utf8 strings from array buffer

	/*
	 * UTF8 to unicode algorithm:
	 * Let's take a UTF-8 byte sequence. The first byte in a new sequence will tell us how long the sequence is.
	 * Let's call the subsequent decimal bytes z y x w v u.
	 *
	 * If z is between and including 0 - 127, then there is 1 byte z. The decimal Unicode value ud = the value of z.
	 * If z is between and including 192 - 223, then there are 2 bytes z y; ud = (z-192)*64 + (y-128)
	 * If z is between and including 224 - 239, then there are 3 bytes z y x; ud = (z-224)*4096 + (y-128)*64 + (x-128)
	 * If z is between and including 240 - 247, then there are 4 bytes z y x w; ud = (z-240)*262144 + (y-128)*4096 + (x-128)*64 + (w-128)
	 * If z is between and including 248 - 251, then there are 5 bytes z y x w v; ud = (z-248)*16777216 + (y-128)*262144 + (x-128)*4096 + (w-128)*64 + (v-128)
	 * If z is 252 or 253, then there are 6 bytes z y x w v u; ud = (z-252)*1073741824 + (y-128)*16777216 + (x-128)*262144 + (w-128)*4096 + (v-128)*64 + (u-128)
	 * If z = 254 or 255 then there is something wrong!
	 *
	 * Example: take the decimal Unicode designation 8482 (decimal), which is for the trademark sign.
	 * This number is larger than 2048, so we get three numbers.
	 * The first number is 224 + (8482 div 4096) = 224 + 2 = 226.
	 * The second number is 128 + (8482 div 64) mod 64) = 128 + (132 mod 64) = 128 + 4 = 132.
	 * The third number is 128 + (8482 mod 64) = 128 + 34 = 162.
	 * Now the other way round. We see the numbers 226, 132 and 162. What is the decimal Unicode value?
	 * In this case: (226-224)*4096+(132-128)*64+(162-128) = 8482.
	 * And the conversion between hexadecimal and decimal? Come on, this is not a math tutorial! In case you don't know, use a calculator.
	*/
	function readUtf8(bytesToRead) {
		if (arguments.length == 0) bytesToRead = -1;

		var s = "";
		var byteCount = 0;

		while (bytesToRead == -1 || byteCount < bytesToRead) {
			var b0 = readByte();
			byteCount++;
			if (b0 == 0) break;

			if ((b0 & 0x80) == 0) {
				s += String.fromCharCode(b0);
				continue;
			}

			var ch;
			var b1 = readByte();
			byteCount++;
			if (b1 == 0) {
				//Dangling lead byte, do not decompose
				s += String.fromCharCode(b0);
				break;
			}

			if ((b0 & 0x20) == 0) {
				ch = String.fromCharCode(((b0 & 0x1F) << 6) | (b1 & 0x3F));
			} else {
				var b2 = readByte();
				byteCount++;
				if (b2 == 0) {
					//Dangling lead bytes, do not decompose
					s += String.fromCharCode((b0 << 8) | b1);
					break;
				}

				var ch32;
				if ((b0 & 0x10) == 0) {
					ch32 = ((b0 & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F);
				} else {
					var b3 = readByte();
					byteCount++;
					if (b3 == 0) {
						s += String.fromCharCode((b0 << 8) | b1);
						s += String.fromCharCode(b2);
						break;
					}
					ch32 = ((b0 & 0x07) << 0x18) // combine 6 bit parts
							| ((b1 & 0x3F) << 12)
							| ((b2 & 0x3F) << 6)
							| (b3 & 0x3F);
				}

				if ((ch32 & 0xFFFF0000) == 0) {
					ch = String.fromCharCode(ch32);
				} else {
					//break up into UTF16 surrogate pair
					s += String.fromCharCode((ch32 >> 10) | 0xD800);
					ch = String.fromCharCode((ch32 & 0x3FF) | 0xDC00);
				}
			}

			s += ch;
		}

		return s;
	}

	return stream;
}

<commit_msg>added stream.readByte<commit_after>/** @const */ var BOOL=0;
/** @const */ var I8=1;
/** @const */ var U8=2;
/** @const */ var I16=3;
/** @const */ var U16=4;
/** @const */ var I32=5;
/** @const */ var U32=6;
/** @const */ var F32=7;
/** @const */ var F64=8;
/** @const */ var DATE=9;
/** @const */ var UTF8=10;

/**
 * Creates binary stream reader with the following API:
 * - stream.read(type) - reads specified type from the stream,
 *   where type could be one of the following values:
 *     BOOL - to read boolean value,
 *     I8 - to read signed 8-bit integer number,
 *     U8 - to read unsigned 8-bit integer number,
 *     I16 - to read signed 16-bit integer number,
 *     U16 - to read unsigned 16-bit integer number,
 *     I32 - to read signed 32-bit integer number,
 *     U32 - to read unsigned 32-bit integer number,
 *     F32 - to read 32-bit floating number,
 *     F64 - to read 64-bit floating number,
 *     UTF8 - to read UTF8 string sequence terminated by zero,
 *     object - to read struct as sequence of primitive types or structs.
 */
function Stream(buffer, offset, length, le){
	if (arguments.length <= 1) offset = 0;
	if (arguments.length <= 2) length = buffer.byteLength;
	if (arguments.length <= 3) le = true;

	var start = offset;
	var pos = offset;
	var view = new DataView(buffer, offset, length);
	var stream = {
		length: length,
		position: function() { return pos; }
	};

	function isObject(v) { return !!(v && (typeof v == 'object')); }

	if (Object.keys === undefined) {
		Object.keys = function(v) {
			var arr = [];
			for (var k in v) {
				arr.push(k);
			}
			return arr;
		};
	}

	stream.read = function(type) {
		if (isObject(type)) return readSeq(type);
		return readPrimitive(type);
	};

	stream.readByte = function() {
		return view.getUint8(pos++);
	};

	stream.readBytes = function(count) {
		// TODO find a way to read buffer with one call
		var bytes = new Array(count);
		for (var i = 0; i < count; i++)
			bytes[i] = readByte();
		return bytes;
	};

	// TODO origin parameter
	stream.seek = function(offset) {
		pos = offset;
		return stream;
	};

	stream.skip = function(bytes) {
		pos += bytes;
		return stream;
	};

	stream.slice = function(position, size) {
		return Stream(buffer, start + position, size, le);
	};

	function NotImplemented() { return new Error("not implemented yet!"); }

	function readSeq(type) {
		var keys = Object.keys(type);
		var result = {};
		for (var i = 0; i < keys.length; i++) {
			var k = keys[i];
			var t = type[k];
			var v = stream.read(t);
			result[k] = v;
		}
		return result;
	}

	function readPrimitive(type) {
		var v;
		switch (type){
			case BOOL:
				return !!view.getInt8(pos++);
			case I8:
				return view.getInt8(pos++);
			case U8:
				return view.getUint8(pos++);
			case I16:
				v = view.getInt16(pos, le);
				pos += 2;
				break;
			case U16:
				v = view.getUint16(pos, le);
				pos += 2;
				break;
			case I32:
				v = view.getInt32(pos, le);
				pos += 4;
				break;
			case U32:
				v = view.getUint32(pos, le);
				pos += 4;
				break;
			case F32:
				v = view.getFloat32(pos, le);
				pos += 4;
				break;
			case F64:
				v = view.getFloat64(pos, le);
				pos += 8;
				break;
			case UTF8:
				return readUtf8();
			case DATE:
				throw NotImplemented();
		}
		return v;
	}

	function readByte(){
		return view.getUint8(pos++);
	}

	// TODO: find native way to read utf8 strings from array buffer

	/*
	 * UTF8 to unicode algorithm:
	 * Let's take a UTF-8 byte sequence. The first byte in a new sequence will tell us how long the sequence is.
	 * Let's call the subsequent decimal bytes z y x w v u.
	 *
	 * If z is between and including 0 - 127, then there is 1 byte z. The decimal Unicode value ud = the value of z.
	 * If z is between and including 192 - 223, then there are 2 bytes z y; ud = (z-192)*64 + (y-128)
	 * If z is between and including 224 - 239, then there are 3 bytes z y x; ud = (z-224)*4096 + (y-128)*64 + (x-128)
	 * If z is between and including 240 - 247, then there are 4 bytes z y x w; ud = (z-240)*262144 + (y-128)*4096 + (x-128)*64 + (w-128)
	 * If z is between and including 248 - 251, then there are 5 bytes z y x w v; ud = (z-248)*16777216 + (y-128)*262144 + (x-128)*4096 + (w-128)*64 + (v-128)
	 * If z is 252 or 253, then there are 6 bytes z y x w v u; ud = (z-252)*1073741824 + (y-128)*16777216 + (x-128)*262144 + (w-128)*4096 + (v-128)*64 + (u-128)
	 * If z = 254 or 255 then there is something wrong!
	 *
	 * Example: take the decimal Unicode designation 8482 (decimal), which is for the trademark sign.
	 * This number is larger than 2048, so we get three numbers.
	 * The first number is 224 + (8482 div 4096) = 224 + 2 = 226.
	 * The second number is 128 + (8482 div 64) mod 64) = 128 + (132 mod 64) = 128 + 4 = 132.
	 * The third number is 128 + (8482 mod 64) = 128 + 34 = 162.
	 * Now the other way round. We see the numbers 226, 132 and 162. What is the decimal Unicode value?
	 * In this case: (226-224)*4096+(132-128)*64+(162-128) = 8482.
	 * And the conversion between hexadecimal and decimal? Come on, this is not a math tutorial! In case you don't know, use a calculator.
	*/
	function readUtf8(bytesToRead) {
		if (arguments.length == 0) bytesToRead = -1;

		var s = "";
		var byteCount = 0;

		while (bytesToRead == -1 || byteCount < bytesToRead) {
			var b0 = readByte();
			byteCount++;
			if (b0 == 0) break;

			if ((b0 & 0x80) == 0) {
				s += String.fromCharCode(b0);
				continue;
			}

			var ch;
			var b1 = readByte();
			byteCount++;
			if (b1 == 0) {
				//Dangling lead byte, do not decompose
				s += String.fromCharCode(b0);
				break;
			}

			if ((b0 & 0x20) == 0) {
				ch = String.fromCharCode(((b0 & 0x1F) << 6) | (b1 & 0x3F));
			} else {
				var b2 = readByte();
				byteCount++;
				if (b2 == 0) {
					//Dangling lead bytes, do not decompose
					s += String.fromCharCode((b0 << 8) | b1);
					break;
				}

				var ch32;
				if ((b0 & 0x10) == 0) {
					ch32 = ((b0 & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F);
				} else {
					var b3 = readByte();
					byteCount++;
					if (b3 == 0) {
						s += String.fromCharCode((b0 << 8) | b1);
						s += String.fromCharCode(b2);
						break;
					}
					ch32 = ((b0 & 0x07) << 0x18) // combine 6 bit parts
							| ((b1 & 0x3F) << 12)
							| ((b2 & 0x3F) << 6)
							| (b3 & 0x3F);
				}

				if ((ch32 & 0xFFFF0000) == 0) {
					ch = String.fromCharCode(ch32);
				} else {
					//break up into UTF16 surrogate pair
					s += String.fromCharCode((ch32 >> 10) | 0xD800);
					ch = String.fromCharCode((ch32 & 0x3FF) | 0xDC00);
				}
			}

			s += ch;
		}

		return s;
	}

	return stream;
}

<|endoftext|>


<commit_before>(function(root, factory) {
    if (typeof exports === 'object') { // Node.js
        module.exports = factory(
            require('./events.js')
        );
    } else if (typeof define === 'function' && define.amd) { // Require.JS
        define(['jef/events'], factory);
    } else { // Browser globals
        root.jef = root.jef || {};
        root.jef.Stream = factory(
            root.jef.events
        );
    }
})(this, function(events, undefined) {
    'use strict';

    // Helper functions
    var slice =  Function.prototype.call.bind(Array.prototype.slice);

    // Private functions
    function drain() {
        var value,
        drain = this.options.drain;

        // You can drain once given stream
        this.options.drain = null;

        // Drain untill will be empty
        while (typeof (value = drain()) !== 'undefined') {
            this.push(value);
        }
    }

    /**
     * Represents a stream
     *
     * @constructor
     */
    function Stream(options) {
        events.call(this);

        this.options = options || {};
        this.chain = [];
        this.buffer = [];
        this.filtered = false;
    }
    Stream.constructor = Stream;
    Stream.prototype = new events();

    Stream.prototype.hasReaders = function() {
        return this.eventsCallbacks > 0;
    };
    Stream.prototype.hasDrain = function() {
        return typeof this.options.drain === 'function';
    };
    Stream.prototype.on = function(name, func) {
        var result = events.prototype.on.call(this, name, func);
        this.trigger('drain');
        return result;
    };
    Stream.prototype.pipe = function(toStream) {
        // Accept only streams
        if (!toStream instanceof Stream) {
            throw new Error('You can pipe to another stream, but given ' + toStream);
        }

        // Connect two streams
        this.on('data', toStream.push.bind(toStream));

        // If we can drain this stream then do it
        if (!this.hasDrain()) {
            return toStream;
        }

        // If next stream dont have readers,
        // and we have drain function postpone drain
        // until this stream or next will have stream readers
        if (!toStream.hasReaders()) {
            toStream.on('drain', drain.bind(this));
            return toStream;
        }

        // Drain data from stream
        drain.call(this);

        return toStream;
    };
    Stream.prototype.take = function (limit, skip, allowEmpty) {
        // Create new stream that will reject values that don't filter test
        this.chain.push(new Stream({
            // apply: true,
            take: {
                limit: limit,
                skip: skip || 0,
                allowEmpty: !! allowEmpty
            }
        }));
        return this.chain[this.chain.length - 1];
    }
    Stream.prototype.map = function(func) {
        // Create new stream with that maps values to new form
        this.chain.push(new Stream({
            apply: this.options.apply,
            map: func
        }));
        return this.chain[this.chain.length - 1];
    };
    Stream.prototype.accept = function(func) {
        // Create new stream that will accept values that pass filter test
        this.chain.push(new Stream({
            apply: this.options.apply,
            filter: func
        }));
        return this.chain[this.chain.length - 1];
    };
    Stream.prototype.reject = function(func) {
        // Create new stream that will reject values that don't filter test
        this.chain.push(new Stream({
            apply: this.options.apply,
            filter: function() {
                return !func.apply(func, arguments);
            }
        }));
        return this.chain[this.chain.length - 1];
    };
    Stream.prototype.reduce = function(func, base) {
        this.chain.push(new Stream({
            reduce: {
                func: func,
                base: base
            }
        }));
        return this.chain[this.chain.length - 1];
    };
    Stream.prototype.push = function(data) {
        if (this.hasDrain()) {
            throw new Error('Stream cant push data because it has drain function');
        }

        // Arguments to array
        data = slice(arguments);

        // On new data mark stream as not filtered
        this.filtered = false;

        // Test data if this part of the stream would like to accept it
        if (this.options.filter && !this.options.filter.apply(this.options.filter, data)) {
            this.filtered = true;
            return this.trigger('out', data, this);
        }

        // Lets map our result
        if (this.options.map) {
            if (typeof this.options.map === 'function')  {
                data = this.options.map.apply(this, data);
            } else {
                data = this.options.map;
            }
            data = Array.isArray(data) ? data : [data];
        }

        // Lets reduce our stream
        if (this.options.reduce) {
            data = this.options.reduce.func.apply(
                this,
                data.concat(
                    this.options.reduce.base
                )
            );
            this.options.reduce.base = data;
            data = Array.isArray(data) ? data : [data];
        }

        // Lets buffer some values in stream
        if (this.options.take) {
            // Collect streamed data
            this.buffer.push(data);

            var length = this.buffer.length;
            var end = length - this.options.take.skip;
            var start = end - this.options.take.limit;

            start = start < 0 ? 0 : start;

            data = this.buffer.slice(start, end);

            // Will take only when will have given limit in buffer
            if (!this.options.allowEmpty
                && data.length !== this.options.take.limit
            ) {
                return this;
            }
        }

        // Notify that data was set
        this.trigger('data', data, this);

        // Notify children nodes
        this.chain.forEach(function(stream) {
            stream.push.apply(stream, data);
        });

        return this;
    };
    Stream.prototype.merge = function(next) {
        return Stream.merge(this, next);
    };
    Stream.prototype.destroy = function() {
        // Custom destroy function
        this.options.destroy && this.options.destroy();
        // Destroy childs
        this.chain.forEach(function(stream) {
            stream.destroy();
        });
        // Invoking constructor clean properties
        Stream.call(this);
    };

    Stream.merge = function () {
        var streams = slice(arguments);
        var refs = [];
        var merged = new Stream({
            destroy: function() {
                streams.forEach(function(item, index) {
                    item.off('data', refs[index]);
                });
            }
        });
        streams.forEach(function (stream, index) {
            stream.on('data', refs[index] = function () {
                merged.push.apply(merged, arguments)
            });
        });

        return merged;
    }
    Stream.when = function() {
        var streams = slice(arguments);
        var refs = [];
        var buffer = new Array(streams.length);
        var called = new Array(streams.length);
        var result = new Stream({
            filter: function(streams) {
                // if true then is valid
                return -1 === streams.called.indexOf(false);
            },
            map: function(streams) {
                return streams.arguments;
            },
            destroy: function() {
                streams.forEach(function(item, index) {
                    item.off('out', refs[index]);
                    item.off('data', refs[index]);
                });
            }
        });

        streams.forEach(function(item, index) {
            called[index] = false;
            refs[index] = function(value) {
                called[index] = !this.filtered;
                buffer[index] = value;
                result.push({arguments: buffer, called: called });
            }
            item.on('out', refs[index]);
            item.on('data', refs[index]);
        });

        return result;
    };
    Stream.fromArray = function(array) {
        var index = -1, length = array.length;
        var result = new Stream({
            drain: function() {
                return ++index < length ? array[index] : undefined;
            }
        });
        return result;
    }

    return Stream;
});
<commit_msg>add descriptions<commit_after>(function(root, factory) {
    if (typeof exports === 'object') { // Node.js
        module.exports = factory(
            require('./events.js')
        );
    } else if (typeof define === 'function' && define.amd) { // Require.JS
        define(['jef/events'], factory);
    } else { // Browser globals
        root.jef = root.jef || {};
        root.jef.Stream = factory(
            root.jef.events
        );
    }
})(this, function(events, undefined) {
    'use strict';

    // Helper functions
    var slice =  Function.prototype.call.bind(Array.prototype.slice);

    /**
     * Drain the stream
     *
     * @this Stream
     */
    function drain() {
        var value,
        drain = this.options.drain;

        // You can drain once given stream
        this.options.drain = null;

        // Drain untill will be empty
        while (typeof (value = drain()) !== 'undefined') {
            this.push(value);
        }
    }

    /**
     * Represents a stream
     *
     * @constructor
     * @param {Object} options - Options object
     */
    function Stream(options) {
        events.call(this);

        this.options = options || {};
        this.chain = [];
        this.buffer = [];
        this.filtered = false;
    }
    Stream.constructor = Stream;
    Stream.prototype = new events();

    /**
     * Check if stream has readers
     *
     * @return {Boolean}
     */
    Stream.prototype.hasReaders = function() {
        return this.eventsCallbacks > 0;
    };

    /**
     * Check wether we can drain data on not
     *
     * @return {Boolean}
     */
    Stream.prototype.canDrain = function() {
        return typeof this.options.drain === 'function';
    };

    /**
     * Register new event handler, and trigger the 'drain' event that stream is ready to datin.
     *
     * @param {String} name     An event to which attach listener
     * @param {Function} func   An listener to listne to the event
     * @return {Stream}
     */
    Stream.prototype.on = function(name, func) {
        var result = events.prototype.on.call(this, name, func);
        this.trigger('drain');
        return result;
    };

    /**
     * Pipe one stream to another
     *
     * @param {Stream} stream   An stream to pipe
     */
    Stream.prototype.pipe = function(stream) {
        // Accept only streams
        if (!stream instanceof Stream) {
            throw new Error('You can pipe to another stream, but given ' + stream);
        }

        // Connect two streams
        this.on('data', stream.push.bind(stream));

        // If we can drain this stream then do it,
        // Otherwise just connect streams
        if (!this.canDrain()) {
            return stream;
        }

        // If next stream dont have readers,
        // and we have drain function postpone drain
        // until this stream or next will have stream readers
        if (!stream.hasReaders()) {
            stream.on('drain', drain.bind(this));
            return stream;
        }

        // Drain data from stream
        drain.call(this);

        return stream;
    };

    /**
     * Create new stream based on current.
     *
     * Stream will buffer values and push them only when reach given {size}.
     * If flag {allowEmpty} is set this stream wont waint till
     * buffer size will be equal {size}, and will push data along the stream
     * even if doesn't have enough size.
     *
     * @param {Integer} size
     * @param {Integer} skip
     * @param {Boolean} allowEmpty
     * @return {Stream}
     */
    Stream.prototype.take = function (size, skip, allowEmpty) {
        // Create new stream that will reject values that don't filter test
        this.chain.push(new Stream({
            // apply: true,
            take: {
                limit: size,
                skip: skip || 0,
                allowEmpty: !! allowEmpty
            }
        }));
        return this.chain[this.chain.length - 1];
    }

    /**
     * Create new stream which will map values in to new representation.
     *
     * @param {Function} func
     * @return {Stream}
     */
    Stream.prototype.map = function(func) {
        // Create new stream with that maps values to new form
        this.chain.push(new Stream({
            apply: this.options.apply,
            map: func
        }));
        return this.chain[this.chain.length - 1];
    };

    /**
     * Create new stream which will accept only values
     * that test by given function {func}
     *
     * @param {Function} func
     * @return {Stream}
     */
    Stream.prototype.accept = function(func) {
        // Create new stream that will accept values that pass filter test
        this.chain.push(new Stream({
            apply: this.options.apply,
            filter: func
        }));
        return this.chain[this.chain.length - 1];
    };

    /**
     * Create new stream which wont accept values
     * when given text function {func} wont accept them.
     *
     * @param {Function} func
     * @return {Stream}
     */
    Stream.prototype.reject = function(func) {
        // Create new stream that will reject values that don't filter test
        this.chain.push(new Stream({
            apply: this.options.apply,
            filter: function() {
                return !func.apply(func, arguments);
            }
        }));
        return this.chain[this.chain.length - 1];
    };

    /**
     * Create new stream that will take all incomming values
     * And reduce them to the {base} by applying given function {func}
     *
     * @param {Function} func
     * @param {Function} base
     * @return {Stream}
     */
    Stream.prototype.reduce = function(func, base) {
        this.chain.push(new Stream({
            reduce: {
                func: func,
                base: base
            }
        }));
        return this.chain[this.chain.length - 1];
    };

    /**
     * Push data along the stream
     *
     * @param {Object} data
     * @return {Stream}
     */
    Stream.prototype.push = function(data) {
        if (this.canDrain()) {
            throw new Error('Stream cant push data because it has drain function');
        }

        // Arguments to array
        data = slice(arguments);

        // On new data mark stream as not filtered
        this.filtered = false;

        // Test data if this part of the stream would like to accept it
        if (this.options.filter && !this.options.filter.apply(this.options.filter, data)) {
            this.filtered = true;
            return this.trigger('out', data, this);
        }

        // Lets map our result
        if (this.options.map) {
            if (typeof this.options.map === 'function')  {
                data = this.options.map.apply(this, data);
            } else {
                data = this.options.map;
            }
            data = Array.isArray(data) ? data : [data];
        }

        // Lets reduce our stream
        if (this.options.reduce) {
            data = this.options.reduce.func.apply(
                this,
                data.concat(
                    this.options.reduce.base
                )
            );
            this.options.reduce.base = data;
            data = Array.isArray(data) ? data : [data];
        }

        // Lets buffer some values in stream
        if (this.options.take) {
            // Collect streamed data
            this.buffer.push(data);

            var length = this.buffer.length;
            var end = length - this.options.take.skip;
            var start = end - this.options.take.limit;

            start = start < 0 ? 0 : start;

            data = this.buffer.slice(start, end);

            // Will take only when will have given limit in buffer
            if (!this.options.allowEmpty
                && data.length !== this.options.take.limit
            ) {
                return this;
            }
        }

        // Notify that data was set
        this.trigger('data', data, this);

        // Notify children nodes
        this.chain.forEach(function(stream) {
            stream.push.apply(stream, data);
        });

        return this;
    };

    /**
     * Crate new stream that merge two stream into one
     *
     * @param {Stream} next
     * @return {Stream}
     */
    Stream.prototype.merge = function(next) {
        return Stream.merge(this, next);
    };

    /**
     * Destroy stream
     */
    Stream.prototype.destroy = function() {
        // Custom destroy function
        this.options.destroy && this.options.destroy();
        // Destroy childs
        this.chain.forEach(function(stream) {
            stream.destroy();
        });
        // Invoking constructor clean properties
        Stream.call(this);
    };

    Stream.merge = function () {
        var streams = slice(arguments);
        var refs = [];
        var merged = new Stream({
            destroy: function() {
                streams.forEach(function(item, index) {
                    item.off('data', refs[index]);
                });
            }
        });
        streams.forEach(function (stream, index) {
            stream.on('data', refs[index] = function () {
                merged.push.apply(merged, arguments)
            });
        });

        return merged;
    }
    Stream.when = function() {
        var streams = slice(arguments);
        var refs = [];
        var buffer = new Array(streams.length);
        var called = new Array(streams.length);
        var result = new Stream({
            filter: function(streams) {
                // if true then is valid
                return -1 === streams.called.indexOf(false);
            },
            map: function(streams) {
                return streams.arguments;
            },
            destroy: function() {
                streams.forEach(function(item, index) {
                    item.off('out', refs[index]);
                    item.off('data', refs[index]);
                });
            }
        });

        streams.forEach(function(item, index) {
            called[index] = false;
            refs[index] = function(value) {
                called[index] = !this.filtered;
                buffer[index] = value;
                result.push({arguments: buffer, called: called });
            }
            item.on('out', refs[index]);
            item.on('data', refs[index]);
        });

        return result;
    };
    Stream.fromArray = function(array) {
        var index = -1, length = array.length;
        var result = new Stream({
            drain: function() {
                return ++index < length ? array[index] : undefined;
            }
        });
        return result;
    }

    return Stream;
});
<|endoftext|>