<commit_before>import { getCache, getReduxStore } from '../cache'

export const ATTACH_REDUCER = '@KEA/ATTACH_REDUCER'
export const DETACH_REDUCER = '@KEA/DETACH_REDUCER'

const defaultState = {}

function initRootReducerTree (pathStart) {
  const { reducerTree } = getCache()
  if (!reducerTree[pathStart]) {
    reducerTree[pathStart] = {}
    regenerateRootReducer(pathStart)
  }
}

export function keaReducer (pathStart = 'scenes', options = {}) {
  const { rootReducers } = getCache()
  initRootReducerTree(pathStart)

  if (options && options.default) {
    getCache().defaultReducerRoot = pathStart
  }

  return (state = defaultState, action) => {
    return rootReducers[pathStart] ? rootReducers[pathStart](state, action) : state
  }
}

export function firstReducerRoot () {
  const { defaultReducerRoot, reducerTree } = getCache()
  return defaultReducerRoot || Object.keys(reducerTree)[0]
}

export function attachReducer (path, reducer) {
  const { reducerTree } = getCache()
  const pathStart = path[0]

  initRootReducerTree(pathStart)

  let pointer = reducerTree

  for (let i = 0; i < path.length; i++) {
    const pathPart = path[i]

    // last part of the path, so [..., pathPart] = path
    if (i === path.length - 1) {
      // there's already something here!
      if (pointer[pathPart]) {
        // if we're in the root level in the tree and it's an empty object
        if (i === 0 && typeof pointer[pathPart] === 'object' && Object.keys(pointer[pathPart]).length === 0) {
          // don't block here

        // if it's a function, assume it's a reducer and replacing it is fine
        // otherwise give an error
        } else if (typeof pointer[pathPart] !== 'function') {
          console.error(`[KEA-LOGIC] Can not add reducer to "${path.join('.')}". There is something in the way:`, pointer[pathPart])
          return
        }
      }

      pointer[pathPart] = reducer
    } else {
      if (!pointer[pathPart]) {
        pointer[pathPart] = {}
      }
      pointer = pointer[pathPart]
    }
  }

  regenerateRootReducer(pathStart)

  const store = getReduxStore()
  store && store.dispatch({ type: ATTACH_REDUCER, payload: { path, reducer } })
}

export function detachReducer (path) {
  const { reducerTree } = getCache()
  const pathStart = path[0]

  initRootReducerTree(pathStart)

  let pointer = reducerTree

  let detached = false

  // ['scenes', 'sceneName', 'page', 'key']
  for (let i = path.length - 2; i >= 0; i--) {
    let pointerToHere = pointer
    for (let j = 0; j <= i; j++) {
      pointerToHere = (pointerToHere && pointerToHere[path[j]]) || undefined
    }

    if (pointerToHere) {
      if (Object.keys(pointerToHere).length === 0) {
        // next
      } else if (Object.keys(pointerToHere).length >= 1 && i === path.length - 2 && typeof pointerToHere[path[i + 1]] === 'function') {
        delete pointerToHere[path[i + 1]]
        detached = true
      } else if (detached && Object.keys(pointerToHere).length >= 1 && i < path.length - 2 && Object.keys(pointerToHere[path[i + 1]]).length === 0) {
        delete pointerToHere[path[i + 1]]
      } else {
        break
      }
    }
  }

  regenerateRootReducer(pathStart)

  if (detached) {
    const store = getReduxStore()
    store && store.dispatch({ type: DETACH_REDUCER, payload: { path } })
  }
}

export function regenerateRootReducer (pathStart) {
  const { reducerTree, rootReducers } = getCache()
  const rootReducer = recursiveCreateReducer(reducerTree[pathStart])

  rootReducers[pathStart] = rootReducer
}

export function recursiveCreateReducer (treeNode) {
  if (typeof treeNode === 'function') {
    return treeNode
  }

  let children = {}

  Object.keys(treeNode).forEach(key => {
    if (typeof treeNode[key] !== 'undefined') {
      children[key] = recursiveCreateReducer(treeNode[key])
    }
  })

  // we have reducers, return combineReducers
  if (Object.keys(children).length > 0) {
    return combineKeaReducers(children)

  // we have reducers that were removed, return something that just returns an empty object
  // } else if (Object.keys(treeNode).length > 0) {
  } else {
    const emptyObj = {}
    return () => emptyObj

  // no reducers and nothing was ever removed... return something that returns with the preloaded state
  // } else {
  //   return (state, action) => state
  }
}

// We are using our own function for the tree nodes instead of redux's combineReducers beacause this way we will not
// get the constant 'Unexpected key "1" found in previous state received by the reducer' warnings when unmounting
function combineKeaReducers (reducers) {
  const reducerKeys = Object.keys(reducers)

  return function combination (state = {}, action) {
    let stateChanged = false
    let nextState = {}

    for (let i = 0; i < reducerKeys.length; i++) {
      const key = reducerKeys[i]
      const reducer = reducers[key]
      const previousKeyState = state[key]
      const nextKeyState = reducer(previousKeyState, action)
      if (typeof nextKeyState === 'undefined') {
        throw new Error(`[KEA] Reducer "${key}" returned undefined for action "${action && action.type}"`)
      }
      nextState[key] = nextKeyState
      stateChanged = stateChanged || nextKeyState !== previousKeyState
    }

    return stateChanged ? nextState : state
  }
}
<commit_msg>the state definitely changed if some key was added/removed<commit_after>import { getCache, getReduxStore } from '../cache'

export const ATTACH_REDUCER = '@KEA/ATTACH_REDUCER'
export const DETACH_REDUCER = '@KEA/DETACH_REDUCER'

const defaultState = {}

function initRootReducerTree (pathStart) {
  const { reducerTree } = getCache()
  if (!reducerTree[pathStart]) {
    reducerTree[pathStart] = {}
    regenerateRootReducer(pathStart)
  }
}

export function keaReducer (pathStart = 'scenes', options = {}) {
  const { rootReducers } = getCache()
  initRootReducerTree(pathStart)

  if (options && options.default) {
    getCache().defaultReducerRoot = pathStart
  }

  return (state = defaultState, action) => {
    return rootReducers[pathStart] ? rootReducers[pathStart](state, action) : state
  }
}

export function firstReducerRoot () {
  const { defaultReducerRoot, reducerTree } = getCache()
  return defaultReducerRoot || Object.keys(reducerTree)[0]
}

export function attachReducer (path, reducer) {
  const { reducerTree } = getCache()
  const pathStart = path[0]

  initRootReducerTree(pathStart)

  let pointer = reducerTree

  for (let i = 0; i < path.length; i++) {
    const pathPart = path[i]

    // last part of the path, so [..., pathPart] = path
    if (i === path.length - 1) {
      // there's already something here!
      if (pointer[pathPart]) {
        // if we're in the root level in the tree and it's an empty object
        if (i === 0 && typeof pointer[pathPart] === 'object' && Object.keys(pointer[pathPart]).length === 0) {
          // don't block here

        // if it's a function, assume it's a reducer and replacing it is fine
        // otherwise give an error
        } else if (typeof pointer[pathPart] !== 'function') {
          console.error(`[KEA-LOGIC] Can not add reducer to "${path.join('.')}". There is something in the way:`, pointer[pathPart])
          return
        }
      }

      pointer[pathPart] = reducer
    } else {
      if (!pointer[pathPart]) {
        pointer[pathPart] = {}
      }
      pointer = pointer[pathPart]
    }
  }

  regenerateRootReducer(pathStart)

  const store = getReduxStore()
  store && store.dispatch({ type: ATTACH_REDUCER, payload: { path, reducer } })
}

export function detachReducer (path) {
  const { reducerTree } = getCache()
  const pathStart = path[0]

  initRootReducerTree(pathStart)

  let pointer = reducerTree

  let detached = false

  // ['scenes', 'sceneName', 'page', 'key']
  for (let i = path.length - 2; i >= 0; i--) {
    let pointerToHere = pointer
    for (let j = 0; j <= i; j++) {
      pointerToHere = (pointerToHere && pointerToHere[path[j]]) || undefined
    }

    if (pointerToHere) {
      if (Object.keys(pointerToHere).length === 0) {
        // next
      } else if (Object.keys(pointerToHere).length >= 1 && i === path.length - 2 && typeof pointerToHere[path[i + 1]] === 'function') {
        delete pointerToHere[path[i + 1]]
        detached = true
      } else if (detached && Object.keys(pointerToHere).length >= 1 && i < path.length - 2 && Object.keys(pointerToHere[path[i + 1]]).length === 0) {
        delete pointerToHere[path[i + 1]]
      } else {
        break
      }
    }
  }

  regenerateRootReducer(pathStart)

  if (detached) {
    const store = getReduxStore()
    store && store.dispatch({ type: DETACH_REDUCER, payload: { path } })
  }
}

export function regenerateRootReducer (pathStart) {
  const { reducerTree, rootReducers } = getCache()
  const rootReducer = recursiveCreateReducer(reducerTree[pathStart])

  rootReducers[pathStart] = rootReducer
}

export function recursiveCreateReducer (treeNode) {
  if (typeof treeNode === 'function') {
    return treeNode
  }

  let children = {}

  Object.keys(treeNode).forEach(key => {
    if (typeof treeNode[key] !== 'undefined') {
      children[key] = recursiveCreateReducer(treeNode[key])
    }
  })

  // we have reducers, return combineReducers
  if (Object.keys(children).length > 0) {
    return combineKeaReducers(children)

  // we have reducers that were removed, return something that just returns an empty object
  // } else if (Object.keys(treeNode).length > 0) {
  } else {
    const emptyObj = {}
    return () => emptyObj

  // no reducers and nothing was ever removed... return something that returns with the preloaded state
  // } else {
  //   return (state, action) => state
  }
}

