<commit_before>/*global clearTimeout, setTimeout*/

;(function(exports) {
"use strict";

exports.fun = {

  // -=-=-=-=-=-=-=-=-
  // static functions
  // -=-=-=-=-=-=-=-=-
  get Empty() { return function() {}; },
  get K() { return function(arg) { return arg; }; },
  get Null() { return function() { return null; }; },
  get False() { return function() { return false; }; },
  get True() { return function() { return true; }; },

  // -=-=-=-=-=-
  // accessing
  // -=-=-=-=-=-
  all: function(object) {
    var a = [];
    for (var name in object) {
      if (!object.__lookupGetter__(name)
       && typeof object[name] === 'function') a.push(name);
    }
    return a;
  },

  own: function(object) {
    var a = [];
    for (var name in object) {
      if (!object.__lookupGetter__(name)
       && object.hasOwnProperty(name)
       && typeof object[name] === 'function') a.push(name);
    }
    return a;
  },

  // -=-=-=-
  // timing
  // -=-=-=-
  timeToRun: function(func) {
    var startTime = Date.now();
    func();
    return Date.now() - startTime;
  },

  timeToRunN: function(func, n, arg0, arg1, arg2) {
    var startTime = Date.now();
    for (var i = 0; i < n; i++) func(arg0, arg1, arg2);
    return (Date.now() - startTime) / n;
  },

  // these last two methods are Underscore.js 1.3.3 and are slightly adapted
  // Underscore.js license:
  // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
  // Underscore is distributed under the MIT license.

  throttle: function(func, wait) {
    // exec func at most once every wait ms even when called more often
    // useful to calm down eagerly running updaters and such
    /* Example:
        var i = 0;
        x = exports.fun.throttle(function() { show(++i + '-' + Date.now()) }, 500);
        Array.range(0,100).forEach(function(n) { x() });
    */
    var context, args, timeout, throttling, more, result,
        whenDone = exports.fun.debounce(wait, function() { more = throttling = false; });
    return function() {
      context = this; args = arguments;
      var later = function() {
        timeout = null;
        if (more) func.apply(context, args);
        whenDone();
      };
      if (!timeout) timeout = setTimeout(later, wait);
      if (throttling) {
        more = true;
      } else {
        result = func.apply(context, args);
      }
      whenDone();
      throttling = true;
      return result;
    };
  },

  debounce: function(wait, func, immediate) {
    // Execute func after wait milliseconds elapsed since invocation.
    // E.g. to exec something after receiving an input stream
    // with immediate truthy exec immediately but when called before
    // wait ms are done nothing happens. E.g. to not exec a user invoked
    // action twice accidentally
    var timeout;
    return function() {
      var context = this, args = arguments;
      var later = function() {
        timeout = null;
        if (!immediate) func.apply(context, args);
      };
      if (immediate && !timeout) func.apply(context, args);
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  },

  throttleNamed: function(name, wait, func) {
    // see comment in debounceNamed
    var store = exports.fun._throttledByName || (exports.fun._throttledByName = {});
    if (store[name]) return store[name];
    function throttleNamedWrapper() {
      // cleaning up
      exports.fun.debounceNamed(name, wait, function() { delete store[name]; })();
      func.apply(this, arguments);
    }
    return store[name] = exports.fun.throttle(throttleNamedWrapper, wait);
  },

  debounceNamed: function(name, wait, func, immediate) {
    // debounce is based on the identity of the function called. When you call the
    // identical method using debounce, multiple calls that happen between the first
    // invocation and wait time will only cause execution once. However, wrapping a
    // function with debounce and then storing (to be able to call the exact same
    // function again) it is a repeating task and unpractical when using anonymous
    // methods. debounceNamed() automatically maps function to ids and removes the
    // need for this housekeeping code.
    var store = exports.fun._debouncedByName || (exports.fun._debouncedByName = {});
    if (store[name]) return store[name];
    function debounceNamedWrapper() {
      // cleaning up
      delete store[name];
      func.apply(this, arguments);
    }
    return store[name] = exports.fun.debounce(wait, debounceNamedWrapper, immediate);
  },

  createQueue: function(id, workerFunc) {
    var store = exports.fun._queues || (exports.fun._queues = {});

    var queue = store[id] || (store[id] = {
        _workerActive: false,
        worker: workerFunc, tasks: [],
        drain: null, // can be overwritten by a function
        push: function(task) {
          queue.tasks.push(task);
          queue.activateWorker();
        },
        pushAll: function(tasks) {
          tasks.forEach(function(ea) { queue.tasks.push(ea); });
          queue.activateWorker();
        },
        pushNoActivate: function(task) {
          queue.tasks.push(task);
        },
        handleError: function(err) {
          // can be overwritten
          err && console.error('Error in queue: ' + err);
        },
        activateWorker: function() {
          function callback(err) { queue.handleError(err); queue.activateWorker(); }
          var tasks = queue.tasks, active = queue._workerActive;
          if (tasks.length === 0) {
            if (active) {
              queue._workerActive = false;
              if (typeof queue.drain === 'function') queue.drain();
            }
            delete store[id];
          } else {
            if (!active) queue._workerActive = true;
            try {
              queue.worker(tasks.shift(), callback);
            } catch(err) { callback(err); }
          }
        }
    });

    return queue;
  },

  workerWithCallbackQueue: function(id, workerFunc, optTimeout) {
    // This functions helps when you have a long running computation that
    // multiple call sites (independent from each other) depend on. This
    // function does the houskeeping to start the long running computation
    // just once and returns an object that allows to schedule callbacks
    // once the workerFunc is done
    // this is how it works:
    // if id does not exist, workerFunc is called, otherwise ignored.
    // workerFunc is expected to call thenDoFunc with arguments: error, arg1, ..., argN
    // if called subsequently before workerFunc is done, the other thenDoFunc
    // will "pile up" and called with the same arguments as the first
    // thenDoFunc once workerFunc is done

    var store = exports.fun._queueUntilCallbacks || (exports.fun._queueUntilCallbacks = {}),
        queueCallbacks = store[id],
        isRunning = !!queueCallbacks;

    if (isRunning) return queueCallbacks;

    var callbacksRun = false, canceled = false;

    function cleanup() {
      if (timeoutProc) clearTimeout(timeoutProc);
      callbacksRun = true;
      delete store[id];
    }

    function runCallbacks(args) {
      if (callbacksRun) return;
      cleanup();
      queueCallbacks.callbacks.forEach(function(cb) {
        try { cb.apply(null, args); } catch (e) {
          console.error(
              "Error when invoking callbacks in queueUntil ["
            + id + "]:\n"
            + (String(e.stack || e)));
        }
      });
    }

    // timeout
    if (optTimeout) {
      var timeoutProc = setTimeout(function() {
        if (callbacksRun) return;
        runCallbacks([new Error("timeout")]);
      }, optTimeout);
    }

    // init the store
    queueCallbacks = store[id] = {
      callbacks: [],
      cancel: function() {
        canceled = true;
        cleanup();
      },
      whenDone: function(cb) {
        queueCallbacks.callbacks.push(cb);
        return queueCallbacks;
      }
    };

    // call worker, but delay so we can immediately return
    setTimeout(function() {
      if (canceled) return;
      try {
        workerFunc(function(/*args*/) { runCallbacks(arguments); });
      } catch (e) { runCallbacks([e]); }
    }, 0);

    return queueCallbacks;
  },

  composeAsync: function(/*functions*/) {
    // composes functions: exports.fun.composeAsync(f,g,h)(arg1, arg2) =
    //   f(arg1, arg2, thenDo1) -> thenDo1(err, fResult)
    // -> g(fResult, thenDo2) -> thenDo2(err, gResult) ->
    // -> h(fResult, thenDo3) -> thenDo2(err, hResult)
    // Example:
    // exports.fun.composeAsync(
    //   function(a,b, thenDo) { thenDo(null, a+b); },
    //  function(x, thenDo) { thenDo(x*4); })(3,2, function(err, result) { alert(result); });

    var toArray = Array.prototype.slice,
        functions = toArray.call(arguments),
        endCallback, intermediateResult;

    return functions.reverse().reduce(function(prevFunc, func) {
      var nextActivated = false;
      return function() {
        var args = toArray.call(arguments);
        if (!endCallback) endCallback = args.length === 0 ? function() {} : args.pop();

        function next(/*err and args*/) {
          nextActivated = true;
          var args = toArray.call(arguments),
              err = args.shift();
          if (err) endCallback && endCallback(err);
          else prevFunc.apply(null, args);
        }

        try {
          func.apply(this, args.concat([next]));
        } catch (e) {
          console.error('composeAsync: ', e.stack || e);
          !nextActivated && endCallback && endCallback(e);
        }
      }
    }, function() {
      endCallback.apply(
        null,
        [null].concat(toArray.call(arguments)));
    });
  },

  compose: function(/*functions*/) {
    // composes functions: exports.fun.compose(f,g,h)(arg1, arg2) = h(g(f(arg1, arg2)))
    // Example:
    //   exports.fun.compose(function(a,b) {return a+b}, function(x) {return x*4})(3,2)

    var functions = Array.prototype.slice.call(arguments);
    return functions.reverse().reduce(
      function(prevFunc, func) {
        return function() {
          return prevFunc(func.apply(this, arguments));
        }
      }, function(x) { return x; });
  },

  flip: function(f) {
    // swaps the first two args
    // exports.fun.flip(function(a, b, c) { return a + b + c; })(' World', 'Hello', '!')
    return function flipped(/*args*/) {
      var args = Array.prototype.slice.call(arguments),
        flippedArgs = [args[1], args[0]].concat(args.slice(2));
      return f.apply(null, flippedArgs);
    }
  },

  waitFor: function(timeoutMs, waitTesterFunc, thenDo) {
    // wait for waitTesterFunc to return true, then run thenDo, passing
    // failure/timout err as first parameter. A timout occurs after
    // timeoutMs. During the wait period waitTesterFunc might be called
    // multiple times
    var start = Date.now();
    (function test() {
      if (waitTesterFunc()) return thenDo();
      var duration = Date.now() - start,
          timeLeft = timeoutMs - duration;
      if (timeLeft <= 0) return thenDo(new Error('timeout'));
      var timeStep = timeLeft < 50 ? timeLeft : 50;
      setTimeout(test, timeStep);
    })();
  },

  // -=-=-=-=-
  // wrapping
  // -=-=-=-=-

  curry: function(/*func and curry args*/) {
    if (arguments.length <= 1) return arguments[0];
    var args = Array.prototype.slice.call(arguments),
        func = args.shift();
    function wrappedFunc() {
      return func.apply(func, args.concat(Array.prototype.slice.call(arguments)));
    }
    wrappedFunc.isWrapper = true;
    wrappedFunc.originalFunction = func;
    return wrappedFunc;
  },

  wrap: function(func, wrapper) {
    var __method = func;
    var wrappedFunc = function wrapped() {
      var wrapperArgs = wrapper.isWrapper ?
        Array.prototype.slice.call(arguments) :
        [__method.bind(func)].concat(Array.prototype.slice.call(arguments));
      return wrapper.apply(func, wrapperArgs);
    }
    wrappedFunc.isWrapper = true;
    wrappedFunc.originalFunction = __method;
    return wrappedFunc;
  },

  getOriginal: function(func) {
    // get the original 'unwrapped' function, traversing as many wrappers as necessary.
    while (func.originalFunction) func = func.originalFunction;
    return func;
  },

  // -=-=-=-=-
  // creation
  // -=-=-=-=-
  fromString: function(funcOrString) {
    return eval('(' + funcOrString.toString() + ')')
  }

};

})(typeof jsext !== 'undefined' ? jsext : this);
<commit_msg>function cleanup<commit_after>/*global clearTimeout, setTimeout*/

;(function(exports) {
"use strict";

var fun = exports.fun = {

  // -=-=-=-=-=-=-=-=-
  // static functions
  // -=-=-=-=-=-=-=-=-
  get Empty() { return function() {}; },
  get K() { return function(arg) { return arg; }; },
  get Null() { return function() { return null; }; },
  get False() { return function() { return false; }; },
  get True() { return function() { return true; }; },

  // -=-=-=-=-=-
  // accessing
  // -=-=-=-=-=-
  all: function(object) {
    var a = [];
    for (var name in object) {
      if (!object.__lookupGetter__(name)
       && typeof object[name] === 'function') a.push(name);
    }
    return a;
  },

  own: function(object) {
    var a = [];
    for (var name in object) {
      if (!object.__lookupGetter__(name)
       && object.hasOwnProperty(name)
       && typeof object[name] === 'function') a.push(name);
    }
    return a;
  },

  // -=-=-=-
  // timing
  // -=-=-=-
  timeToRun: function(func) {
    var startTime = Date.now();
    func();
    return Date.now() - startTime;
  },

  timeToRunN: function(func, n, arg0, arg1, arg2) {
    var startTime = Date.now();
    for (var i = 0; i < n; i++) func(arg0, arg1, arg2);
    return (Date.now() - startTime) / n;
  },

  // these last two methods are Underscore.js 1.3.3 and are slightly adapted
  // Underscore.js license:
  // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
  // Underscore is distributed under the MIT license.

  throttle: function(func, wait) {
    // exec func at most once every wait ms even when called more often
    // useful to calm down eagerly running updaters and such
    /* Example:
        var i = 0;
        x = fun.throttle(function() { show(++i + '-' + Date.now()) }, 500);
        Array.range(0,100).forEach(function(n) { x() });
    */
    var context, args, timeout, throttling, more, result,
        whenDone = fun.debounce(wait, function() { more = throttling = false; });
    return function() {
      context = this; args = arguments;
      var later = function() {
        timeout = null;
        if (more) func.apply(context, args);
        whenDone();
      };
      if (!timeout) timeout = setTimeout(later, wait);
      if (throttling) {
        more = true;
      } else {
        result = func.apply(context, args);
      }
      whenDone();
      throttling = true;
      return result;
    };
  },

  debounce: function(wait, func, immediate) {
    // Execute func after wait milliseconds elapsed since invocation.
    // E.g. to exec something after receiving an input stream
    // with immediate truthy exec immediately but when called before
    // wait ms are done nothing happens. E.g. to not exec a user invoked
    // action twice accidentally
    var timeout;
    return function() {
      var context = this, args = arguments;
      var later = function() {
        timeout = null;
        if (!immediate) func.apply(context, args);
      };
      if (immediate && !timeout) func.apply(context, args);
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  },

  throttleNamed: function(name, wait, func) {
    // see comment in debounceNamed
    var store = fun._throttledByName || (fun._throttledByName = {});
    if (store[name]) return store[name];
    function throttleNamedWrapper() {
      // cleaning up
      fun.debounceNamed(name, wait, function() { delete store[name]; })();
      func.apply(this, arguments);
    }
    return store[name] = fun.throttle(throttleNamedWrapper, wait);
  },

  debounceNamed: function(name, wait, func, immediate) {
    // debounce is based on the identity of the function called. When you call the
    // identical method using debounce, multiple calls that happen between the first
    // invocation and wait time will only cause execution once. However, wrapping a
    // function with debounce and then storing (to be able to call the exact same
    // function again) it is a repeating task and unpractical when using anonymous
    // methods. debounceNamed() automatically maps function to ids and removes the
    // need for this housekeeping code.
    var store = fun._debouncedByName || (fun._debouncedByName = {});
    if (store[name]) return store[name];
    function debounceNamedWrapper() {
      // cleaning up
      delete store[name];
      func.apply(this, arguments);
    }
    return store[name] = fun.debounce(wait, debounceNamedWrapper, immediate);
  },

  createQueue: function(id, workerFunc) {
    var store = fun._queues || (fun._queues = {});

    var queue = store[id] || (store[id] = {
        _workerActive: false,
        worker: workerFunc, tasks: [],
        drain: null, // can be overwritten by a function
        push: function(task) {
          queue.tasks.push(task);
          queue.activateWorker();
        },
        pushAll: function(tasks) {
          tasks.forEach(function(ea) { queue.tasks.push(ea); });
          queue.activateWorker();
        },
        pushNoActivate: function(task) {
          queue.tasks.push(task);
        },
        handleError: function(err) {
          // can be overwritten
          err && console.error('Error in queue: ' + err);
        },
        activateWorker: function() {
          function callback(err) { queue.handleError(err); queue.activateWorker(); }
          var tasks = queue.tasks, active = queue._workerActive;
          if (tasks.length === 0) {
            if (active) {
              queue._workerActive = false;
              if (typeof queue.drain === 'function') queue.drain();
            }
            delete store[id];
          } else {
            if (!active) queue._workerActive = true;
            try {
              queue.worker(tasks.shift(), callback);
            } catch(err) { callback(err); }
          }
        }
    });

    return queue;
  },

  workerWithCallbackQueue: function(id, workerFunc, optTimeout) {
    // This functions helps when you have a long running computation that
    // multiple call sites (independent from each other) depend on. This
    // function does the houskeeping to start the long running computation
    // just once and returns an object that allows to schedule callbacks
    // once the workerFunc is done
    // this is how it works:
    // if id does not exist, workerFunc is called, otherwise ignored.
    // workerFunc is expected to call thenDoFunc with arguments: error, arg1, ..., argN
    // if called subsequently before workerFunc is done, the other thenDoFunc
    // will "pile up" and called with the same arguments as the first
    // thenDoFunc once workerFunc is done

    var store = fun._queueUntilCallbacks || (fun._queueUntilCallbacks = {}),
        queueCallbacks = store[id],
        isRunning = !!queueCallbacks;

    if (isRunning) return queueCallbacks;

    var callbacksRun = false, canceled = false;

    function cleanup() {
      if (timeoutProc) clearTimeout(timeoutProc);
      callbacksRun = true;
      delete store[id];
    }

    function runCallbacks(args) {
      if (callbacksRun) return;
      cleanup();
      queueCallbacks.callbacks.forEach(function(cb) {
        try { cb.apply(null, args); } catch (e) {
          console.error(
              "Error when invoking callbacks in queueUntil ["
            + id + "]:\n"
            + (String(e.stack || e)));
        }
      });
    }

    // timeout
    if (optTimeout) {
      var timeoutProc = setTimeout(function() {
        if (callbacksRun) return;
        runCallbacks([new Error("timeout")]);
      }, optTimeout);
    }

    // init the store
    queueCallbacks = store[id] = {
      callbacks: [],
      cancel: function() {
        canceled = true;
        cleanup();
      },
      whenDone: function(cb) {
        queueCallbacks.callbacks.push(cb);
        return queueCallbacks;
      }
    };

    // call worker, but delay so we can immediately return
    setTimeout(function() {
      if (canceled) return;
      try {
        workerFunc(function(/*args*/) { runCallbacks(arguments); });
      } catch (e) { runCallbacks([e]); }
    }, 0);

    return queueCallbacks;
  },

  composeAsync: function(/*functions*/) {
    // composes functions: fun.composeAsync(f,g,h)(arg1, arg2) =
    //   f(arg1, arg2, thenDo1) -> thenDo1(err, fResult)
    // -> g(fResult, thenDo2) -> thenDo2(err, gResult) ->
    // -> h(fResult, thenDo3) -> thenDo2(err, hResult)
    // Example:
    // fun.composeAsync(
    //   function(a,b, thenDo) { thenDo(null, a+b); },
    //  function(x, thenDo) { thenDo(x*4); })(3,2, function(err, result) { alert(result); });

    var toArray = Array.prototype.slice,
        functions = toArray.call(arguments),
        endCallback, intermediateResult;

    return functions.reverse().reduce(function(prevFunc, func) {
      var nextActivated = false;
      return function() {
        var args = toArray.call(arguments);
        if (!endCallback) endCallback = args.length === 0 ? function() {} : args.pop();

        function next(/*err and args*/) {
          nextActivated = true;
          var args = toArray.call(arguments),
              err = args.shift();
          if (err) endCallback && endCallback(err);
          else prevFunc.apply(null, args);
        }

        try {
          func.apply(this, args.concat([next]));
        } catch (e) {
          console.error('composeAsync: ', e.stack || e);
          !nextActivated && endCallback && endCallback(e);
        }
      }
    }, function() {
      endCallback.apply(
        null,
        [null].concat(toArray.call(arguments)));
    });
  },

  compose: function(/*functions*/) {
    // composes functions: fun.compose(f,g,h)(arg1, arg2) = h(g(f(arg1, arg2)))
    // Example:
    //   fun.compose(function(a,b) {return a+b}, function(x) {return x*4})(3,2)

    var functions = Array.prototype.slice.call(arguments);
    return functions.reverse().reduce(
      function(prevFunc, func) {
        return function() {
          return prevFunc(func.apply(this, arguments));
        }
      }, function(x) { return x; });
  },

  flip: function(f) {
    // swaps the first two args
    // fun.flip(function(a, b, c) { return a + b + c; })(' World', 'Hello', '!')
    return function flipped(/*args*/) {
      var args = Array.prototype.slice.call(arguments),
        flippedArgs = [args[1], args[0]].concat(args.slice(2));
      return f.apply(null, flippedArgs);
    }
  },

  waitFor: function(timeoutMs, waitTesterFunc, thenDo) {
    // wait for waitTesterFunc to return true, then run thenDo, passing
    // failure/timout err as first parameter. A timout occurs after
    // timeoutMs. During the wait period waitTesterFunc might be called
    // multiple times
    var start = Date.now();
    (function test() {
      if (waitTesterFunc()) return thenDo();
      var duration = Date.now() - start,
          timeLeft = timeoutMs - duration;
      if (timeLeft <= 0) return thenDo(new Error('timeout'));
      var timeStep = timeLeft < 50 ? timeLeft : 50;
      setTimeout(test, timeStep);
    })();
  },

  // -=-=-=-=-
  // wrapping
  // -=-=-=-=-

  curry: function(/*func and curry args*/) {
    if (arguments.length <= 1) return arguments[0];
    var args = Array.prototype.slice.call(arguments),
        func = args.shift();
    function wrappedFunc() {
      return func.apply(func, args.concat(Array.prototype.slice.call(arguments)));
    }
    wrappedFunc.isWrapper = true;
    wrappedFunc.originalFunction = func;
    return wrappedFunc;
  },

  wrap: function(func, wrapper) {
    var __method = func;
    var wrappedFunc = function wrapped() {
      var wrapperArgs = wrapper.isWrapper ?
        Array.prototype.slice.call(arguments) :
        [__method.bind(func)].concat(Array.prototype.slice.call(arguments));
      return wrapper.apply(func, wrapperArgs);
    }
    wrappedFunc.isWrapper = true;
    wrappedFunc.originalFunction = __method;
    return wrappedFunc;
  },

  getOriginal: function(func) {
    // get the original 'unwrapped' function, traversing as many wrappers as necessary.
    while (func.originalFunction) func = func.originalFunction;
    return func;
  },

  // -=-=-=-=-
  // creation
  // -=-=-=-=-
  fromString: function(funcOrString) {
    return eval('(' + funcOrString.toString() + ')')
  }

};

})(typeof jsext !== 'undefined' ? jsext : this);
<|endoftext|>


<commit_before>"use strict";

let events = require( 'events' );
let jsonGate = require( 'json-gate' );

let pem = require( './pem.js' );
let time = require( './time.js' );
let DefinitionSink = require( './definition-sink.js' );
let HormoneSink = require( './hormone-sink.js' );

// The hormone names must not start or end with '/' and must not be empty
let nameSchema = jsonGate.createSchema( {
	type: 'string',
	pattern: '^[^/]+.*$'
} );

let definitionRE = /^definition\/(.*)$/;
let hormoneRE = /^hormone\/(.*)$/;

class Sink extends events.EventEmitter {

	constructor( es, filter, certCheck ) {

		super();

		// TODO: Check es

		this._es = es;
		this._filter = filter;
		this._certCheck = certCheck;

		// Create some stores ...
		this._subscriptions = {}; // Received definitions and subscribed hormones
		this._hormones = {};      // Last received hormone by name

		// Subscribe to defintion messages
		es._subscribe( 'definition/' + filter, ( topic, payload ) => {
			this._receivedDefinition( topic, payload );
		} ).catch( ( err ) => {
			this.emit( 'error', err );
		} );

	}

	destroy() {

		// Unsubscribe definition topic
		return this._es._unsubscribe( 'defintion/' + this._filter ).then( () => {

			let unsubJobs = [];

			// Unsubscribe all hormones
			for( let name in this._subscriptions ) {
				unsubJobs.push( this._removeHormoneSource( name ) );
			}

			// Wait for all unsubscriptions to be done
			return Promise.all( unsubJobs );

		});

	}

	_receivedDefinition( topic, payload ) {

		// Extract name
		let tmp = definitionRE.exec( topic );
		if( ! tmp ) {
			return this.emit( 'receiveError', new Error( "Received no definition" ) );
		}
		let name = tmp[ 1 ];

		// The payload decides what to do
		if( payload == '' ) {
			// Remove hormone source
			this._removeHormoneSource( name ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		} else {
			// Add hormone source
			this._addHormoneSource( name, payload ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		}

	}

	_addHormoneSource( name, payload ) {

		// Store for definition data
		let definition;

		return new Promise( ( resolve ) => {

			// Create new definition from payload
			// Makes sure the format and signature are vaild
			definition = new DefinitionSink( payload );

			// At this point the payload was readable and okay
			resolve();

		} ).then( () => {

			// Check the certificate
			let data = definition.data;
			return Promise.all( [
				pem.verifySigningChain( data.cert, this._es._ca ),
				pem.readCertificateInfo( data.cert )
			] )

		} ).then( ( results ) => {

			// Is cert signed by CA?
			if( ! results[0] ) {
				throw new Error( "Certificate has not been signed by CA" );
			}

			// Is the cert still valid?
			// First get current time. If we have an ESdummy, read time from es.
			// Otherwise get the current time from the OS
			let now = time.now();
			if( results[1].validity.start > now ) {
				throw new Error( "Certificate is valid in the future" );
			}
			if( results[1].validity.end < now ) {
				throw new Error( "Certificate has been expired" );
			}

			// Own check function
			if( typeof this._certCheck == 'function' ) {
				// The check function my return a promise, a value (-> everything is
				// ok) or throws an error
				return this._certCheck( name, results[ 1 ] );
			}

		} ).then( () => {

			// If the hormone is known, remove the old one first
			if( this._subscriptions[ name ] ) this._removeHormoneSource( name );

			// Subscribe to hormone
			return this._es._subscribe( 'hormone/' + name, ( topic, payload ) => {

				// Skip empty messages
				if( payload == '' ) return;

				// Hand over hormone to handler
				this._receivedHormone( topic, payload );

			} );

		} ).then( () => {

			// Store handle
			this._subscriptions[ name ] = definition;

			// Report that we started listening to hormones
			this.emit( 'subscribe', name, definition );

		} );

	}

	_removeHormoneSource( name ) {

		// Check if we know the hormone source that shall be removed
		if( ! this._subscriptions[ name ] ) {
			return Promise.reject( new Error( "Received unkown definition removal" ) );
		}

		// Unsubscribe hormone
		return this._es._unsubscribe( 'hormone/' + name ).then( () => {

			// Remove timeout if it is pending
			if( this._hormones[ name ].timeout ) {
				clearTimeout( this._hormones[ name ].timeout );
			}

			// Remove subscription and last hormone
			delete this._subscriptions[ name ];
			delete this._hormones[ name ];

		} );

	}

	_receivedHormone( topic, payload ) {
		try {

			// Extract name
			let tmp = hormoneRE.exec( topic );
			if( ! tmp ) throw new Error( "Received no hormone" );
			let name = tmp[ 1 ];

			// Check if we have a definition for the received hormone
			if( ! this._subscriptions[ name ] ) throw new Error( "Received unkown hormone" );

			// Interprete hormone
			let hormone = new HormoneSink( this._subscriptions[ name ], payload );

			// At this point the hormone is okay :) At least the signature

			// If this is the first hormone we recieved under this name, make sure we
			// have at least some dummy data to compare with
			let formerHormone = this._hormones[ name ] ? this._hormones[ name ] : {
				isFresh: true,
				err: 0,
				timeout: null
			} ;

			// Stop expiration timeout if present
			if( formerHormone.timeout ) clearTimeout( formerHormone.timeout );

			// Further checks:
			// - Freshness
			let isFresh = hormone.isFresh;
			if( isFresh && ! formerHormone.isFresh ) {
				// The expired hormone went fresh -> tell the good news!
				this.emit( 'hormoneRefresh', name, hormone );
			} else if( ! isFresh && formerHormone.isFresh ) {
				// The received hormone has expired!
				this.emit( 'hormoneExpired', name, hormone );
			}
			// - Error
			let err = hormone.error;
			if( err == 0 && formerHormone.err != err ) {
				// The hormone reported recovery
				this.emit( 'hormoneRecover', name, hormone );
			} else if( err > 0 && formerHormone.err != err ) {
				// Some error occured!
				this.emit( 'hormoneError', name, hormone );
			}

			// If the hormone is fresh, set an expire timeout
			let timeout = null;
			if( isFresh ) timeout = setTimeout( () => {
				// The hormone just expired!
				this._hormones[ name ].isFresh = false;
				this._hormones[ name ].timeout = null;
				this.emit( 'hormoneExpired', name, hormone );
			}, hormone.freshness * 1000 );

			// Store all hormone related data
			this._hormones[ name ] = {
				hormone: hormone,
				isFresh: isFresh,
				err: err,
				timeout: timeout
			};

			// Tell the user we got a new hormone
			this.emit( 'hormone', name, hormone );

		} catch( err ) {
			this.emit( 'receiveError', err );
		}

	}

	_fetchHormones( cond ) {
		if( typeof cond !== 'object' ) cond = {};

		// Copy all hormones into an array
		let hormones = [];
		for( let name in this._hormones ) {
			let h = this._hormones[ name ];

			// First we assume that the hormone is part of the required set
			let add = true;

			// Check conditions. If a condition is not met, add is set false
			// - A certain freshness is required
			if( cond.isFresh !== undefined && h.isFresh !== cond.isFresh ) {
				add = false
			}
			// - The hormone error state is interesting
			if( cond.err !== undefined ) {
				if( cond.err && h.err <= 0 ) {
					add = false;
				} else if( ! cond.err && h.err > 0 ) {
					add = false;
				}
			}

			// If no show stopper occured, add the hormone to the set
			if( add ) hormones.push( h );

		}

		return hormones;

	}

	get hormones() { return this._fetchHormones( {} ); }
	get expiredHormones() { return this._fetchHormones( { isFresh: false } ); }
	get errorHormones() { return this._fetchHormones( { err: true } ); }
	get goodHormones() { return this._fetchHormones( { err: false, isFresh: true } ); }

}

module.exports = Sink;
<commit_msg>Store and report some interesting timestamps<commit_after>"use strict";

let events = require( 'events' );
let jsonGate = require( 'json-gate' );

let pem = require( './pem.js' );
let time = require( './time.js' );
let DefinitionSink = require( './definition-sink.js' );
let HormoneSink = require( './hormone-sink.js' );

// The hormone names must not start or end with '/' and must not be empty
let nameSchema = jsonGate.createSchema( {
	type: 'string',
	pattern: '^[^/]+.*$'
} );

let definitionRE = /^definition\/(.*)$/;
let hormoneRE = /^hormone\/(.*)$/;

class Sink extends events.EventEmitter {

	constructor( es, filter, certCheck ) {

		super();

		// TODO: Check es

		this._es = es;
		this._filter = filter;
		this._certCheck = certCheck;

		// Create some stores ...
		this._subscriptions = {}; // Received definitions and subscribed hormones
		this._hormones = {};      // Last received hormone by name

		// Subscribe to defintion messages
		es._subscribe( 'definition/' + filter, ( topic, payload ) => {
			this._receivedDefinition( topic, payload );
		} ).catch( ( err ) => {
			this.emit( 'error', err );
		} );

	}

	destroy() {

		// Unsubscribe definition topic
		return this._es._unsubscribe( 'defintion/' + this._filter ).then( () => {

			let unsubJobs = [];

			// Unsubscribe all hormones
			for( let name in this._subscriptions ) {
				unsubJobs.push( this._removeHormoneSource( name ) );
			}

			// Wait for all unsubscriptions to be done
			return Promise.all( unsubJobs );

		});

	}

	_receivedDefinition( topic, payload ) {

		// Extract name
		let tmp = definitionRE.exec( topic );
		if( ! tmp ) {
			return this.emit( 'receiveError', new Error( "Received no definition" ) );
		}
		let name = tmp[ 1 ];

		// The payload decides what to do
		if( payload == '' ) {
			// Remove hormone source
			this._removeHormoneSource( name ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		} else {
			// Add hormone source
			this._addHormoneSource( name, payload ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		}

	}

	_addHormoneSource( name, payload ) {

		// Store for definition data
		let definition;

		return new Promise( ( resolve ) => {

			// Create new definition from payload
			// Makes sure the format and signature are vaild
			definition = new DefinitionSink( payload );

			// At this point the payload was readable and okay
			resolve();

		} ).then( () => {

			// Check the certificate
			let data = definition.data;
			return Promise.all( [
				pem.verifySigningChain( data.cert, this._es._ca ),
				pem.readCertificateInfo( data.cert )
			] )

		} ).then( ( results ) => {

			// Is cert signed by CA?
			if( ! results[0] ) {
				throw new Error( "Certificate has not been signed by CA" );
			}

			// Is the cert still valid?
			// First get current time. If we have an ESdummy, read time from es.
			// Otherwise get the current time from the OS
			let now = time.now();
			if( results[1].validity.start > now ) {
				throw new Error( "Certificate is valid in the future" );
			}
			if( results[1].validity.end < now ) {
				throw new Error( "Certificate has been expired" );
			}

			// Own check function
			if( typeof this._certCheck == 'function' ) {
				// The check function my return a promise, a value (-> everything is
				// ok) or throws an error
				return this._certCheck( name, results[ 1 ] );
			}

		} ).then( () => {

			// If the hormone is known, remove the old one first
			if( this._subscriptions[ name ] ) this._removeHormoneSource( name );

			// Subscribe to hormone
			return this._es._subscribe( 'hormone/' + name, ( topic, payload ) => {

				// Skip empty messages
				if( payload == '' ) return;

				// Hand over hormone to handler
				this._receivedHormone( topic, payload );

			} );

		} ).then( () => {

			// Store handle
			this._subscriptions[ name ] = definition;

			// Report that we started listening to hormones
			this.emit( 'subscribe', name, definition );

		} );

	}

	_removeHormoneSource( name ) {

		// Check if we know the hormone source that shall be removed
		if( ! this._subscriptions[ name ] ) {
			return Promise.reject( new Error( "Received unkown definition removal" ) );
		}

		// Unsubscribe hormone
		return this._es._unsubscribe( 'hormone/' + name ).then( () => {

			// Remove timeout if it is pending
			if( this._hormones[ name ].timeout ) {
				clearTimeout( this._hormones[ name ].timeout );
			}

			// Remove subscription and last hormone
			delete this._subscriptions[ name ];
			delete this._hormones[ name ];

		} );

	}

	_receivedHormone( topic, payload ) {
		try {

			// Extract name
			let tmp = hormoneRE.exec( topic );
			if( ! tmp ) throw new Error( "Received no hormone" );
			let name = tmp[ 1 ];

			// Check if we have a definition for the received hormone
			if( ! this._subscriptions[ name ] ) throw new Error( "Received unkown hormone" );

			// Interprete hormone
			let hormone = new HormoneSink( this._subscriptions[ name ], payload );

			// At this point the hormone is okay :) At least the signature.

			// Keep the current timestamp
			let receivedAt = time.now();

			// If this is the first hormone we recieved under this name, make sure we
			// have at least some dummy data to compare with
			let formerHormone = this._hormones[ name ] ? this._hormones[ name ] : {
				isFresh: true,
				err: 0,
				timeout: null,
				stateChangedAt: time.now()
			} ;

			// Stop expiration timeout if present
			if( formerHormone.timeout ) clearTimeout( formerHormone.timeout );

			// Further checks:
			let stateChanged = false;
			// - Freshness
			let isFresh = hormone.isFresh;
			if( isFresh && ! formerHormone.isFresh ) {
				// The expired hormone went fresh -> tell the good news!
				this.emit( 'hormoneRefresh', name, hormone );
				stateChanged = true;
			} else if( ! isFresh && formerHormone.isFresh ) {
				// The received hormone has expired!
				this.emit( 'hormoneExpired', name, hormone );
				stateChanged = true;
			}
			// - Error
			let err = hormone.error;
			if( err == 0 && formerHormone.err != err ) {
				// The hormone reported recovery
				this.emit( 'hormoneRecover', name, hormone );
				stateChanged = true;
			} else if( err > 0 && formerHormone.err != err ) {
				// Some error occured!
				this.emit( 'hormoneError', name, hormone );
				stateChanged = true;
			}

			// If the hormone is fresh, set an expire timeout
			let timeout = null;
			if( isFresh ) timeout = setTimeout( () => {
				// The hormone just expired!
				this._hormones[ name ].isFresh = false;
				this._hormones[ name ].timeout = null;
				this.emit( 'hormoneExpired', name, hormone );
			}, hormone.freshness * 1000 );

			// Store all hormone related data
			this._hormones[ name ] = {
				receivedAt: receivedAt,
				stateChangedAt: stateChanged ? receivedAt : formerHormone.stateChangedAt,
				hormone: hormone,
				isFresh: isFresh,
				err: err,
				timeout: timeout
			};

			// Tell the user we got a new hormone
			this.emit( 'hormone', name, hormone );

		} catch( err ) {
			this.emit( 'receiveError', err );
		}

	}

	_fetchHormones( cond ) {
		if( typeof cond !== 'object' ) cond = {};

		// Copy all hormones into an array
		let hormones = [];
		for( let name in this._hormones ) {
			let h = this._hormones[ name ];

			// First we assume that the hormone is part of the required set
			let add = true;

			// Check conditions. If a condition is not met, add is set false
			// - A certain freshness is required
			if( cond.isFresh !== undefined && h.isFresh !== cond.isFresh ) {
				add = false
			}
			// - The hormone error state is interesting
			if( cond.err !== undefined ) {
				if( cond.err && h.err <= 0 ) {
					add = false;
				} else if( ! cond.err && h.err > 0 ) {
					add = false;
				}
			}

			// If no show stopper occured, add the hormone to the set
			if( add ) hormones.push( {
				name: name,
				sentAt: h.hormone.timestamp,
				receivedAt: h.receivedAt,
				stateChangedAt: h.stateChangedAt,
				data: h.hormone.data,
				err: h.err,
				isFresh: h.isFresh
			);

		}

		return hormones;

	}

	get hormones() { return this._fetchHormones( {} ); }
	get expiredHormones() { return this._fetchHormones( { isFresh: false } ); }
	get errorHormones() { return this._fetchHormones( { err: true } ); }
	get goodHormones() { return this._fetchHormones( { err: false, isFresh: true } ); }

}

module.exports = Sink;
<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    _path = __dirname
        .replace(/\\/g, '/')
        .replace(/\/$/, ''),
    _testPath = _path+"/release";

var fis = require('../../fis-kernel.js');
var release = fis.release,
    config = fis.config,
    project = fis.project;

var expect = require('chai').expect;

describe('release',function(){

    beforeEach(function(){
        config.init();
        fis.compile.clean();
    });

    /*
    * 结果打包
    * */
    it('pack = true & map &domain',function(done){
        var opt = {
            afterEach:function(file){
                files.push(file.origin);
                filess.push(file);
            },
            pack:true,
            domain:true
        },files=[],expectFiles,filess=[];
        expectFiles =
            [ _testPath+'/test1/index.css',
                _testPath+'/test1/index.js',
                _testPath+'/test1/index.tpl',
                _testPath+'/test1/npm.png',
                _testPath+'/test1/plugin/FISResource.class.php',
                _testPath+'/test1/plugin/compiler.body.php',
                _testPath+'/test1/plugin/compiler.head.php',
                _testPath+'/test1/plugin/compiler.html.php',
                _testPath+'/test1/plugin/compiler.require.php',
                _testPath+'/test1/plugin/compiler.script.php',
                _testPath+'/test1/plugin/compiler.widget.php',
                _testPath+'/test1/sea.js',
                _testPath+'/test1/widget/comp/comp.js',
                _testPath+'/test1/widget/list/list.css',
                _testPath+'/test1/widget/list/list.js',
                _testPath+'/test1/widget/list/list.tpl' ];
        project.setProjectRoot(_testPath+"/test1");
        var conf = _testPath+"/test1/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);

        release(opt,function(ret){
            /*
             *打包的文件存在
             * */
              expect("/aio.js" in ret.pkg,'aio pkg').to.true;
             //包的内容正确
             expect(ret.map.pkg['photo:p0'].has).to.deep.equal([ 'photo:widget/comp/comp.js', 'photo:widget/list/list.js' ]);
             //依赖正确
             expect(ret.map.pkg['photo:p0'].deps).to.deep.equal(['photo:widget/list/list.css']);

             for(var i = 0; i< filess.length;i++){
                 var file = filess[i];
                if(file.ext == ".js"){
                    expect(file.domain).to.equal("http://img.baidu.com");
                }else if(file.ext == ".css"){
                    expect(file.domain).to.equal("http://css.baidu.com");
                }
            }

             /*
              * 普通文件 打包文件 map文件内容测试             * */
            var content = ret.pkg["/photo-map.json"]._content;
            content = content.replace(/[\r\n\t\s]*/g,'');

             fs.readFile(_testPath+"/expect1/photo-map.json","utf-8",function(err,data){
                expect(content).to.equal(data);
//                expect(files).to.deep.equal(expectFiles);
                done();
            });

        });
    });

    it('domain——true',function(done){
        project.setProjectRoot(_testPath+"/test2");
        var conf = _testPath+"/test2/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var files = [];
        var opt = {
            afterEach:function(file){
                files.push(file.origin);
            },
            domain:true
        };
        release(opt,function(ret){
            for(var file in ret.map.res){
                if(ret.map.res[file].type == "css"){
                    expect(/^http:\/\/css\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(true);
                }else if(ret.map.res[file].type == "js"){
                    expect(/^http:\/\/img\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(true);
                }
            }
            for(var i = 0; i<files.length;i++){
                var file = files[i];
                if(file.ext == ".js"){
                    expect(file.domain).to.equal("http://img.baidu.com");
                }else if(file.ext == ".css"){
                    expect(file.domain).to.equal("http://css.baidu.com");
                }
            }
            done();
        });

    });
    it('domain——false',function(done){
        project.setProjectRoot(_testPath+"/test2");
        var conf = _testPath+"/test2/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var opt = {
            domain:false
        };
        release(opt,function(ret){
            for(var file in ret.map.res){
                if(ret.map.res[file].type == "css"){
                    expect(/^http:\/\/css\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(false);
                }else if(ret.map.res[file].type == "js"){
                    expect(/^http:\/\/img\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(false);
                }
            }
            done();
        });
    });

   /*
    * 依赖测试
    * */
    it('deps',function(done){
        project.setProjectRoot(_testPath+"/test4");
        var conf = _testPath+"/test4/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var files = [];
        var opt = {
            pack:true
        };
        release(opt,function(ret){
            /*
             *  依赖检测，不但包括文件中require的内容，还包括同名的css文件
             * */
             expect(ret.src["/widget/list/list.js"].requires).to.deep.equal(
                 [  'photo:widget/comp/comp.js',
                    'photo:widget/c/c.js',
                    'photo:widget/e/e.js',
                    'photo:widget/list/list.css' ]);
            expect(ret.src["/widget/c/c.js"].requires).to.deep.equal(
                ['photo:widget/d/d.js']
            );
            expect(ret.src["/index.js"].requires).to.deep.equal(
                ['photo:index.css']
            );
           expect(ret.src["/ui/a/a.js"].requires).to.deep.equal(
                ['photo:ui/b/b.js']
            );
            done();
        });
    });

    it('opt——md5',function(done){
        project.setProjectRoot(_testPath+"/test5");
        var conf = _testPath+"/test5/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var opt = {
            md5:true
        };
        release(opt,function(ret){
            expect(ret.src["/index.css"]._hash).to.equal(fis.util.md5(ret.src["/index.css"]._content,7));
            expect(ret.src["/index.js"]._hash).to.equal(fis.util.md5(ret.src["/index.js"]._content,7));
            expect(ret.src["/ui/a/a.js"]._hash).to.equal(fis.util.md5(ret.src["/ui/a/a.js"]._content,7));
            expect(ret.src["/widget/list/list.js"]._hash).to.equal(fis.util.md5(ret.src["/widget/list/list.js"]._content,7));
            done();
        });
    });
    it('opt——md5关闭',function(done){
        project.setProjectRoot(_testPath+"/test5");
        var conf = _testPath+"/test5/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        //关闭md5
        var opt = {
            md5:false
        };
        release(opt,function(ret){
            expect("_hash" in ret.src["/index.css"]).to.equal(false);
            expect("_hash" in ret.src["/index.js"]).to.equal(false);
            expect("_hash" in ret.src["/ui/a/a.js"]).to.equal(false);
            expect("_hash" in ret.src["/widget/list/list.js"]).to.equal(false);
            done();
        });
    });

    it('beforeEach & afterEach',function(done){
        fis.project.setProjectRoot(_testPath+"/test6");
        var conf = _testPath+"/test6/fis-conf.js";
        fis.config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var curFile = "",beforeFiles=[],afterFiles=[];

        var opt = {
            beforeEach:function(file){
                beforeFiles.push(file.origin);
            },
            afterEach:function(file){
                if(file.ext!=".png"){
                    expect("cache" in file).to.equal(true);
                    expect("compiled" in file).to.equal(true);
                    expect("_content" in file).to.equal(true);
                }
                afterFiles.push(file.origin);
            },
            pack:true
        };
        release(opt,function(){
            done();
        });
        expect(beforeFiles).to.deep.equal(afterFiles);
    });

    it('prepackager & postpackager',function(done){
        fis.project.setProjectRoot(_testPath+"/test3");
        var conf = _testPath+"/test3/fis-conf.js";
        fis.config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var opt = {
            prepackager:function(ret){
                expect(ret.pkg).to.deep.equal({});
            },
            postpackager:function(ret){
                expect("/static/aio1.js" in ret.pkg,'aio1.js').to.be.true;
                expect("/static/aio.js" in ret.pkg,'aio.js').to.be.true;
                done();
            },
            pack:true
        };
        release(opt,function(ret){
              expect(ret.map.pkg["photo:p0"].has,'photo:p0').to.deep.equal([
                  "photo:widget/comp/comp.js",
                  "photo:widget/list/list.js"
              ]);
              expect(ret.map.pkg["photo:p1"].has,'photo:p1').to.deep.equal([
                  "photo:ui/a/a.js",
                  "photo:ui/b/b.js"
              ]);
        });
    });

    it("打包支持正则",function(done){
        fis.project.setProjectRoot(_testPath+"/test7");
        var conf = _testPath+"/test7/fis-conf.js";
        fis.config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var opt = {
            pack:true
        };

        release(opt,function(ret){
            expect(ret.map.pkg["photo:p0"].has).to.deep.equal([
                "photo:widget/comp/comp.js",
                "photo:widget/list/list.js"
            ]);
            expect(ret.map.pkg["photo:p1"].has).to.deep.equal([
                "photo:ui/b/b.js",
                "photo:ui/a/a.js"
            ]);
            expect(ret.map.pkg["photo:p2"].has).to.deep.equal([
                "photo:js/a/a.js",
                "photo:js/b/b.js"
            ]);
            done();
        });

    });

//    //需要添加，bug未修
//    it("打包文件 打包",function(done){
//        fis.project.setProjectRoot(_testPath+"/test8");
//        var conf = _testPath+"/test8/fis-conf.js";
//        fis.config.merge(fis.util.readJSON(_testPath + '/standard.json'));
//        require(conf);
//        var opt = {
//            pack:true
//        };
//
//        release(opt,function(ret){
//            expect(ret.map.pkg["photo:p1"].has).to.deep.equal([
//                "photo:widget/comp/comp.js",
//                "photo:widget/list/list.js"
//            ]);
//            expect(ret.map.pkg["photo:p1"].has).to.deep.equal([
//                "photo:ui/b/b.js",
//                "photo:ui/a/a.js"
//            ]);
//            expect(ret.map.pkg["photo:p2"].has).to.deep.equal([
//                "photo:js/a/a.js",
//                "photo:js/b/b.js"
//            ]);
//            done();
//        });
//
//    });

    it('without opt',function(done){
        //opt为空所有打包、domain、md5都不做
        //domain
        project.setProjectRoot(_testPath+"/test2");
        var conf = _testPath+"/test2/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        release(function(ret){
            for(var file in ret.map.res){
                if(ret.map.res[file].type == "css"){
                    expect(/^http:\/\/css\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(false);
                }else if(ret.map.res[file].type == "js"){
                    expect(/^http:\/\/img\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(false);
                }
            }
            done();
        });
    });
});<commit_msg>[case]更新release用例<commit_after>var fs = require('fs'),
    path = require('path'),
    _path = __dirname
        .replace(/\\/g, '/')
        .replace(/\/$/, ''),
    _testPath = _path+"/release";

var fis = require('../../fis-kernel.js');
var release = fis.release,
    config = fis.config,
    project = fis.project;

var expect = require('chai').expect;

describe('release',function(){

    beforeEach(function(){
        config.init();
        fis.compile.clean();
    });

    /*
    * 结果打包
    * */
    it('pack = true & map &domain',function(done){
        var opt = {
            afterEach:function(file){
                files.push(file.origin);
                filess.push(file);
            },
            pack:true,
            domain:true
        },files=[],expectFiles,filess=[];
        expectFiles =
            [ _testPath+'/test1/index.css',
                _testPath+'/test1/index.js',
                _testPath+'/test1/index.tpl',
                _testPath+'/test1/npm.png',
                _testPath+'/test1/plugin/FISResource.class.php',
                _testPath+'/test1/plugin/compiler.body.php',
                _testPath+'/test1/plugin/compiler.head.php',
                _testPath+'/test1/plugin/compiler.html.php',
                _testPath+'/test1/plugin/compiler.require.php',
                _testPath+'/test1/plugin/compiler.script.php',
                _testPath+'/test1/plugin/compiler.widget.php',
                _testPath+'/test1/sea.js',
                _testPath+'/test1/widget/comp/comp.js',
                _testPath+'/test1/widget/list/list.css',
                _testPath+'/test1/widget/list/list.js',
                _testPath+'/test1/widget/list/list.tpl' ];
        project.setProjectRoot(_testPath+"/test1");
        var conf = _testPath+"/test1/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);

        release(opt,function(ret){
            /*
             *打包的文件存在
             * */
              expect("/aio.js" in ret.pkg,'aio pkg').to.true;
             //包的内容正确
             expect(ret.map.pkg['photo:p0'].has).to.deep.equal([ 'photo:widget/comp/comp.js', 'photo:widget/list/list.js' ]);
             //依赖正确
             expect(ret.map.pkg['photo:p0'].deps).to.deep.equal(['photo:widget/list/list.css']);

             for(var i = 0; i< filess.length;i++){
                 var file = filess[i];
                if(file.ext == ".js"){
                    expect(file.domain).to.equal("http://img.baidu.com");
                }else if(file.ext == ".css"){
                    expect(file.domain).to.equal("http://css.baidu.com");
                }
            }

             /*
              * 普通文件 打包文件 map文件内容测试             * */
            var content = ret.pkg["/photo-map.json"]._content;
            content = content.replace(/[\r\n\t\s]*/g,'');

             fs.readFile(_testPath+"/expect1/photo-map.json","utf-8",function(err,data){
                expect(content).to.equal(data);
//                expect(files).to.deep.equal(expectFiles);
                done();
            });

        });
    });

    it('domain——true',function(done){
        project.setProjectRoot(_testPath+"/test2");
        var conf = _testPath+"/test2/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var files = [];
        var opt = {
            afterEach:function(file){
                files.push(file.origin);
            },
            domain:true
        };
        release(opt,function(ret){
            for(var file in ret.map.res){
                if(ret.map.res[file].type == "css"){
                    expect(/^http:\/\/css\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(true);
                }else if(ret.map.res[file].type == "js"){
                    expect(/^http:\/\/img\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(true);
                }
            }
            for(var i = 0; i<files.length;i++){
                var file = files[i];
                if(file.ext == ".js"){
                    expect(file.domain).to.equal("http://img.baidu.com");
                }else if(file.ext == ".css"){
                    expect(file.domain).to.equal("http://css.baidu.com");
                }
            }
            done();
        });

    });
    it('domain——false',function(done){
        project.setProjectRoot(_testPath+"/test2");
        var conf = _testPath+"/test2/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var opt = {
            domain:false
        };
        release(opt,function(ret){
            for(var file in ret.map.res){
                if(ret.map.res[file].type == "css"){
                    expect(/^http:\/\/css\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(false);
                }else if(ret.map.res[file].type == "js"){
                    expect(/^http:\/\/img\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(false);
                }
            }
            done();
        });
    });

   /*
    * 依赖测试
    * */
    it('deps',function(done){
        project.setProjectRoot(_testPath+"/test4");
        var conf = _testPath+"/test4/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var files = [];
        var opt = {
            pack:true
        };
        release(opt,function(ret){
            /*
             *  依赖检测，不但包括文件中require的内容，还包括同名的css文件
             * */
             expect(ret.src["/widget/list/list.js"].requires).to.deep.equal(
                 [  'photo:widget/comp/comp.js',
                    'photo:widget/c/c.js',
                    'photo:widget/e/e.js',
                    'photo:widget/list/list.css' ]);
            expect(ret.src["/widget/c/c.js"].requires).to.deep.equal(
                ['photo:widget/d/d.js']
            );
            expect(ret.src["/index.js"].requires).to.deep.equal(
                ['photo:index.css']
            );
           expect(ret.src["/ui/a/a.js"].requires).to.deep.equal(
                ['photo:ui/b/b.js']
            );
            done();
        });
    });

    it('opt——md5',function(done){
        project.setProjectRoot(_testPath+"/test5");
        var conf = _testPath+"/test5/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var opt = {
            md5:true
        };
        release(opt,function(ret){
            expect(ret.src["/index.css"]._hash).to.equal(fis.util.md5(ret.src["/index.css"]._content,7));
            expect(ret.src["/index.js"]._hash).to.equal(fis.util.md5(ret.src["/index.js"]._content,7));
            expect(ret.src["/ui/a/a.js"]._hash).to.equal(fis.util.md5(ret.src["/ui/a/a.js"]._content,7));
            expect(ret.src["/widget/list/list.js"]._hash).to.equal(fis.util.md5(ret.src["/widget/list/list.js"]._content,7));
            done();
        });
    });
    it('opt——md5关闭',function(done){
        project.setProjectRoot(_testPath+"/test5");
        var conf = _testPath+"/test5/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        //关闭md5
        var opt = {
            md5:false
        };
        release(opt,function(ret){
            expect("_hash" in ret.src["/index.css"]).to.equal(false);
            expect("_hash" in ret.src["/index.js"]).to.equal(false);
            expect("_hash" in ret.src["/ui/a/a.js"]).to.equal(false);
            expect("_hash" in ret.src["/widget/list/list.js"]).to.equal(false);
            done();
        });
    });

    it('beforeEach & afterEach',function(done){
        fis.project.setProjectRoot(_testPath+"/test6");
        var conf = _testPath+"/test6/fis-conf.js";
        fis.config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var curFile = "",beforeFiles=[],afterFiles=[];

        var opt = {
            beforeEach:function(file){
                beforeFiles.push(file.origin);
            },
            afterEach:function(file){
                if(file.ext!=".png"){
                    expect("cache" in file).to.equal(true);
                    expect("compiled" in file).to.equal(true);
                    expect("_content" in file).to.equal(true);
                }
                afterFiles.push(file.origin);
            },
            pack:true
        };
        release(opt,function(){
            done();
        });
        expect(beforeFiles).to.deep.equal(afterFiles);
    });

    it('prepackager & postpackager',function(done){
        fis.project.setProjectRoot(_testPath+"/test3");
        var conf = _testPath+"/test3/fis-conf.js";
        fis.config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var opt = {
            prepackager:function(ret){
                expect(ret.pkg).to.deep.equal({});
            },
            postpackager:function(ret){
                expect("/static/aio1.js" in ret.pkg,'aio1.js').to.be.true;
                expect("/static/aio.js" in ret.pkg,'aio.js').to.be.true;
                done();
            },
            pack:true
        };
        release(opt,function(ret){
              expect(ret.map.pkg["photo:p0"].has,'photo:p0').to.deep.equal([
                  "photo:widget/comp/comp.js",
                  "photo:widget/list/list.js"
              ]);
              expect(ret.map.pkg["photo:p1"].has,'photo:p1').to.deep.equal([
                  "photo:ui/a/a.js",
                  "photo:ui/b/b.js"
              ]);
        });
    });

    it("打包支持正则",function(done){
        fis.project.setProjectRoot(_testPath+"/test7");
        var conf = _testPath+"/test7/fis-conf.js";
        fis.config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        var opt = {
            pack:true
        };

        release(opt,function(ret){
            expect(ret.map.pkg["photo:p0"].has).to.deep.equal([
                "photo:widget/comp/comp.js",
                "photo:widget/list/list.js"
            ]);
            expect(ret.map.pkg["photo:p1"].has).to.deep.equal([
                "photo:ui/b/b.js",
                "photo:ui/a/a.js"
            ]);
            expect(ret.map.pkg["photo:p2"].has).to.deep.equal([
                "photo:js/a/a.js",
                "photo:js/b/b.js"
            ]);
            done();
        });

    });

//    //需要添加，bug未修
//    it("打包文件 打包",function(done){
//        fis.project.setProjectRoot(_testPath+"/test8");
//        var conf = _testPath+"/test8/fis-conf.js";
//        fis.config.merge(fis.util.readJSON(_testPath + '/standard.json'));
//        require(conf);
//        var opt = {
//            pack:true
//        };
//
//        release(opt,function(ret){
//            expect(ret.map.pkg["photo:p1"].has).to.deep.equal([
//                "photo:widget/comp/comp.js",
//                "photo:widget/list/list.js"
//            ]);
//            expect(ret.map.pkg["photo:p1"].has).to.deep.equal([
//                "photo:ui/b/b.js",
//                "photo:ui/a/a.js"
//            ]);
//            expect(ret.map.pkg["photo:p2"].has).to.deep.equal([
//                "photo:js/a/a.js",
//                "photo:js/b/b.js"
//            ]);
//            done();
//        });
//
//    });

    it('without opt',function(done){
        //opt为空所有打包、domain、md5都不做
        //domain
        project.setProjectRoot(_testPath+"/test2");
        project.getTempPath();
        var conf = _testPath+"/test2/fis-conf.js";
        config.merge(fis.util.readJSON(_testPath + '/standard.json'));
        require(conf);
        release(function(ret){
            for(var file in ret.map.res){
                if(ret.map.res[file].type == "css"){
                    expect(/^http:\/\/css\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(false);
                }else if(ret.map.res[file].type == "js"){
                    expect(/^http:\/\/img\.baidu\.com/.test(ret.map.res[file].uri)).to.equal(false);
                }
            }
            done();
        });
    });
});<|endoftext|>


<commit_before>var should = require('should');
var PassStream = require('pass-stream');
var jayson = require('./..');
var utils = jayson.utils;

describe('jayson.utils', function() {

  describe('request', function() {

    it('exists', function() {
      utils.should.have.property('request');
      utils.request.should.be.a.Function;
    });

    it('should throw a TypeError on an invalid method argument', function() {
      (function() {
        utils.request(null, [1, 2,], null);
      }).should.throw(TypeError);
    });

    it('should throw a TypeError on an invalid params argument', function() {
      (function() {
        utils.request('a_method', true);
      }).should.throw(TypeError);
    });

    it('should omit the params argument when not given', function() {
      var request = utils.request('a_method', null);
      request.should.have.property('method', 'a_method');
      request.should.not.have.property('params');
    });

  });

  describe('getParameterNames', function() {

    it('should return an empty array when passed a parameter-less function', function() {
      var func = function() { return true; };
      var result = utils.getParameterNames(func);
      should.exist(result);
      result.should.be.instanceof(Array);
      result.should.have.length(func.length);
    });

    it('should return the correct names when passed a single-parameter function', function() {
      var func = function(a) { return a; };
      var result = utils.getParameterNames(func);
      should.exist(result);
      result.should.be.instanceof(Array);
      result.should.have.length(func.length);
      result.should.containDeep(['a']);
    });

    it('should return the correct names when passed a simple function', function() {
      var func = function(a, b) { return a + b; };
      var result = utils.getParameterNames(func);
      should.exist(result);
      result.should.be.instanceof(Array);
      result.should.have.length(func.length);
      result.should.containDeep(['a', 'b']);
    });

    it('should return the correct names when passed a odd-formatted function', function() {
      var func = function     (a, b            , __b) {
        func(2, 3, 55, 4);
        return a + b;
      };
      var result = utils.getParameterNames(func);
      should.exist(result);
      result.should.be.instanceof(Array);
      result.should.have.length(func.length);
      result.should.containDeep(['a', 'b', '__b']);
    });

    it('should return the correct names when passed multi-line arguments', function() {
      var func = function (
        a,
        b , __b) {
        func(2, 3, 55, 4);
        return a + b;
      };
      var result = utils.getParameterNames(func);
      should.exist(result);
      result.should.be.instanceof(Array);
      result.should.have.length(func.length);
      result.should.containDeep(['a', 'b', '__b']);
    });

    it('should return the correct names when passed a function with complex parameters', function() {
      var func = function(_$foo, $$, FOO, $F00, _) { return false; };
      var result = utils.getParameterNames(func);
      should.exist(result);
      result.should.be.instanceof(Array);
      result.should.have.length(func.length);
      result.should.containDeep(['_$foo', '$$', 'FOO', '$F00', '_']);
    });

    it('should return the correct names in the right order', function() {
      var func = function(b, c, a) { return false; };
      var result = utils.getParameterNames(func);
      should.exist(result);
      result.should.be.instanceof(Array);
      result.should.have.length(func.length);
      result.should.containDeep(['b', 'c', 'a']);
      result[0].should.equal('b');
      result[1].should.equal('c');
      result[2].should.equal('a');
    });

    it('should return the correct parameters when passed a simple named function', function() {
      var func = function named(b, c, a) {};
      var result = utils.getParameterNames(func);
      should.exist(result);
      result.should.be.instanceof(Array).and.have.length(func.length);
      result.should.containDeep(['b', 'c', 'a']);
    });

    it('should return the correct parameters when passed a complex named function', function() {
      var func = function named_complex$(b, c, a) {};
      var result = utils.getParameterNames(func);
      should.exist(result);
      result.should.be.instanceof(Array).and.have.length(func.length);
      result.should.containDeep(['b', 'c', 'a']);
    });

    it('should return the correct paremters when passed an ES6 style function with zero parameters', function() {
      var func = () => {};
      var result = utils.getParameterNames(func);
      should.exist(result);
      result.should.be.instanceof(Array).and.have.length(0);
    });

    it('should return the correct paremters when passed an ES6 style function with more than zero parameters', function() {
      var func = (b, c, a) => {};
      var result = utils.getParameterNames(func);
      should.exist(result);
      result.should.be.instanceof(Array).and.have.length(func.length);
      result.should.containDeep(['b', 'c', 'a']);
    });

  });

  describe('parseBody', function() {

    var parseBody = utils.parseBody;

    it('should parse a valid json object', function(done) {
      var stream = new PassStream();
      var obj = {asdf: true, complex: {value: 2, a: 3}};

      parseBody(stream, {}, function(err, result) {
        if(err) return done(err);
        obj.should.eql(result);
        done();
      });

      stream.end(JSON.stringify(obj));
    });

    it('should parse a valid json array', function(done) {
      var stream = new PassStream();
      var arr = [{first: true}, {asdf: true, complex: {value: 2, a: 3}}];

      parseBody(stream, {}, function(err, result) {
        if(err) return done(err);
        arr.should.eql(result);
        done();
      });

      stream.end(JSON.stringify(arr));
    });

    it('should return an error on bad input', function(done) {
      var stream = new PassStream();

      parseBody(stream, {}, function(err, result) {
        should(err).be.instanceof(Error);
        done();
      });

      stream.end("\"");
    });

  });

  describe('stringify', function(done) {

    it('should not throw with circular JSON reference', function(done) {

      var foo = {};
      var bar = { foo: foo };
      foo.bar = bar;

      var fn = utils.JSON.stringify(bar, {}, function(err, str) {
        should(err).not.exist;
        done();
      });

      should(fn).not.throw();
    });

  });

});
<commit_msg>Add table based testing for function parameter parsing<commit_after>var should = require('should');
var PassStream = require('pass-stream');
var jayson = require('./..');
var utils = jayson.utils;

describe('jayson.utils', function() {

  describe('request', function() {

    it('exists', function() {
      utils.should.have.property('request');
      utils.request.should.be.a.Function;
    });

    it('should throw a TypeError on an invalid method argument', function() {
      (function() {
        utils.request(null, [1, 2,], null);
      }).should.throw(TypeError);
    });

    it('should throw a TypeError on an invalid params argument', function() {
      (function() {
        utils.request('a_method', true);
      }).should.throw(TypeError);
    });

    it('should omit the params argument when not given', function() {
      var request = utils.request('a_method', null);
      request.should.have.property('method', 'a_method');
      request.should.not.have.property('params');
    });

  });

  describe('getParameterNames', function() {

    var specs = [
      {
        desc: 'no parameters',
        fn: function() { return true; },
        expected: [],
      },
      {
        desc: 'single-parameter fn',
        fn: function(a) {},
        expected: ['a'],
      },
      {
        desc: 'simple fn',
        fn: function(a, b) {},
        expected: ['a', 'b'],
      },
      {
        desc: 'odd-formatted function',
        fn: function     (a, b            , __b) {},
        expected: ['a', 'b', '__b'],
      },
      {
        desc: 'multi-line arguments',
        fn: function (
          a,
          b , __b) {},
        expected: ['a', 'b', '__b'],
      },
      {
        desc: 'complex parameters',
        fn: function(_$foo, $$, FOO, $F00, _) {},
        expected: ['_$foo', '$$', 'FOO', '$F00', '_'],
      },
      {
        desc: 'returning in right order',
        fn: function(b, c, a) {},
        expected: ['b', 'c', 'a'],
      },
      {
        desc: 'named function',
        fn: function named(b, c, a) {},
        expected: ['b', 'c', 'a'],
      },
      {
        desc: 'complex named function',
        fn: function named_complex$(b, c, a) {},
        expected: ['b', 'c', 'a'],
      },
      {
        desc: 'arrow function no parameters',
        fn: () => {},
        expected: [],
      },
      {
        desc: 'arrow function simple parameters',
        fn: (a, b, c) =>  {},
        expected: ['a', 'b', 'c'],
      },
      {
        desc: 'class function no params',
        fn: (function() {
          var obj = {a() {}};
          return obj.a;
        })(),
        expected: [],
      },
      {
        desc: 'class function',
        fn: (function() {
          var obj = {a(a, b, c) {}};
          return obj.a;
        })(),
        expected: ['a', 'b', 'c'],
      },
    ];

    specs.forEach(function(spec) {

      it('should handle ' + spec.desc, function() {
        var result = utils.getParameterNames(spec.fn);
        should.exist(result);
        result.should.eql(spec.expected);
      });

    });

  });

  describe('parseBody', function() {

    var parseBody = utils.parseBody;

    it('should parse a valid json object', function(done) {
      var stream = new PassStream();
      var obj = {asdf: true, complex: {value: 2, a: 3}};

      parseBody(stream, {}, function(err, result) {
        if(err) return done(err);
        obj.should.eql(result);
        done();
      });

      stream.end(JSON.stringify(obj));
    });

    it('should parse a valid json array', function(done) {
      var stream = new PassStream();
      var arr = [{first: true}, {asdf: true, complex: {value: 2, a: 3}}];

      parseBody(stream, {}, function(err, result) {
        if(err) return done(err);
        arr.should.eql(result);
        done();
      });

      stream.end(JSON.stringify(arr));
    });

    it('should return an error on bad input', function(done) {
      var stream = new PassStream();

      parseBody(stream, {}, function(err, result) {
        should(err).be.instanceof(Error);
        done();
      });

      stream.end("\"");
    });

  });

  describe('stringify', function(done) {

    it('should not throw with circular JSON reference', function(done) {

      var foo = {};
      var bar = { foo: foo };
      foo.bar = bar;

      var fn = utils.JSON.stringify(bar, {}, function(err, str) {
        should(err).not.exist;
        done();
      });

      should(fn).not.throw();
    });

  });

});
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';

// Modify with your startup's name!
var startupName = "Upao";

// Put your mock objects here, as in Workshop 4
var initialData = {
  //users
  "users":{
    "1":{
      "_id":1,
      "firstname": "Vincent",
      "lastname": "Lou",
      "nickname": "crown",
      "avatar": "img/user.png",
      "description": "Hello everyone, I'm a mock user",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "friends":[2,3],
      "post":1,
      "activity":1,
      "notification":1,
      "email": "upao@umass.edu",
      "dob_month": 5,
      "dob_day": 21,
      "dob_year": 1995
    },
    "2": {
      "_id":2,
      "firstname": "Test",
      "lastname": "Account2",
      "nickname": "None",
      "avatar": "img/user.png",
      "description": "Hello everyone, I'm a test account",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "friends":[1],
      "post":2,
      "activity":2,
      "notification":2,
      "email": "test@umass.edu",
      "dob_month": 11,
      "dob_day": 11,
      "dob_year": 2016
    },
    "3": {
      "_id":3,
      "firstname": "Test",
      "lastname": "Account3",
      "nickname": "None",
      "avatar": "img/user.png",
      "description": "Hello everyone, I'm a test account",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "friends":[],
      "post":3,
      "activity":3,
      "notification":3,
      "email": "test2@umass.edu",
      "dob_month": 11,
      "dob_day": 11,
      "dob_year": 2016
    }
  },
  //notification collections
  "notifications": {
      "1": {
        "_id":1,
        "contents":[1,2]
      },
      "2": {
        "_id":2,
        "contents":[]
      },
      "3": {
        "_id":3,
        "contents":[]
      }
  },
  "notificationItems": {
    "1": {
      "_id":1,
      "sender":2,
      "type": "FR"
    },
    "2": {
      "_id":2,
      "author": 2,
      "type": "NF"
    }
  },
  //activity collection
  "activities": {
    "1": {
      "_id":1,
      "contents": [1]
    },
    "2": {
      "_id":2,
      "contents": [2]
    },
    "3": {
      "_id":3,
      "contents":[3]
    }
  },

  "activityItems":{
    "1": {
      "_id":1,
      "type": "Event",
      "author":1,
      "title": "Hack UMass",
      "img":"./img/HackUMass.jpg",
      "startTime": 1478129314000,
      "endTime": 1479940314000,
      "description": "Hack Umass",
      "location": "University of Massachusetts Amherst",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "participants": [1,2],
      "likeCounter": [1,2,3],
      "comments":[
        {
          "author": 1,
          "postDate": 1478129314000,
          "text": "iufihishf"
        }
      ],
      "contents": {
        "img": "./img/HackUMass-detail-1.png",
        "text": "Friday, October 7th\n6 PM - 9 PM: Check-in at Campus Center first floor, dinner at Blue Wall Cafe\n9 PM - 10 PM: Opening ceremony in Campus Center Auditorium\n 10 PM: Move to Integrative Learning Center (ILC) \n12 Midnight: Hacking begins in the ILC \n"
      }
    },
    "2": {
      "_id":2,
      "type": "Party",
      "author":2,
      "title": "birthday party",
      "img":"img/Birthday-Party.jpg",
      "startTime": 1478129314000,
      "endTime": 1479940314000,
      "description": "Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis. Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis.",
      "location": "University of Massachusetts Amherst",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "participants": [],
      "likeCounter": [],
      "comments":[
        {
          "author": 1,
          "postDate": 1478129314000,
          "text": "iufihishf"
        }
      ],
      "contents": {
        "img": null,
        "text": "acticity detail of activity 2"
      }
    },
    "3": {
      "_id":3,
      "type": "Party",
      "author":1,
      "title": "dance party",
      "img":"img/parties.jpg",
      "startTime": 1478129314000,
      "endTime": 1479940314000,
      "description": "Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis. Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis.",
      "location": "University of Massachusetts Amherst",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "participants": [2,3],
      "likeCounter": [1,2,3],
      "comments":[
        {
          "author": 1,
          "postDate": 1478129314000,
          "text": "iufihishf"
        }
      ],
      "contents": {
        "img": null,
        "text": "activity detail of activity 3"
      }
    }

  },

  // "feeds" collection. Feeds for each user.
  "postFeeds": {
    "2": {
      "_id": 2,
      "contents": []
    },
    "1": {
      "_id": 1,
      "contents": [1]
    }
  },

  //post feed items
  "postFeedItems": {
    "1": {
      "_id":1,
      "likeCounter":[],
      "type": "general",
      "contents": {
        "author": 1,
        //unix time
        "postDate": 1478129314000,
        "text": "What's up there",
        "img": "img/tmp.jpg"
      },
      "comments": [
        {
          "author": 2,
          "text": "what's up",
          "postDate": 1478149440000
        },
        {
          "author": 3,
          "text": "Hello",
          "postDate": 1478149540000
        }
      ]
    }
  },

  // "feeds" collection. Feeds for each user.
  "messageSession": {
    "1": {
      "_id": 1,
      "contents": [1]
    }
  },
  //message table
  "message": {
    "1": {
      "_id":1,
      "messages": [
        {
          "sender" : 1,
          "target" : 2,
          "date" : 1478149540000,
          "text": "what's up"
        },
        {
          "sender" : 2,
          "target" : 1,
          "date" : 1478149540000,
          "text": `
Mr Trump tweeted that the process of selecting his new cabinet and other positions was "very organised".`

        },
        {
          "sender" : 1,
          "target" : 2,
          "date" : 1478149540000,
          "text": "cool"
        }
      ]
    }
  }

};

var data = JSON.parse(localStorage.getItem(startupName));
if (data === null) {
  data = JSONClone(initialData);
}

/**
 * A dumb cloning routing. Serializes a JSON object as a string, then
 * deserializes it.
 */
function JSONClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

/**
 * Emulates reading a "document" from a NoSQL database.
 * Doesn't do any tricky document joins, as we will cover that in the latter
 * half of the course. :)
 */
export function readDocument(collection, id) {
  // Clone the data. We do this to model a database, where you receive a
  // *copy* of an object and not the object itself.
  return JSONClone(data[collection][id]);
}

/**
 * Emulates writing a "document" to a NoSQL database.
 */
export function writeDocument(collection, changedDocument) {
  var id = changedDocument._id;
  // Store a copy of the object into the database. Models a database's behavior.
  data[collection][id] = JSONClone(changedDocument);
  // Update our 'database'.
  localStorage.setItem(startupName, JSON.stringify(data));
}

export function deleteDocument(collection,id){
  delete data[collection][id];
  localStorage.setItem(startupName, JSON.stringify(data));
}

/**
 * Adds a new document to the NoSQL database.
 */
export function addDocument(collectionName, newDoc) {
  var collection = data[collectionName];
  var nextId = Object.keys(collection).length;
  while (collection[nextId]) {
    nextId++;
  }
  newDoc._id = nextId;
  writeDocument(collectionName, newDoc);
  return newDoc;
}

/**
 * Reset our browser-local database.
 */
export function resetDatabase() {
  localStorage.setItem(startupName, JSON.stringify(initialData));
  data = JSONClone(initialData);
}

/**
 * Reset database button.
 */
class ResetDatabase extends React.Component {
  render() {
    return (
      <button className="btn btn-default" type="button" onClick={() => {
        resetDatabase();
        window.alert("Database reset! Refreshing the page now...");
        document.location.reload(false);
      }}>Reset Mock DB</button>
    );
  }
}

ReactDOM.render(
  <ResetDatabase />,
  document.getElementById('db-reset')
);
<commit_msg>database fix<commit_after>import React from 'react';
import ReactDOM from 'react-dom';

// Modify with your startup's name!
var startupName = "Upao";

// Put your mock objects here, as in Workshop 4
var initialData = {
  //users
  "users":{
    "1":{
      "_id":1,
      "firstname": "Vincent",
      "lastname": "Lou",
      "nickname": "crown",
      "avatar": "img/user.png",
      "description": "Hello everyone, I'm a mock user",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "friends":[2],
      "post":1,
      "activity":1,
      "notification":1,
      "email": "upao@umass.edu",
      "dob_month": 5,
      "dob_day": 21,
      "dob_year": 1995
    },
    "2": {
      "_id":2,
      "firstname": "Test",
      "lastname": "Account2",
      "nickname": "None",
      "avatar": "img/user.png",
      "description": "Hello everyone, I'm a test account",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "friends":[1],
      "post":2,
      "activity":2,
      "notification":2,
      "email": "test@umass.edu",
      "dob_month": 11,
      "dob_day": 11,
      "dob_year": 2016
    },
    "3": {
      "_id":3,
      "firstname": "Test",
      "lastname": "Account3",
      "nickname": "None",
      "avatar": "img/user.png",
      "description": "Hello everyone, I'm a test account",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "friends":[],
      "post":3,
      "activity":3,
      "notification":3,
      "email": "test2@umass.edu",
      "dob_month": 11,
      "dob_day": 11,
      "dob_year": 2016
    }
  },
  //notification collections
  "notifications": {
      "1": {
        "_id":1,
        "contents":[1,2]
      },
      "2": {
        "_id":2,
        "contents":[]
      },
      "3": {
        "_id":3,
        "contents":[]
      }
  },
  "notificationItems": {
    "1": {
      "_id":1,
      "sender":2,
      "type": "FR"
    },
    "2": {
      "_id":2,
      "author": 2,
      "type": "NF"
    }
  },
  //activity collection
  "activities": {
    "1": {
      "_id":1,
      "contents": [1]
    },
    "2": {
      "_id":2,
      "contents": [2]
    },
    "3": {
      "_id":3,
      "contents":[3]
    }
  },

  "activityItems":{
    "1": {
      "_id":1,
      "type": "Event",
      "author":1,
      "title": "Hack UMass",
      "img":"./img/HackUMass.jpg",
      "startTime": 1478129314000,
      "endTime": 1479940314000,
      "description": "Hack Umass",
      "location": "University of Massachusetts Amherst",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "participants": [1,2],
      "likeCounter": [1,2,3],
      "comments":[
        {
          "author": 1,
          "postDate": 1478129314000,
          "text": "iufihishf"
        }
      ],
      "contents": {
        "img": "./img/HackUMass-detail-1.png",
        "text": "Friday, October 7th\n6 PM - 9 PM: Check-in at Campus Center first floor, dinner at Blue Wall Cafe\n9 PM - 10 PM: Opening ceremony in Campus Center Auditorium\n 10 PM: Move to Integrative Learning Center (ILC) \n12 Midnight: Hacking begins in the ILC \n"
      }
    },
    "2": {
      "_id":2,
      "type": "Party",
      "author":2,
      "title": "birthday party",
      "img":"img/Birthday-Party.jpg",
      "startTime": 1478129314000,
      "endTime": 1479940314000,
      "description": "Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis. Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis.",
      "location": "University of Massachusetts Amherst",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "participants": [],
      "likeCounter": [],
      "comments":[
        {
          "author": 1,
          "postDate": 1478129314000,
          "text": "iufihishf"
        }
      ],
      "contents": {
        "img": null,
        "text": "acticity detail of activity 2"
      }
    },
    "3": {
      "_id":3,
      "type": "Party",
      "author":1,
      "title": "dance party",
      "img":"img/parties.jpg",
      "startTime": 1478129314000,
      "endTime": 1479940314000,
      "description": "Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis. Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis.",
      "location": "University of Massachusetts Amherst",
      "country": "USA",
      "state": "MA",
      "city": "Amherst",
      "participants": [2,3],
      "likeCounter": [1,2,3],
      "comments":[
        {
          "author": 1,
          "postDate": 1478129314000,
          "text": "iufihishf"
        }
      ],
      "contents": {
        "img": null,
        "text": "activity detail of activity 3"
      }
    }

  },

  // "feeds" collection. Feeds for each user.
  "postFeeds": {
    "2": {
      "_id": 2,
      "contents": []
    },
    "1": {
      "_id": 1,
      "contents": [1]
    }
  },

  //post feed items
  "postFeedItems": {
    "1": {
      "_id":1,
      "likeCounter":[],
      "type": "general",
      "contents": {
        "author": 1,
        //unix time
        "postDate": 1478129314000,
        "text": "What's up there",
        "img": "img/tmp.jpg"
      },
      "comments": [
        {
          "author": 2,
          "text": "what's up",
          "postDate": 1478149440000
        },
        {
          "author": 3,
          "text": "Hello",
          "postDate": 1478149540000
        }
      ]
    }
  },

  // "feeds" collection. Feeds for each user.
  "messageSession": {
    "1": {
      "_id": 1,
      "contents": [1]
    }
  },
  //message table
  "message": {
    "1": {
      "_id":1,
      "messages": [
        {
          "sender" : 1,
          "target" : 2,
          "date" : 1478149540000,
          "text": "what's up"
        },
        {
          "sender" : 2,
          "target" : 1,
          "date" : 1478149540000,
          "text": `
Mr Trump tweeted that the process of selecting his new cabinet and other positions was "very organised".`

        },
        {
          "sender" : 1,
          "target" : 2,
          "date" : 1478149540000,
          "text": "cool"
        }
      ]
    }
  }

};

var data = JSON.parse(localStorage.getItem(startupName));
if (data === null) {
  data = JSONClone(initialData);
}

/**
 * A dumb cloning routing. Serializes a JSON object as a string, then
 * deserializes it.
 */
function JSONClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

/**
 * Emulates reading a "document" from a NoSQL database.
 * Doesn't do any tricky document joins, as we will cover that in the latter
 * half of the course. :)
 */
export function readDocument(collection, id) {
  // Clone the data. We do this to model a database, where you receive a
  // *copy* of an object and not the object itself.
  return JSONClone(data[collection][id]);
}

/**
 * Emulates writing a "document" to a NoSQL database.
 */
export function writeDocument(collection, changedDocument) {
  var id = changedDocument._id;
  // Store a copy of the object into the database. Models a database's behavior.
  data[collection][id] = JSONClone(changedDocument);
  // Update our 'database'.
  localStorage.setItem(startupName, JSON.stringify(data));
}

export function deleteDocument(collection,id){
  delete data[collection][id];
  localStorage.setItem(startupName, JSON.stringify(data));
}

/**
 * Adds a new document to the NoSQL database.
 */
export function addDocument(collectionName, newDoc) {
  var collection = data[collectionName];
  var nextId = Object.keys(collection).length;
  while (collection[nextId]) {
    nextId++;
  }
  newDoc._id = nextId;
  writeDocument(collectionName, newDoc);
  return newDoc;
}

/**
 * Reset our browser-local database.
 */
export function resetDatabase() {
  localStorage.setItem(startupName, JSON.stringify(initialData));
  data = JSONClone(initialData);
}

/**
 * Reset database button.
 */
class ResetDatabase extends React.Component {
  render() {
    return (
      <button className="btn btn-default" type="button" onClick={() => {
        resetDatabase();
        window.alert("Database reset! Refreshing the page now...");
        document.location.reload(false);
      }}>Reset Mock DB</button>
    );
  }
}

ReactDOM.render(
  <ResetDatabase />,
  document.getElementById('db-reset')
);
<|endoftext|>