// We are using our own function for the tree nodes instead of redux's combineReducers beacause this way we will not
// get the constant 'Unexpected key "1" found in previous state received by the reducer' warnings when unmounting
function combineKeaReducers (reducers) {
  const reducerKeys = Object.keys(reducers)

  return function combination (state = {}, action) {
    let stateChanged = Object.keys(state).length !== reducerKeys.length
    let nextState = {}

    for (let i = 0; i < reducerKeys.length; i++) {
      const key = reducerKeys[i]
      const reducer = reducers[key]
      const previousKeyState = state[key]
      const nextKeyState = reducer(previousKeyState, action)
      if (typeof nextKeyState === 'undefined') {
        throw new Error(`[KEA] Reducer "${key}" returned undefined for action "${action && action.type}"`)
      }
      nextState[key] = nextKeyState
      stateChanged = stateChanged || nextKeyState !== previousKeyState
    }

    return stateChanged ? nextState : state
  }
}
<|endoftext|>


<commit_before>/**
 * Copyright 2013 Mark Watson. All rights reserved.
 * This code may be used under the AGPL version 3 license.
 * This notice must remain in this file and derived files.
 */

var learningRate = 0.15;

function new_neural_network(num_input, num_hidden, num_output) {
  var i, h, o, j, data;
  data = {
    numInputs: num_input,
    numHidden: num_hidden,
    numOutputs: num_output,
    inputs: [],  // num_input
    hidden: [],  // num_hidden
    outputs: [], // num_output
    w1: [],      // num_input  * num_hidden
    w2: [],      // num_hidden * num_output
    output_errors: [], // num_output
    hidden_errors: [], // num_hidden),
    input_training_examples: [],
    output_training_examples: []
  };
  for (i = 0; i < num_input; i += 1) {
    data.w1[i] = []; // num_hidden
    data.inputs[i] = 0;
  }
  for (i = 0; i < num_hidden; i += 1) {
    data.w2[i] = []; // num_output);
  }
  for (i = 0; i < num_input; i += 1) {
    for (h = 0; h < num_hidden; h += 1) {
      data.w1[i][h] = 0.1 * (Math.random() - 0.5);
    }
  }
  for (h = 0; h < num_hidden; h += 1) {
    for (o = 0; o < num_output; o += 1) {
      data.w2[h][o] = 0.1 * (Math.random() - 0.5);
    }
  }
  return data;
}

function add_training_example(nn, inputs, outputs) {
  nn.input_training_examples.push(inputs);
  nn.output_training_examples.push(outputs);
}

function sigmoid(x) {
  return (1.0 / (1.0 + Math.exp(-x)));
}

function sigmoidP(x) {
  var z = sigmoid(x);
  return (z * (1.0 - z));
}

function forward_pass(nn) {
  var i, h, o;
  for (h = 0; h < nn.numHidden; h += 1) {
    nn.hidden[h] = 0;
  }
  for (i = 0; i < nn.numInputs; i += 1) {
    for (h = 0; h < nn.numHidden; h += 1) {
      nn.hidden[h] += nn.inputs[i] * nn.w1[i][h];
    }
  }
  for (o = 0; o < nn.numOutputs; o += 1) {
    nn.outputs[o] = 0;
  }
  for (h = 0; h < nn.numHidden; h += 1) {
    for (o = 0; o < nn.numOutputs; o += 1) {
      nn.outputs[o] += sigmoid(nn.hidden[h]) * nn.w2[h][o];
    }
  }
  for (o = 0; o < nn.numOutput; o += 1) {
    nn.outputs[o] = sigmoid(nn.outputs[o]);
  }
}

function train(nn) {
  var i, h, o, ncase, error = 0, outs, num_cases;
  num_cases = nn.input_training_examples.length;
  for (ncase = 0; ncase < num_cases; ncase += 1) {
    // zero out the errors: at the hidden and output layers:
    for (h = 0; h < nn.numHidden; h += 1) {
      nn.hidden_errors[h] = 0;
    }
    for (o = 0; o < nn.numOutputs; o += 1) {
      nn.output_errors[o] = 0;
    }
    for (i = 0; i < nn.numInputs; i += 1) {
      nn.inputs[i] = nn.input_training_examples[ncase][i];
    }
    outs = nn.output_training_examples[ncase];
    forward_pass(nn);
    for (o = 0; o < nn.numOutputs; o += 1) {
      nn.output_errors[o] = (outs[o] - nn.outputs[o]) * sigmoidP(nn.outputs[o]);
    }
    for (h = 0; h < nn.numHidden; h += 1) {
      nn.hidden_errors[h] = 0.0;
      for (o = 0; o < nn.numOutputs; o += 1) {
        nn.hidden_errors[h] += nn.output_errors[o] * nn.w2[h][o];
      }
    }
    for (h = 0; h < nn.numHidden; h += 1) {
      nn.hidden_errors[h] =
        nn.hidden_errors[h] * sigmoidP(nn.hidden[h]);
    }
    // update the hidden to output weights:
    for (o = 0; o < nn.numOutputs; o += 1) {
      for (h = 0; h < nn.numHidden; h += 1) {
        nn.w2[h][o] +=
          learningRate * nn.output_errors[o] * nn.hidden[h];
      }
    }
    // update the input to hidden weights:
    for (h = 0; h < nn.numHidden; h += 1) {
      for (i = 0; i < nn.numInputs; i += 1) {
        nn.w1[i][h] +=
          learningRate * nn.hidden_errors[h] * nn.inputs[i];
      }
    }
    for (o = 0; o < nn.numOutputs; o += 1) {
      error += Math.abs(outs[o] - nn.outputs[o]);
    }
  }
  return error;
}

function recall(nn, inputs) {
  var i, num_inputs = nn.inputs.length;
  for (i = 0; i < num_inputs; i += 1) {
    nn.inputs[i] = inputs[i];
  }
  forward_pass(nn);
  return nn.outputs;
}

function test_nn() {
  var error, iter, test_data = new_neural_network(3, 3, 3);
  add_training_example(test_data, [0.1, 0.1, 0.9], [0.9, 0.1, 0.1]);
  add_training_example(test_data, [0.1, 0.9, 0.1], [0.1, 0.1, 0.9]);
  add_training_example(test_data, [0.9, 0.1, 0.1], [0.1, 0.9, 0.1]);
  console.log(test_data);
  console.log(test_data);
  for (iter = 0; iter < 1000; iter += 1) {
    console.log(train(test_data));
  }
  console.log(recall(test_data, [0.08, 0.2, 0.88]));
  console.log(recall(test_data, [0.93, 0.2, 0.11]));
  console.log(recall(test_data, [0.11, 0.9, 0.06]));
}

// test everything:
test_nn();<commit_msg>improved backprop example<commit_after>/**
 * Copyright 2013 Mark Watson. All rights reserved.
 * This code may be used under the AGPL version 3 license.
 * This notice must remain in this file and derived files.
 */

var learningRate_w1 = 0.1, learningRate_w2 = 0.05;

function new_neural_network(num_input, num_hidden, num_output) {
  var i, h, o, data;
  data = {
    numInput: num_input,
    numHidden: num_hidden,
    numOutput: num_output,
    inputs: [],  // num_input
    hidden: [],  // num_hidden
    outputs: [], // num_output
    w1: [],      // num_input  * num_hidden
    w2: [],      // num_hidden * num_output
    output_errors: [], // num_output
    hidden_errors: [], // num_hidden),
    input_training_examples: [],
    output_training_examples: []
  };
  for (i = 0; i < num_input; i += 1) {
    data.w1[i] = []; // num_hidden
    data.inputs[i] = 0;
  }
  for (i = 0; i < num_hidden; i += 1) {
    data.w2[i] = []; // num_output);
  }
  for (i = 0; i < num_input; i += 1) {
    for (h = 0; h < num_hidden; h += 1) {
      data.w1[i][h] = 0.05 * (Math.random() - 0.5);
    }
  }
  for (h = 0; h < num_hidden; h += 1) {
    for (o = 0; o < num_output; o += 1) {
      data.w2[h][o] = 0.005 * (Math.random() - 0.5);
    }
  }
  return data;
}

function add_training_example(nn, inputs, outputs) {
  nn.input_training_examples.push(inputs);
  nn.output_training_examples.push(outputs);
}

function sigmoid(x) {
  return (1.0 / (1.0 + Math.exp(-x)));
}

function sigmoidP(x) {
  var z = sigmoid(x);
  return (z * (1.0 - z));
}

function forward_pass(nn) {
  var i, h, o;
  for (h = 0; h < nn.numHidden; h += 1) {
    nn.hidden[h] = 0;
  }
  for (i = 0; i < nn.numInput; i += 1) {
    for (h = 0; h < nn.numHidden; h += 1) {
      nn.hidden[h] += nn.inputs[i] * nn.w1[i][h];
    }
  }
  for (o = 0; o < nn.numOutput; o += 1) {
    nn.outputs[o] = 0;
  }
  for (h = 0; h < nn.numHidden; h += 1) {
    for (o = 0; o < nn.numOutput; o += 1) {
      nn.outputs[o] += sigmoid(nn.hidden[h]) * nn.w2[h][o];
    }
  }
  for (o = 0; o < nn.numOutput; o += 1) {
    nn.outputs[o] = sigmoid(nn.outputs[o]);
  }
}

function reset_weights(nn) {
  console.log("* nn.numInput=" + nn.numInput + ", nn.numHidden=" + nn.numHidden + ", nn.numOutput=" + nn.numOutput);
  //console.log(nn);
  var i, h, o;
  for (i = 0; i < nn.numInput; i += 1) {
    for (h = 0; h < nn.numHidden; h += 1) {
      nn.w1[i][h] = 0.025 * (Math.random() - 0.5);
    }
  }
  for (h = 0; h < nn.numHidden; h += 1) {
    for (o = 0; o < nn.numOutput; o += 1) {
      nn.w2[h][o] = 0.005 * (Math.random() - 0.5);
    }
  }
  //console.log(nn);
}

function train_helper(nn) {
  var i, h, o, ncase, error = 0, outs, num_cases;
  num_cases = nn.input_training_examples.length;
  for (ncase = 0; ncase < num_cases; ncase += 1) {
    // zero out the errors: at the hidden and output layers:
    for (h = 0; h < nn.numHidden; h += 1) {
      nn.hidden_errors[h] = 0;
    }
    for (o = 0; o < nn.numOutput; o += 1) {
      nn.output_errors[o] = 0;
    }
    for (i = 0; i < nn.numInput; i += 1) {
      nn.inputs[i] = nn.input_training_examples[ncase][i];
    }
    outs = nn.output_training_examples[ncase];
    forward_pass(nn);
    for (o = 0; o < nn.numOutput; o += 1) {
      nn.output_errors[o] = (outs[o] - nn.outputs[o]) * sigmoidP(nn.outputs[o]);
    }
    for (h = 0; h < nn.numHidden; h += 1) {
      nn.hidden_errors[h] = 0.0;
      for (o = 0; o < nn.numOutput; o += 1) {
        nn.hidden_errors[h] += nn.output_errors[o] * nn.w2[h][o];
      }
    }
    for (h = 0; h < nn.numHidden; h += 1) {
      nn.hidden_errors[h] =
        nn.hidden_errors[h] * sigmoidP(nn.hidden[h]);
    }
    // update the hidden to output weights:
    for (o = 0; o < nn.numOutput; o += 1) {
      for (h = 0; h < nn.numHidden; h += 1) {
        nn.w2[h][o] +=
          learningRate_w2 * nn.output_errors[o] * nn.hidden[h];
      }
    }
    // update the input to hidden weights:
    for (h = 0; h < nn.numHidden; h += 1) {
      for (i = 0; i < nn.numInput; i += 1) {
        nn.w1[i][h] +=
          learningRate_w1 * nn.hidden_errors[h] * nn.inputs[i];
      }
    }
    for (o = 0; o < nn.numOutput; o += 1) {
      error += Math.abs(outs[o] - nn.outputs[o]);
    }
  }
  return error;
}

function recall(nn, inputs) {
  var i, num_inputs = nn.inputs.length;
  for (i = 0; i < num_inputs; i += 1) {
    nn.inputs[i] = inputs[i];
  }
  forward_pass(nn);
  return nn.outputs;
}

function train(nn) {
  for (iter = 0; iter < 150000; iter += 1) {
    error = train_helper(nn);
    if ((iter % 800) === 0) {
      console.log(error);
    }
    if (error < 0.05) {
      break;
    }
    // reset weights: if network get stuck then start over:
    if (error > 2.0  && (iter % 5111) === 0) {
      console.log("** reset weights");
      reset_weights(nn);
    }
  }
}

function test_nn() {
  var error, iter, test_network = new_neural_network(3, 3, 3);
  add_training_example(test_network, [0.1, 0.1, 0.9], [0.9, 0.1, 0.1]);
  add_training_example(test_network, [0.1, 0.9, 0.1], [0.1, 0.1, 0.9]);
  add_training_example(test_network, [0.9, 0.1, 0.1], [0.1, 0.9, 0.1]);
  train(test_network);
  console.log(recall(test_network, [0.08, 0.2, 0.88]));
  console.log(recall(test_network, [0.93, 0.2, 0.11]));
  console.log(recall(test_network, [0.11, 0.9, 0.06]));
}

// test everything:
test_nn();<|endoftext|>


<commit_before>define(['./stream', './both'], function(Stream, both) {
    'use strict';

    /**
     * @param {Stream} stream
     * @return {Stream}
     */
    return function concat(stream) {
        return new Stream(function(sinkValue, sinkError) {
            stream.on(function(value, next) {
                if (Stream.streamable(value)) {
                    concat(both(value, next)).on(function(value, inner) {
                        sinkValue(
                            value,
                            inner
                        );

                        return Stream.stop;
                    }, sinkError);
                } else {
                    sinkValue(value, concat(next));
                }

                return Stream.stop;
            }, sinkError);
        })
    }
});
<commit_msg>Update concat<commit_after>define(['./stream', './both'], function(Stream, both) {
    'use strict';

    /**
     * @param {Stream} stream
     * @return {Stream}
     */
    return function concat(stream) {
        return new Stream(function(sinkValue, sinkError) {
            stream.on(function(value, next) {
                if (Stream.streamable(value)) {
                    concat(both(value, next)).on(function(value, inner) {
                        sinkValue(
                            value,
                            inner
                        );

                        return Stream.stop;
                    }, sinkError);
                } else {
                    sinkValue(
                        value,
                        Stream.streamable(next)
                            ? concat(next)
                            : Stream.stop
                    );
                }

                return Stream.stop;
            }, sinkError);
        })
    }
});
<|endoftext|>


<commit_before>"use strict";

var Q = require("q");
var _ = require("underscore");
var pubit = require("pubit-as-promised");
var whatsDifferent = require("./utils").whatsDifferent;
var previously = require("./utils").previously;
var isObject = require("./utils").isObject;
var cloneObject = require("./utils").cloneObject;
var isEqual = require("./utils").isEqual;
var Action = require("./Action");
var StoreitError = require("./StoreitError");

// Built-in mixins.
var mixins = [
    require("./mixins/map"),
    require("./mixins/enumerables"),
    require("./mixins/metadata")
];

var NS_SEPARATOR_CHAR = "#";

var events = ["added", "modified", "removed", "cleared", "ready"];
var EventName = _.object(events, events);

function throwUninitializedError() {
    throw new StoreitError(StoreitError.loadUninitialized);
}

function throwIfUndefined(value) {
    if (value === undefined) {
        throw new StoreitError(StoreitError.undefinedValue);
    }
}

function fakePublish() {}
fakePublish.when = function () {
    return Q.resolve([]);
};

function Storeit(namespace, storageProvider) {
    var that = this;
    var cache = {};

    if (namespace.indexOf(NS_SEPARATOR_CHAR) !== -1) {
        throw new StoreitError(StoreitError.invalidNamespace);
    }

    var options = { // Defaut options.
        publish: true,
        publishRemoveOnClear: false
    };

    var publish = pubit.makeEmitter(that, events); // Mixin `on`, `off`, `once`.
    var originalPublish = publish;

    var throwIfUninitialized = throwUninitializedError; // Default until `load` is called.

    function isInitialized() {
        return throwIfUninitialized !== throwUninitializedError;
    }

    function initialize() {
        var needsInitialization = !isInitialized();
        if (needsInitialization) {
            throwIfUninitialized = _.noop; // Allow other methods to work without throwing.
        }
        return needsInitialization;
    }

    function addEvent(eventName) {
        if (!Storeit.EventName[eventName]) {
            events.push(eventName);
            Storeit.EventName[eventName] = eventName;
        }
    }

    function typeCheckKey(key) {
        throwIfUninitialized();
        if (typeof key !== "string") {
            throw new StoreitError(StoreitError.invalidKey);
        }
    }

    function nskey(key, modifier) {
        return namespace + (modifier ? NS_SEPARATOR_CHAR + modifier : "") + ":" + key;
    }

    function ikey(key) {
        return nskey(key, "index"); // ex: mynamespace#index:primary
    }

    function mkey(key) {
        return nskey(key, "metadata"); // ex: mynamespace#metadata:123
    }

    function has(key) {
        typeCheckKey(key);
        return key in cache;
    }

    function getValue(key) {
        return cloneObject(cache[key].value);
    }

    function removeKeyFromCache(key) {
        var value = getValue(key);
        delete cache[key];
        return value;
    }

    function removeKey(key) {
        if (has(key)) {
            var removedValue = removeKeyFromCache(key);
            storageProvider.removeItem(nskey(key));
            storageProvider.removeItem(mkey(key));
            publish(EventName.removed, removedValue, key);

            setIndex();
            return {
                action: Action.removed,
                key: key,
                value: removedValue
            };
        } else {
            throw new StoreitError(StoreitError.nonexistentKey);
        }
    }

    function setMetadata(key, value) {
        throwIfUndefined(value);
        cache[key].metadata = value;
        storageProvider.setItem(mkey(key), value);
    }

    function setCache(key, value) {
        var results = {};
        if (has(key)) {
            var previousPartial;
            var partial;
            var currentValue = getValue(key);
            if (isObject(value) && isObject(currentValue)) {
                value = _.extend(cloneObject(currentValue), value); // Allow "patching" with partial value.
                partial = whatsDifferent(currentValue, value);
                previousPartial = previously(currentValue, partial);
            } else {
                partial = value;
                previousPartial = currentValue;
            }
            if (isEqual(currentValue, value)) {
                results.action = Action.none;
            } else {
                cache[key].value = value;
                publish(EventName.modified, partial, key, previousPartial);
                results.previousValue = previousPartial;
                results.action = Action.modified;
            }
        } else {
            cache[key] = {
                value: value,
                metadata: null
            };
            publish(EventName.added, cloneObject(value), key);
            results.action = Action.added;
        }
        results.key = key;
        results.value = value;
        return results;
    }

    function setValue(key, value, metadata) {
        throwIfUndefined(value);
        var results = setCache(key, value, metadata);
        if (results.action === Action.added) {
            setSerializerName();
            setIndex();
        }
        if (results.action !== Action.none) {
            storageProvider.setItem(nskey(key), results.value);
        }
        if (metadata !== undefined) {
            setMetadata(key, metadata);
            results.metadata = metadata;
        }
        return results;
    }

    function getIndex() {
        return storageProvider.getMetadata(ikey("primary")) || [];
    }

    function setIndex() {
        var keys = Object.keys(cache);
        storageProvider.setMetadata(ikey("primary"), keys);
    }

    // Read in the base namespace key.
    function initializeItemSerializer(hasItems) {
        // Is there is a itemSerializer specified, we MUST use it.
        // If existing data and no itemSerializer specified, this is an old JSON database,
        // so "fake" the compatible JSON serializer
        var providerInfo = storageProvider.getMetadata(namespace); // TODO I hate this!
        var itemSerializerName = providerInfo ? providerInfo.itemSerializer :
            hasItems ? "JSONSerializer" : null;
        storageProvider.itemSerializer = itemSerializerName;
    }

    function setSerializerName() {
        var data = {
            itemSerializer: storageProvider.itemSerializer
        };
        storageProvider.setMetadata(namespace, data);
    }

    function publishProxy() {
        publish.apply(null, arguments);
    }

    // Here are a few built-in methods and properties.
    that.clear = function () {
        var publishReady = initialize(); // Initialize only if needed and return true if initalization was performed.
        getIndex().forEach(function (key) { // Remove everything from provider, loaded or not.
            storageProvider.removeItem(nskey(key));
            storageProvider.removeItem(mkey(key));
        });
        // Publish and remove (or simply remove) each value from cache.
        Object.keys(cache).reverse().forEach(options.publishRemoveOnClear ? removeKey : removeKeyFromCache);
        storageProvider.removeItem(ikey("primary"));
        storageProvider.removeItem(namespace); // Remove the storageMetadata for the namespace.
        publish(EventName.cleared);
        if (publishReady) {
            publish(EventName.ready);
        }
    };

    Object.defineProperty(that, "options", {
        get: function () {
            return options;
        },
        set: function (value) {
            _.extend(options, value);
            publish = options.publish ? originalPublish : fakePublish;
            publishProxy.when = publish.when; // So that mixins can use publish.when if they so desire.
        },
        enumerable: true
    });

    Object.defineProperty(that, "isInitialized", {
        get: isInitialized,
        enumerable: true
    });

    Object.defineProperty(that, "namespace", {
        value: namespace,
        enumerable: true
    });

    that.load = function () {
        throwIfUninitialized = _.noop; // Allow other methods to work without throwing.
        var index = getIndex();
        initializeItemSerializer(!!index.length);
        index.forEach(function (key) { // For each key in "namespace#index:primary"...
            var value = storageProvider.getItem(nskey(key));
            setCache(key, value); // Build cache and publish an "added" events.
        });
        publish(EventName.ready);
    };

    // Expose these internal functions to the mixins.
    var local = {
        has: has,
        getValue: getValue,
        setValue: setValue,
        setMetadata: setMetadata,
        removeKey: removeKey,
        publish: publishProxy,
        addEvent: addEvent
    };

    // Expose these internal properties.
    Object.defineProperty(local, "cache", {
        get: function () {
            throwIfUninitialized();
            return cache;
        },
        enumerable: true
    });

    // Mixin the rest of the methods.
    mixins.forEach(function (mixin) {
        mixin(that, local, Storeit);
    });

}

Storeit.Action = Action;
Storeit.EventName = EventName;
Storeit.StoreitError = StoreitError;

Storeit.createError = function (type) {
    return new StoreitError(StoreitError[type]);
};

Storeit.use = function (mixin) { // Allow third parties to write mixins for StoreIt!
    mixins.push(mixin);
};

module.exports = Storeit;
<commit_msg>Publish a `ready` event once the store is initialized<commit_after>"use strict";

var Q = require("q");
var _ = require("underscore");
var pubit = require("pubit-as-promised");
var whatsDifferent = require("./utils").whatsDifferent;
var previously = require("./utils").previously;
var isObject = require("./utils").isObject;
var cloneObject = require("./utils").cloneObject;
var isEqual = require("./utils").isEqual;
var Action = require("./Action");
var StoreitError = require("./StoreitError");

// Built-in mixins.
var mixins = [
    require("./mixins/map"),
    require("./mixins/enumerables"),
    require("./mixins/metadata")
];

var NS_SEPARATOR_CHAR = "#";

var events = ["added", "modified", "removed", "cleared", "ready"];
var EventName = _.object(events, events);

function throwUninitializedError() {
    throw new StoreitError(StoreitError.loadUninitialized);
}

function throwIfUndefined(value) {
    if (value === undefined) {
        throw new StoreitError(StoreitError.undefinedValue);
    }
}

function fakePublish() {}
fakePublish.when = function () {
    return Q.resolve([]);
};

function Storeit(namespace, storageProvider) {
    var that = this;
    var cache = {};

    if (namespace.indexOf(NS_SEPARATOR_CHAR) !== -1) {
        throw new StoreitError(StoreitError.invalidNamespace);
    }

    var options = { // Defaut options.
        publish: true,
        publishRemoveOnClear: false
    };

    var publish = pubit.makeEmitter(that, events); // Mixin `on`, `off`, `once`.
    var originalPublish = publish;

    var throwIfUninitialized = throwUninitializedError; // Default until `load` is called.

    function isInitialized() {
        return throwIfUninitialized !== throwUninitializedError;
    }

    function initialize() {
        var needsInitialization = !isInitialized();
        if (needsInitialization) {
            throwIfUninitialized = _.noop; // Allow other methods to work without throwing.
        }
        return needsInitialization;
    }

    function addEvent(eventName) {
        if (!Storeit.EventName[eventName]) {
            events.push(eventName);
            Storeit.EventName[eventName] = eventName;
        }
    }

    function typeCheckKey(key) {
        throwIfUninitialized();
        if (typeof key !== "string") {
            throw new StoreitError(StoreitError.invalidKey);
        }
    }

    function nskey(key, modifier) {
        return namespace + (modifier ? NS_SEPARATOR_CHAR + modifier : "") + ":" + key;
    }

    function ikey(key) {
        return nskey(key, "index"); // ex: mynamespace#index:primary
    }

    function mkey(key) {
        return nskey(key, "metadata"); // ex: mynamespace#metadata:123
    }

    function has(key) {
        typeCheckKey(key);
        return key in cache;
    }

    function getValue(key) {
        return cloneObject(cache[key].value);
    }

    function removeKeyFromCache(key) {
        var value = getValue(key);
        delete cache[key];
        return value;
    }

    function removeKey(key) {
        if (has(key)) {
            var removedValue = removeKeyFromCache(key);
            storageProvider.removeItem(nskey(key));
            storageProvider.removeItem(mkey(key));
            publish(EventName.removed, removedValue, key);

            setIndex();
            return {
                action: Action.removed,
                key: key,
                value: removedValue
            };
        } else {
            throw new StoreitError(StoreitError.nonexistentKey);
        }
    }

    function setMetadata(key, value) {
        throwIfUndefined(value);
        cache[key].metadata = value;
        storageProvider.setItem(mkey(key), value);
    }

    function setCache(key, value) {
        var results = {};
        if (has(key)) {
            var previousPartial;
            var partial;
            var currentValue = getValue(key);
            if (isObject(value) && isObject(currentValue)) {
                value = _.extend(cloneObject(currentValue), value); // Allow "patching" with partial value.
                partial = whatsDifferent(currentValue, value);
                previousPartial = previously(currentValue, partial);
            } else {
                partial = value;
                previousPartial = currentValue;
            }
            if (isEqual(currentValue, value)) {
                results.action = Action.none;
            } else {
                cache[key].value = value;
                publish(EventName.modified, partial, key, previousPartial);
                results.previousValue = previousPartial;
                results.action = Action.modified;
            }
        } else {
            cache[key] = {
                value: value,
                metadata: null
            };
            publish(EventName.added, cloneObject(value), key);
            results.action = Action.added;
        }
        results.key = key;
        results.value = value;
        return results;
    }

    function setValue(key, value, metadata) {
        throwIfUndefined(value);
        var results = setCache(key, value, metadata);
        if (results.action === Action.added) {
            setSerializerName();
            setIndex();
        }
        if (results.action !== Action.none) {
            storageProvider.setItem(nskey(key), results.value);
        }
        if (metadata !== undefined) {
            setMetadata(key, metadata);
            results.metadata = metadata;
        }
        return results;
    }

    function getIndex() {
        return storageProvider.getMetadata(ikey("primary")) || [];
    }

    function setIndex() {
        var keys = Object.keys(cache);
        storageProvider.setMetadata(ikey("primary"), keys);
    }

    // Read in the base namespace key.
    function initializeItemSerializer(hasItems) {
        // Is there is a itemSerializer specified, we MUST use it.
        // If existing data and no itemSerializer specified, this is an old JSON database,
        // so "fake" the compatible JSON serializer
        var providerInfo = storageProvider.getMetadata(namespace); // TODO I hate this!
        var itemSerializerName = providerInfo ? providerInfo.itemSerializer :
            hasItems ? "JSONSerializer" : null;
        storageProvider.itemSerializer = itemSerializerName;
    }

    function setSerializerName() {
        var data = {
            itemSerializer: storageProvider.itemSerializer
        };
        storageProvider.setMetadata(namespace, data);
    }

    function publishProxy() {
        publish.apply(null, arguments);
    }

    // Here are a few built-in methods and properties.
    that.clear = function () {
        var publishReady = initialize(); // Initialize only if needed and return true if initalization was performed.
        getIndex().forEach(function (key) { // Remove everything from provider, loaded or not.
            storageProvider.removeItem(nskey(key));
            storageProvider.removeItem(mkey(key));
        });
        // Publish and remove (or simply remove) each value from cache.
        Object.keys(cache).reverse().forEach(options.publishRemoveOnClear ? removeKey : removeKeyFromCache);
        storageProvider.removeItem(ikey("primary"));
        storageProvider.removeItem(namespace); // Remove the storageMetadata for the namespace.
        publish(EventName.cleared);
        if (publishReady) {
            originalPublish(EventName.ready);
        }
    };

    Object.defineProperty(that, "options", {
        get: function () {
            return options;
        },
        set: function (value) {
            _.extend(options, value);
            publish = options.publish ? originalPublish : fakePublish;
            publishProxy.when = publish.when; // So that mixins can use publish.when if they so desire.
        },
        enumerable: true
    });

    Object.defineProperty(that, "isInitialized", {
        get: isInitialized,
        enumerable: true
    });

    Object.defineProperty(that, "namespace", {
        value: namespace,
        enumerable: true
    });

    that.load = function () {
        throwIfUninitialized = _.noop; // Allow other methods to work without throwing.
        var index = getIndex();
        initializeItemSerializer(!!index.length);
        index.forEach(function (key) { // For each key in "namespace#index:primary"...
            var value = storageProvider.getItem(nskey(key));
            setCache(key, value); // Build cache and publish an "added" events.
        });
        originalPublish(EventName.ready); // Publish even if options.publish is false.
    };

    // Expose these internal functions to the mixins.
    var local = {
        has: has,
        getValue: getValue,
        setValue: setValue,
        setMetadata: setMetadata,
        removeKey: removeKey,
        publish: publishProxy,
        addEvent: addEvent
    };

    // Expose these internal properties.
    Object.defineProperty(local, "cache", {
        get: function () {
            throwIfUninitialized();
            return cache;
        },
        enumerable: true
    });

    // Mixin the rest of the methods.
    mixins.forEach(function (mixin) {
        mixin(that, local, Storeit);
    });

}

Storeit.Action = Action;
Storeit.EventName = EventName;
Storeit.StoreitError = StoreitError;

Storeit.createError = function (type) {
    return new StoreitError(StoreitError[type]);
};

Storeit.use = function (mixin) { // Allow third parties to write mixins for StoreIt!
    mixins.push(mixin);
};

module.exports = Storeit;
<|endoftext|>


<commit_before>// @flow

import fbWatchman from 'fb-watchman';
import loglevel from 'loglevel';
import minimatch from 'minimatch';

import ExportsStorage from './ExportsStorage';
import findAllFiles from './findAllFiles';
import normalizePath from './normalizePath';

const SUBSCRIPTION_NAME = 'import-js-subscription';

export default class Watcher {
  workingDirectory: string;
  excludes: Array<string>;
  onFilesAdded: Function;
  onFilesRemoved: Function;
  storage: ExportsStorage;

  constructor({
    workingDirectory = process.cwd(),
    excludes = [],
    onFilesAdded = (): Promise<void> => Promise.resolve(),
    onFilesRemoved = (): Promise<void> => Promise.resolve(),
    storage,
  }: Object) {
    this.workingDirectory = workingDirectory;
    this.excludes = excludes;
    this.onFilesAdded = onFilesAdded;
    this.onFilesRemoved = onFilesRemoved;
    this.storage = storage;
  }

  subscribe({
    client,
    fbWatch,
    relativePath,
  }: {
    client: fbWatchman.Client,
    fbWatch: string,
    relativePath: string,
  }): Promise<void> {
    const subscription = {
      // Match javascript files
      expression: [
        'anyof',
        ['suffix', 'js'],
        ['suffix', 'jsx'],
        ['suffix', 'json'],
      ],
      fields: ['name', 'exists', 'mtime_ms'],
      relative_root: relativePath,
    };

    return new Promise((resolve: Function, reject: Function) => {
      client.command(['subscribe', fbWatch, SUBSCRIPTION_NAME, subscription],
        (error: Error) => {
          if (error) {
            reject(error);
            return;
          }
          resolve();
        });

      client.on('subscription', (resp: Object) => {
        if (resp.subscription !== SUBSCRIPTION_NAME) {
          return;
        }

        const added = [];
        const removed = [];
        resp.files.forEach((file: Object) => {
          const normalizedPath = normalizePath(file.name, this.workingDirectory);
          if (normalizedPath.startsWith('./node_modules/')) {
            return;
          }
          if (this.excludes.some((pattern: string): boolean =>
            minimatch(normalizedPath, pattern))) {
            return;
          }
          if (file.exists) {
            added.push({ path: normalizedPath, mtime: +file.mtime_ms });
          } else {
            removed.push({ path: normalizedPath });
          }
        });
        if (added.length) {
          this.onFilesAdded(added);
        }
        if (removed.length) {
          this.onFilesRemoved(removed);
        }
      });
    });
  }

  startSubscription({ client }: { client: fbWatchman.Client }): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {
      client.command(['watch-project', this.workingDirectory], (error: Error, resp: Object) => {
        if (error) {
          reject(error);
          return;
        }

        if ('warning' in resp) {
          loglevel.warn(`WARNING received during watchman init: ${resp.warning}`);
        }

        this.subscribe({
          client,
          fbWatch: resp.watch,
          relativePath: resp.relative_path,
        }).then(resolve).catch(reject);
      });
    });
  }

  initialize(): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {
      this.initializeWatchman().then(resolve).catch(() => {
        loglevel.info(
          "Couldn't initialize watchman watcher. Falling back to polling.");
        this.initializePolling().then(resolve).catch(reject);
      });
    });
  }

  /**
   * Get all files from the watchman-powered cache. Returns a promise that will
   * resolve if watchman is available, and the file cache is enabled. Will
   * resolve immediately if previously initialized.
   */
  initializeWatchman(): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {
      const client = new fbWatchman.Client();
      client.on('error', (error: Error) => {
        reject(error);
      });
      client.capabilityCheck({
        optional: [],
        required: ['relative_root'],
      }, (error: Error) => {
        if (error) {
          client.end();
          reject(error);
        } else {
          this.startSubscription({ client }).then(resolve).catch(reject);
        }
      });
    });
  }

  initializePolling(): Promise<void> {
    setInterval(() => {
      this.poll();
    }, 30000);
    return this.poll();
  }

  poll(): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {
      findAllFiles(this.workingDirectory, this.excludes)
        .then((files: Array<Object>) => {
          const mtimes = {};
          files.forEach(({ path: pathToFile, mtime }: Object) => {
            mtimes[pathToFile] = mtime;
          });
          this.storage.allFiles().then((storedFiles: Array<string>) => {
            const removedFiles = [];
            storedFiles.forEach((storedFile: string) => {
              const mtime = mtimes[storedFile];
              if (!mtime) {
                removedFiles.push({ path: storedFile });
              }
            });
            this.onFilesAdded(files)
              .then((): Promise<void> => this.onFilesRemoved(removedFiles))
              .then(resolve)
              .catch(reject);
          });
        }).catch(reject);
    });
  }
}
<commit_msg>Ignore watching nested node_modules folders<commit_after>// @flow

import fbWatchman from 'fb-watchman';
import loglevel from 'loglevel';
import minimatch from 'minimatch';

import ExportsStorage from './ExportsStorage';
import findAllFiles from './findAllFiles';
import normalizePath from './normalizePath';

const SUBSCRIPTION_NAME = 'import-js-subscription';

export default class Watcher {
  workingDirectory: string;
  excludes: Array<string>;
  onFilesAdded: Function;
  onFilesRemoved: Function;
  storage: ExportsStorage;

  constructor({
    workingDirectory = process.cwd(),
    excludes = [],
    onFilesAdded = (): Promise<void> => Promise.resolve(),
    onFilesRemoved = (): Promise<void> => Promise.resolve(),
    storage,
  }: Object) {
    this.workingDirectory = workingDirectory;
    this.excludes = excludes;
    this.onFilesAdded = onFilesAdded;
    this.onFilesRemoved = onFilesRemoved;
    this.storage = storage;
  }

  subscribe({
    client,
    fbWatch,
    relativePath,
  }: {
    client: fbWatchman.Client,
    fbWatch: string,
    relativePath: string,
  }): Promise<void> {
    const subscription = {
      // Match javascript files
      expression: [
        'anyof',
        ['suffix', 'js'],
        ['suffix', 'jsx'],
        ['suffix', 'json'],
      ],
      fields: ['name', 'exists', 'mtime_ms'],
      relative_root: relativePath,
    };

    return new Promise((resolve: Function, reject: Function) => {
      client.command(['subscribe', fbWatch, SUBSCRIPTION_NAME, subscription],
        (error: Error) => {
          if (error) {
            reject(error);
            return;
          }
          resolve();
        });

      client.on('subscription', (resp: Object) => {
        if (resp.subscription !== SUBSCRIPTION_NAME) {
          return;
        }

        const added = [];
        const removed = [];
        resp.files.forEach((file: Object) => {
          const normalizedPath = normalizePath(file.name, this.workingDirectory);
          if (normalizedPath.indexOf('/node_modules/') !== -1) {
            return;
          }
          if (this.excludes.some((pattern: string): boolean =>
            minimatch(normalizedPath, pattern))) {
            return;
          }
          if (file.exists) {
            added.push({ path: normalizedPath, mtime: +file.mtime_ms });
          } else {
            removed.push({ path: normalizedPath });
          }
        });
        if (added.length) {
          this.onFilesAdded(added);
        }
        if (removed.length) {
          this.onFilesRemoved(removed);
        }
      });
    });
  }

  startSubscription({ client }: { client: fbWatchman.Client }): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {
      client.command(['watch-project', this.workingDirectory], (error: Error, resp: Object) => {
        if (error) {
          reject(error);
          return;
        }

        if ('warning' in resp) {
          loglevel.warn(`WARNING received during watchman init: ${resp.warning}`);
        }

        this.subscribe({
          client,
          fbWatch: resp.watch,
          relativePath: resp.relative_path,
        }).then(resolve).catch(reject);
      });
    });
  }

  initialize(): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {
      this.initializeWatchman().then(resolve).catch(() => {
        loglevel.info(
          "Couldn't initialize watchman watcher. Falling back to polling.");
        this.initializePolling().then(resolve).catch(reject);
      });
    });
  }

  /**
   * Get all files from the watchman-powered cache. Returns a promise that will
   * resolve if watchman is available, and the file cache is enabled. Will
   * resolve immediately if previously initialized.
   */
  initializeWatchman(): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {
      const client = new fbWatchman.Client();
      client.on('error', (error: Error) => {
        reject(error);
      });
      client.capabilityCheck({
        optional: [],
        required: ['relative_root'],
      }, (error: Error) => {
        if (error) {
          client.end();
          reject(error);
        } else {
          this.startSubscription({ client }).then(resolve).catch(reject);
        }
      });
    });
  }

  initializePolling(): Promise<void> {
    setInterval(() => {
      this.poll();
    }, 30000);
    return this.poll();
  }

  poll(): Promise<void> {
    return new Promise((resolve: Function, reject: Function) => {
      findAllFiles(this.workingDirectory, this.excludes)
        .then((files: Array<Object>) => {
          const mtimes = {};
          files.forEach(({ path: pathToFile, mtime }: Object) => {
            mtimes[pathToFile] = mtime;
          });
          this.storage.allFiles().then((storedFiles: Array<string>) => {
            const removedFiles = [];
            storedFiles.forEach((storedFile: string) => {
              const mtime = mtimes[storedFile];
              if (!mtime) {
                removedFiles.push({ path: storedFile });
              }
            });
            this.onFilesAdded(files)
              .then((): Promise<void> => this.onFilesRemoved(removedFiles))
              .then(resolve)
              .catch(reject);
          });
        }).catch(reject);
    });
  }
}
<|endoftext|>


<commit_before>var Util = require('./Util.js');
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;

function Watcher() {
  var observes = {};

  function add(id,observer){
    del(id);
    observes[id] = observer;
  }

  function del(id) {
    var observer = observes[id];
    if(observer){
      observer.disconnect();
      observes[id] = null;
      delete observes[id];
    }
  }

  this.watch = function (selector, mutationCallback) {
    var observer = new MutationObserver(mutationCallback);
    observer.observe(Util.el(selector), {
      attributes: true,
      childList: true,
      characterData: true,
      subtree: true
    });

    add(selector, observer);
  }

  this.unwatch = function (selector) {
    del(selector);
  }
}
module.exports = Watcher;
<commit_msg>added genObserver<commit_after>var Util = require('./Util.js');
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;

function Watcher() {
  var observes = {};

  function add(id,observer){
    del(id);
    observes[id] = observer;
  }

  function del(id) {
    var observer = observes[id];
    if(observer){
      observer.disconnect();
      observes[id] = null;
      delete observes[id];
    }
  }

  function genObserver(selector, mutationCallback){
    return new MutationObserver(mutationCallback)
      .observe(Util.el(selector), {
        attributes: true,
        childList: true,
        characterData: true,
        subtree: true
      });
  }

  this.watch = function (selector, mutationCallback) {
    add(selector, genObserver(selector, mutationCallback));
  }

  this.unwatch = function (selector) {
    del(selector);
  }
}
module.exports = Watcher;
<|endoftext|>


<commit_before>// @flow

import * as React from 'react'

import {TabNavigator} from '../components/tabbed-view'
import {TabBarIcon} from '../components/tabbar-icon'

import {OtherModesView} from './other-modes'
import {BusView} from './bus'

export {OtherModesDetailView} from './other-modes'
export {BusMap} from './bus'

export default TabNavigator(
	{
		ExpressLineBusView: {
			screen: ({navigation}) => (
				<BusView line="Express Bus" navigation={navigation} />
			),
			navigationOptions: {
				tabBarLabel: 'Express Bus',
				tabBarIcon: TabBarIcon('subway'),
			},
		},

		BlueLineBusView: {
			screen: ({navigation}) => (
				<BusView line="Blue Line" navigation={navigation} />
			),
			navigationOptions: {
				tabBarLabel: 'Blue Line',
				tabBarIcon: TabBarIcon('bus'),
			},
		},

		TransportationOtherModesListView: {
			screen: OtherModesView,
		},
	},
	{
		navigationOptions: {
			title: 'Transportation',
		},
	},
)
<commit_msg>add carls-go 1&2 tabs<commit_after>// @flow

import * as React from 'react'

import {TabNavigator} from '../components/tabbed-view'
import {TabBarIcon} from '../components/tabbar-icon'

import {OtherModesView} from './other-modes'
import {BusView} from './bus'

export {OtherModesDetailView} from './other-modes'
export {BusMap} from './bus'

export default TabNavigator(
	{
		ExpressLineBusView: {
			screen: ({navigation}) => (
				<BusView line="Express Bus" navigation={navigation} />
			),
			navigationOptions: {
				tabBarLabel: 'Express Bus',
				tabBarIcon: TabBarIcon('subway'),
			},
		},

		BlueLineBusView: {
			screen: ({navigation}) => (
				<BusView line="Blue Line" navigation={navigation} />
			),
			navigationOptions: {
				tabBarLabel: 'Blue Line',
				tabBarIcon: TabBarIcon('bus'),
			},
		},

		CarlsGo1BusView: {
			screen: ({navigation}) => (
				<BusView line="Carls-Go! Route 1" navigation={navigation} />
			),
			navigationOptions: {
				tabBarLabel: 'Carls-Go! R1',
				tabBarIcon: TabBarIcon('car'),
			},
		},

		CarlsGo2BusView: {
			screen: ({navigation}) => (
				<BusView line="Carls-Go! Route 2" navigation={navigation} />
			),
			navigationOptions: {
				tabBarLabel: 'Carls-Go! R2',
				tabBarIcon: TabBarIcon('car'),
			},
		},

		TransportationOtherModesListView: {
			screen: OtherModesView,
		},
	},
	{
		navigationOptions: {
			title: 'Transportation',
		},
	},
)
<|endoftext|>


<commit_before>define( [
    'osg/Notify',
    'osgShader/ShaderLib'
], function ( Notify, shaderLib ) {
    'use strict';

    var shaderPrefix = shaderLib.prefix;

    //     Manage External Load Shader
    //     Or storage as json inside code
    //     (like concatened from shader file
    //     to json using a build tool like grunt)
    //     TODO: tests load/reload shaders for realtime editing.
    //
    //     Idea is to be able to edit shader in separate files than js, getting readable code, and avoid string/shader duplication in code, resulting in min code.
    //     Handle loading shader files using ajax,json,jsonp or  even inline, with a grunt dir2json task that will generate according files.
    //     Handle (recursive) include, avoiding code repeat and help code factorization
    //     Handle per shader and global define (upon extension supported, hw capabilites ("highp precision") or shader usage ("LAMBERT or BLINN_PHONG").)
    //     Possible afterward Todo list:
    //     use glsl optimizer on shaders
    //     use glsl minimizer on shaders.
    /**
     * @class ShaderLoader
     */

    var getParametersURL = function () {
        var vars = [], hash;
        if ( typeof window !== 'undefined' ){
            var hashes = window.location.href.slice( window.location.href.indexOf( '?' ) + 1 ).split( '&' );
            for( var i = 0; i < hashes.length; i++ )
            {
                hash = hashes[i].split( '=' );
                var element = hash[0].toLowerCase();
                vars.push(element);
                var result = hash[1];
                if ( result === undefined ) {
                    result = '1';
                }
                var val = parseFloat( result );
                if ( !isNaN( val ) )
                    vars[ element ] = val;
                else
                    vars[ element ] = result.toLowerCase();
            }
        }
        return vars;
    };

    var ShaderLoader = function ( opt ) {
        var options = opt;
        if ( !options ) {
            options = {
                inline: getParametersURL()[ 'debug' ] ? false : true,
                callbacksingle: function ( file ) {
                    window.dbg.viewer.log( file + 'is loaded' );
                },
                libs: [ {
                    loadprefix:  shaderPrefix,
                    shaders: shaderLib
                } ]
            };
        }
        this.init( options );
    };

    /** @lends osg.ShaderLoader.prototype */
    ShaderLoader.prototype = {
        _shadersText: {},
        _shadersList: {},
        _shaderLoaded: {},
        _loaded: false,
        _callbackSingle: false,
        _numtoLoad: 0,
        _globalDefaultDefines: '',
        _globalDefaultprecision: '#ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp float;\n #else\n precision mediump float;\n#endif',
        _debugLines: false,
        _includeR: /#pragma include "([^"]+)"/g,
        _defineR: /#define\s+([a-zA-Z_0-9]+)\s+(.*)/,
        _precisionR: /precision\s+(high|low|medium)p\s+float/,

        initShaderLib: function ( lib, inline ) {

            if ( !lib.loadprefix ) lib.loadprefix = '';
            var i;
            for ( i in lib.shaders ) {
                if ( lib.shaders.hasOwnProperty( i ) ) {
                    this._numtoLoad++;
                }
            }
            if ( !inline ) {
                for ( i in lib.shaders ) {
                    if ( lib.shaders.hasOwnProperty( i ) ) {
                        this._shadersList[ i ] = lib.loadprefix + i;
                    }
                }
                this._loaded = false;

            } else {
                for ( i in lib.shaders ) {
                    if ( lib.shaders.hasOwnProperty( i ) ) {
                        this._shadersList[ i ] = i;
                        this._shadersText[ i ] = lib.shaders[ i ];
                        if ( this._callbackSingle ) this._callbackSingle( i );
                        this._numtoLoad--;
                    }
                }
                this._loaded = true;

                Notify.assert( this._numtoLoad === 0 );
            }

        },

        init: function(options) {

            this._callbackSingle = options.callbackSingle;
            this._numtoLoad = 0;

            if ( options.libs ) {
                options.libs.forEach( function( lib ) {
                    this.initShaderLib( lib, options.inline );
                }, this );
            }

            return this;
        },
        load: function( shaderFilename, shaderName, callbackSingle ) {

            if ( !this._shadersList[ shaderName ] )
                this._shadersList[ shaderName ] = shaderFilename;

            // require the shader
            require( [ 'text!'+shaderFilename ], function ( content ) {

                this._shadersText[ shaderName ] = content;
                if ( this._callbackSingle ) this._callbackSingle();
                this._numtoLoad--;

            }.bind( this ) );

            if ( callbackSingle )
                this._callbackSingle = callbackSingle;

            return this;
        },

        loadAll: function ( options ) {
            if ( this._numtoLoad > 0 ) {

                Object.keys( this._shadersList ).forEach( function( shader ) {
                    var shaderPath = this._shadersList[ shader ];
                    this.load( shaderPath, shader, options && options.callbackSingle );
                }, this );

            }
            return this;
        },

        reloadAll: function ( options ) {
            this._shaderLoaded = {};
            this._loaded = false;
            this._numtoLoad = 0;

            Object.keys( this._shadersList ).forEach( function( shader ) {
                // unload from require
                require.undef( 'vendors/require/text!'+ shader );
                this._numtoLoad++;

            }, this );

            this.loadAll( options );
            return this;
        },

        reload: function ( options ) {
            this._shaderLoaded[ options.shaderName ] = undefined;
            this._loaded = false;
            this._numtoLoad = 1;

            // unload from require
            var shaderPath = this._shadersList[ options.shaderName ];
            require.undef( 'vendors/require/text!' + shaderPath );

            this.load( this._shaders[ options.shaderName ], options && options.shaderName, options && options.callbackSingle );
            return this;
        },

        instrumentShaderlines: function ( content, sourceID ) {
            // TODO instrumentShaderlines
            // http://immersedcode.org/2012/1/12/random-notes-on-webgl/
            // one ID per "file"
            // Each file has its line number starting at 0
            //   handle include, the do that numbering also in preprocess...
            // Then on shader error using sourceID and line you can point the correct line...
            // has to attach that info to osg.shader object.
            /*
              var allLines = content.split('\n');
              var i = 0;
              for (var k = 0; k < allLines.length; k++) {
              if (!this._includeR.test(allLines[k])) {
              allLines[k] = "#line " + (i++) + " " + sourceID + '\n' + allLines[k] ;
              }
              }
              content = allLines.join('\n');
            */

            // seems just  prefixing first line seems ok to help renumbering error mesg
            return '\n#line ' + 0 + ' ' + sourceID + '\n' + content;
        },

        getShaderTextPure: function ( shaderName ) {
            var preShader;

            if ( !( shaderName in this._shadersText ) ) {
                // directory include/prefix problems.
                for ( var name in this._shadersText ) {
                    if ( name.indexOf( shaderName ) !== -1 ) {
                        preShader = this._shadersText[ name ];
                        break;
                    }
                }
                if ( !preShader ) {
                    window.dbg.viewer.error( 'shader file/text: ' + shaderName + ' not loaded' );
                    return '';
                }
            }
            else {
                preShader = this._shadersText[ shaderName ];
            }
            return preShader;
        },

        getShader: function( shaderName ) {
            var shader = this.getShaderTextPure( shaderName );
            return this.processShader( shader );
        },

        // recursively  handle #include external glsl
        // files (for now in the same folder.)
        preprocess: function ( content, sourceID, includeList ) {
            return content.replace( this._includeR, function ( _, name ) {
                // \#pragma include 'name';
                // already included
                if ( includeList.indexOf( name ) !== -1 ) return '';
                // avoid endless loop, not calling the impure
                var txt = this.getShaderTextPure( name );
                // make sure it's not included twice
                includeList.push( name );
                if ( this._debugLines ) {
                    txt = this.instrumentShaderlines( txt, sourceID );
                }
                sourceID++;
                // to the infinite and beyond !
                txt = this.preprocess( txt, sourceID, includeList );
                return txt;
            }.bind( this ) );
        },

        //  process a shader and define
        //  get a full expanded single shader source code
        //  resolving include dependencies
        //  adding defines
        //  adding line instrumenting.
        processShader: function ( shader, defines ) {
            var includeList = [];
            var preShader = shader;
            var sourceID = 0;
            if ( this._debugLines ) {
                preShader = this.instrumentShaderlines( preShader, sourceID );
                sourceID++;
            }
            var postShader = this.preprocess( preShader, sourceID, includeList );

            var prePrend = '';
            if ( this._globalDefaultprecision ) {
                if ( !this._precisionR.test( postShader ) ) {
                    // use the shaderhighprecision flag at shaderloader start
                    //var highp = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
                    //var highpSupported = highp.precision != 0;
                    prePrend += this._globalDefaultprecision + '\n';
                }
            }

            if ( !defines ) defines = [];
            defines.push( this._globalDefaultDefines );

            prePrend += defines.join( '\n' ) + '\n';
            postShader = prePrend + postShader;
            return postShader;
        }
    };
    return ShaderLoader;

});
<commit_msg>Cleans shaderprocessor<commit_after>define( [
    'osg/Notify',
    'osgShader/ShaderLib'
], function ( Notify, shaderLib ) {
    'use strict';

    var shaderPrefix = shaderLib.prefix;

    //     Shader as vert/frag/glsl files Using requirejs text plugin
    //     Preprocess features like:    //
    //     - Handle (recursive) include, avoiding code repeat and help code factorization
    //     - Handle per shader and global define/precision
    /**
     * @class ShaderLoader
     */

    var ShaderLoader = function ( opt ) {
        var options = opt;
        if ( !options ) {
            options = {
                callbacksingle: function ( file ) {
                    window.dbg.viewer.log( file + 'is loaded' );
                },
                libs: [ {
                    loadprefix: shaderPrefix,
                    shaders: shaderLib
                } ]
            };
        }
        this.init( options );
    };

    /** @lends osg.ShaderLoader.prototype */
    ShaderLoader.prototype = {
        _shadersText: {},
        _shadersList: {},
        _shaderLoaded: {},
        _loaded: false,
        _callbackSingle: false,
        _numtoLoad: 0,
        _globalDefaultDefines: '',
        _globalDefaultprecision: '#ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp float;\n #else\n precision mediump float;\n#endif',
        _debugLines: false,
        _includeR: /#pragma include "([^"]+)"/g,
        _defineR: /#define\s+([a-zA-Z_0-9]+)\s+(.*)/,
        _precisionR: /precision\s+(high|low|medium)p\s+float/,

        initShaderLib: function ( lib ) {

            if ( !lib.loadprefix ) lib.loadprefix = '';
            var i;
            for ( i in lib.shaders ) {
                if ( lib.shaders.hasOwnProperty( i ) ) {
                    this._numtoLoad++;
                }
            }
            for ( i in lib.shaders ) {
                if ( lib.shaders.hasOwnProperty( i ) ) {
                    this._shadersList[ i ] = i;
                    this._shadersText[ i ] = lib.shaders[ i ];
                    if ( this._callbackSingle ) this._callbackSingle( i );
                    this._numtoLoad--;
                }
            }
            this._loaded = true;

            Notify.assert( this._numtoLoad === 0 );

        },

        init: function ( options ) {

            this._callbackSingle = options.callbackSingle;
            this._numtoLoad = 0;

            if ( options.libs ) {
                options.libs.forEach( function ( lib ) {
                    this.initShaderLib( lib );
                }, this );
            }

            return this;
        },

        instrumentShaderlines: function ( content, sourceID ) {
            // TODO instrumentShaderlines
            // http://immersedcode.org/2012/1/12/random-notes-on-webgl/
            // one ID per "file"
            // Each file has its line number starting at 0
            //   handle include, the do that numbering also in preprocess...
            // Then on shader error using sourceID and line you can point the correct line...
            // has to attach that info to osg.shader object.
            /*
              var allLines = content.split('\n');
              var i = 0;
              for (var k = 0; k < allLines.length; k++) {
              if (!this._includeR.test(allLines[k])) {
              allLines[k] = "#line " + (i++) + " " + sourceID + '\n' + allLines[k] ;
              }
              }
              content = allLines.join('\n');
            */

            // seems just  prefixing first line seems ok to help renumbering error mesg
            return '\n#line ' + 0 + ' ' + sourceID + '\n' + content;
        },

        getShaderTextPure: function ( shaderName ) {
            var preShader;

            if ( !( shaderName in this._shadersText ) ) {
                // directory include/prefix problems.
                for ( var name in this._shadersText ) {
                    if ( name.indexOf( shaderName ) !== -1 ) {
                        preShader = this._shadersText[ name ];
                        break;
                    }
                }
                if ( !preShader ) {
                    window.dbg.viewer.error( 'shader file/text: ' + shaderName + ' not loaded' );
                    return '';
                }
            } else {
                preShader = this._shadersText[ shaderName ];
            }
            return preShader;
        },

        getShader: function ( shaderName ) {
            var shader = this.getShaderTextPure( shaderName );
            return this.processShader( shader );
        },

        // recursively  handle #include external glsl
        // files (for now in the same folder.)
        preprocess: function ( content, sourceID, includeList ) {
            return content.replace( this._includeR, function ( _, name ) {
                // \#pragma include 'name';
                // already included
                if ( includeList.indexOf( name ) !== -1 ) return '';
                // avoid endless loop, not calling the impure
                var txt = this.getShaderTextPure( name );
                // make sure it's not included twice
                includeList.push( name );
                if ( this._debugLines ) {
                    txt = this.instrumentShaderlines( txt, sourceID );
                }
                sourceID++;
                // to the infinite and beyond !
                txt = this.preprocess( txt, sourceID, includeList );
                return txt;
            }.bind( this ) );
        },

        //  process a shader and define
        //  get a full expanded single shader source code
        //  resolving include dependencies
        //  adding defines
        //  adding line instrumenting.
        processShader: function ( shader, defines ) {
            var includeList = [];
            var preShader = shader;
            var sourceID = 0;
            if ( this._debugLines ) {
                preShader = this.instrumentShaderlines( preShader, sourceID );
                sourceID++;
            }
            var postShader = this.preprocess( preShader, sourceID, includeList );

            var prePrend = '';
            if ( this._globalDefaultprecision ) {
                if ( !this._precisionR.test( postShader ) ) {
                    // use the shaderhighprecision flag at shaderloader start
                    //var highp = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
                    //var highpSupported = highp.precision != 0;
                    prePrend += this._globalDefaultprecision + '\n';
                }
            }

            if ( !defines ) defines = [];
            defines.push( this._globalDefaultDefines );

            prePrend += defines.join( '\n' ) + '\n';
            postShader = prePrend + postShader;
            return postShader;
        }
    };
    return ShaderLoader;

} );
<|endoftext|>


<commit_before>var mbaasApi = require('fh-mbaas-api');
var q = require('q');
var http = require('https');

var Account = function () {
    this.login = oAuthPostLogin;

    /**
     * This function consumes the sessionToken, and the authResponse
     *
     * @param {String} sessionToken session token returned by the OAuth mbaas
     * @param {Object} authResponse authResponse returned by Google
     * @param {Callback} callback a node callback
     * @returns nothing, but returns the session on the callback
     */
    function oAuthPostLogin(req, res) {
        console.log(req.body);
        var sessionToken = req.body.sessionToken;
        var authResponse = req.body.authResponse;
        var callback = function(error, data) {
            if (error){
                res.statusCode = 500;
                console.log('login error', error);
                return res.end(error);
            } else {
                res.json(data);
            }

        }
        mbaasApi.session.get(sessionToken, function (err, session) {
            if (!session) {
                initSession(sessionToken, authResponse, callback);
            } else {
                callback(null, session);
            }
        });

    }
    /**
     * Validate the user's session info with Google, and create a session object.
     * This will also create a account object if necessary.
     *
     * @param {String} sessionToken session token returned by the OAuth mbaas
     * @param {Object} authResponse authResponse returned by Google
     * @param {Callback} nodeCallback a node callback
     */
    function initSession(sessionToken, authResponse, nodeCallback) {
        //validate with google
        var request = {
            protocol: 'https',
            port: 443,
            hostname: 'www.googleapis.com',
            path: '/oauth2/v2/userinfo',
            method: 'GET',
            headers: {'Authorization': 'Bearer ' + authResponse.authToken}
        };

        http.request(request, handleGoogleResponse(sessionToken, authResponse, nodeCallback)).end();

    }

    /**
     *
     * This callback consumes Google's response, and performs the appropriate
     * actions on the session. (Set session optionally creating the account).
     *
     * @param {type} sessionToken
     * @param {type} authResponse
     * @param {type} nodeCallback
     * @returns session Data on callback if successful, error on callback otherwise
     */
    function handleGoogleResponse(sessionToken, authResponse, nodeCallback) {
        return function (response) {
            var body = '';

            //another chunk of data has been recieved, so append it to `str`
            response.on('data', function (chunk) {
                body += chunk;
            });

            //the whole response has been recieved, so we just print it out here
            response.on('end', function () {
                var resObject = JSON.parse(body);
                if (authResponse.id === resObject.id) {
                    //load from storage
                    checkAccountAndCreateSession(resObject.id, sessionToken, authResponse, nodeCallback);
                } else {
                    nodeCallback('Id for client does not match id for token.');
                }
            });

            response.on('error', function (e) {
                nodeCallback(e);
            });

        };
    }

    /**
     * Loads a users data onto the session or creates a user and loads
     * authResponse onto the session
     *
     * @param {Sting}  googleUserId
     * @param {Sting} sessionToken
     * @param {Object} authResponse
     * @param {NodeCallback} nodeCallback
     * @returns session Data on callback if successful, error on callback otherwise
     */
    function checkAccountAndCreateSession(googleUserId, sessionToken, authResponse, nodeCallback) {

        var handleResults = function (error, responseData) {
            if (error) {
                return nodeCallback(error, null);
            } else {
                if (!responseData || responseData.count === 0) {
                    //no account, must create.
                    createAccountAndSetSession(sessionToken, authResponse, nodeCallback);
                } else {
                    //account exists, set session and return it.
                    setSession(sessionToken, responseData.list[0].fields, nodeCallback);
                }
            }
        };

        mbaasApi.db({
            "act": "list",
            "type": 'account',
            "eq": {"id": googleUserId},
        }, handleResults);
    }

    /**
     *  Creates an account and sets the session to that account.
     * @param {Sting} sessionToken
     * @param {Object} authResponse
     * @param {NodeCallback} nodeCallback
     * @returns session Data on callback if successful, error on callback otherwise
     */
    function createAccountAndSetSession(sessionToken, authResponse, nodeCallback) {
        mbaasApi.db({
            "act": "create",
            "type": 'account',
            "fields": [authResponse]
        }, function (error, data) {
            if (error) {
                return nodeCallback(error);
            } else {
                setSession(sessionToken, authResponse, nodeCallback);

            }
        });
    }

    /**
     *
     * Sets the session.
     *
     * @param {Sting} sessionToken
     * @param {Object} sessionData
     * @param {NodeCallback} nodeCallback
     * @returns session Data on callback if successful, error on callback otherwise
     */
    function setSession(sessionToken, sessionData, nodeCallback) {
        mbaasApi.session.set(sessionToken, sessionData, function (error, ignore) {
            if (error)
                return nodeCallback(error);
            else
                return nodeCallback(null, sessionData);
        });
    }

};

module.exports = new Account();
<commit_msg>update file /lib/account.js<commit_after>var mbaasApi = require('fh-mbaas-api');
var q = require('q');
var request = require('https');

var Account = function () {
    this.login = oAuthPostLogin;

    /**
     * This function consumes the sessionToken, and the authResponse
     *
     * @param {String} sessionToken session token returned by the OAuth mbaas
     * @param {Object} authResponse authResponse returned by Google
     * @param {Callback} callback a node callback
     * @returns nothing, but returns the session on the callback
     */
    function oAuthPostLogin(req, res) {
        console.log(req.body);
        var sessionToken = req.body.sessionToken;
        var authResponse = req.body.authResponse;
        var callback = function(error, data) {
            if (error){
                res.statusCode = 500;
                console.log('login error', error);
                return res.end(error);
            } else {
                res.json(data);
            }

        }
        mbaasApi.session.get(sessionToken, function (err, session) {
            if (!session) {
                initSession(sessionToken, authResponse, callback);
            } else {
                callback(null, session);
            }
        });

    }
    /**
     * Validate the user's session info with Google, and create a session object.
     * This will also create a account object if necessary.
     *
     * @param {String} sessionToken session token returned by the OAuth mbaas
     * @param {Object} authResponse authResponse returned by Google
     * @param {Callback} nodeCallback a node callback
     */
    function initSession(sessionToken, authResponse, nodeCallback) {
        //validate with google
        var request = {
            protocol: 'https',
            port: 443,
            hostname: 'www.googleapis.com',
            path: '/oauth2/v2/userinfo',
            method: 'GET',
            headers: {'Authorization': 'Bearer ' + authResponse.authToken}
        };

        http.request(request, handleGoogleResponse(sessionToken, authResponse, nodeCallback)).end();

    }

    /**
     *
     * This callback consumes Google's response, and performs the appropriate
     * actions on the session. (Set session optionally creating the account).
     *
     * @param {type} sessionToken
     * @param {type} authResponse
     * @param {type} nodeCallback
     * @returns session Data on callback if successful, error on callback otherwise
     */
    function handleGoogleResponse(sessionToken, authResponse, nodeCallback) {
        return function (response) {
            var body = '';

            //another chunk of data has been recieved, so append it to `str`
            response.on('data', function (chunk) {
                body += chunk;
            });

            //the whole response has been recieved, so we just print it out here
            response.on('end', function () {
                var resObject = JSON.parse(body);
                if (authResponse.id === resObject.id) {
                    //load from storage
                    checkAccountAndCreateSession(resObject.id, sessionToken, authResponse, nodeCallback);
                } else {
                    nodeCallback('Id for client does not match id for token.');
                }
            });

            response.on('error', function (e) {
                nodeCallback(e);
            });

        };
    }

    /**
     * Loads a users data onto the session or creates a user and loads
     * authResponse onto the session
     *
     * @param {Sting}  googleUserId
     * @param {Sting} sessionToken
     * @param {Object} authResponse
     * @param {NodeCallback} nodeCallback
     * @returns session Data on callback if successful, error on callback otherwise
     */
    function checkAccountAndCreateSession(googleUserId, sessionToken, authResponse, nodeCallback) {

        var handleResults = function (error, responseData) {
            if (error) {
                return nodeCallback(error, null);
            } else {
                if (!responseData || responseData.count === 0) {
                    //no account, must create.
                    createAccountAndSetSession(sessionToken, authResponse, nodeCallback);
                } else {
                    //account exists, set session and return it.
                    setSession(sessionToken, responseData.list[0].fields, nodeCallback);
                }
            }
        };

        mbaasApi.db({
            "act": "list",
            "type": 'account',
            "eq": {"id": googleUserId},
        }, handleResults);
    }

    /**
     *  Creates an account and sets the session to that account.
     * @param {Sting} sessionToken
     * @param {Object} authResponse
     * @param {NodeCallback} nodeCallback
     * @returns session Data on callback if successful, error on callback otherwise
     */
    function createAccountAndSetSession(sessionToken, authResponse, nodeCallback) {
        mbaasApi.db({
            "act": "create",
            "type": 'account',
            "fields": [authResponse]
        }, function (error, data) {
            if (error) {
                return nodeCallback(error);
            } else {
                setSession(sessionToken, authResponse, nodeCallback);

            }
        });
    }

    /**
     *
     * Sets the session.
     *
     * @param {Sting} sessionToken
     * @param {Object} sessionData
     * @param {NodeCallback} nodeCallback
     * @returns session Data on callback if successful, error on callback otherwise
     */
    function setSession(sessionToken, sessionData, nodeCallback) {
        mbaasApi.session.set(sessionToken, sessionData, function (error, ignore) {
            if (error)
                return nodeCallback(error);
            else
                return nodeCallback(null, sessionData);
        });
    }

};

module.exports = new Account();
<|endoftext|>


<commit_before>var Controller = function() {
    this._controls = []; // private
};

Controller.prototype.addControl = function(name, control) {
    if (control instanceof ControllerControl) {
        control.setController(this);
        this._controls[name] = control;
    }
};

Controller.prototype.bindControl = function(target) {
    var name = target.data('control');
    if (this._controls[name] instanceof ControllerControl) {
        this._controls[name].bind(target);
    }
};

Controller.prototype.bind = function(target) {
    target = $(target || 'body');

    if (target.data('control')) {
        this.bindControl(target);
    } else {
        var that = this;
        target.find('[data-control]').each(function () {
            that.bindControl($(this));
        });
    }
};
<commit_msg>return self<commit_after>var Controller = function() {
    this._controls = []; // private
};

Controller.prototype.addControl = function(name, control) {
    if (control instanceof ControllerControl) {
        control.setController(this);
        this._controls[name] = control;
    }
    return this;
};

Controller.prototype.bindControl = function(target) {
    var name = target.data('control');
    if (this._controls[name] instanceof ControllerControl) {
        this._controls[name].bind(target);
    }
    return this;
};

Controller.prototype.bind = function(target) {
    target = $(target || 'body');

    if (target.data('control')) {
        this.bindControl(target);
    } else {
        var that = this;
        target.find('[data-control]').each(function () {
            that.bindControl($(this));
        });
    }
    return this;
};
<|endoftext|>