<commit_before>'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var HASH = '#'.charCodeAt(0);
var DOT = '.'.charCodeAt(0);

var createElement = function (query, ns) {
  var tag;
  var id;
  var className;

  var mode = 0;
  var start = 0;

  for (var i = 0; i <= query.length; i++) {
    var char = query.charCodeAt(i);

    if (char === HASH || char === DOT || !char) {
      if (mode === 0) {
        if (i === 0) {
          tag = 'div';
        } else if (!char) {
          tag = query;
        } else {
          tag = query.substring(start, i);
        }
      } else {
        var slice = query.substring(start, i);

        if (mode === 1) {
          id = slice;
        } else if (className) {
          className += ' ' + slice;
        } else {
          className = slice;
        }
      }

      start = i + 1;

      if (char === HASH) {
        mode = 1;
      } else {
        mode = 2;
      }
    }
  }

  var element = ns ? document.createElementNS(ns, tag) : document.createElement(tag);

  if (id) {
    element.id = id;
  }

  if (className) {
    if (ns) {
      element.setAttribute('class', className);
    } else {
      element.className = className;
    }
  }

  return element;
};

var hookNames = ['onmount', 'onunmount'];

var mount = function (parent, child, before) {
  var parentEl = getEl(parent);
  var childEl = getEl(child);

  if (child === childEl && childEl.__redom_view) {
    // try to look up the view if not provided
    child = childEl.__redom_view;
  }

  if (child !== childEl) {
    childEl.__redom_view = child;
  }

  var wasMounted = childEl.__redom_mounted;
  var oldParent = childEl.parentNode;

  if (wasMounted && (oldParent !== parentEl)) {
    doUnmount(child, childEl, oldParent);
  }

  if (before != null) {
    parentEl.insertBefore(childEl, getEl(before));
  } else {
    parentEl.appendChild(childEl);
  }

  doMount(child, childEl, parentEl, oldParent);

  return child;
};

var unmount = function (parent, child) {
  var parentEl = getEl(parent);
  var childEl = getEl(child);

  if (child === childEl && childEl.__redom_view) {
    // try to look up the view if not provided
    child = childEl.__redom_view;
  }

  doUnmount(child, childEl, parentEl);

  parentEl.removeChild(childEl);

  return child;
};

var doMount = function (child, childEl, parentEl, oldParent) {
  var hooks = childEl.__redom_lifecycle || (childEl.__redom_lifecycle = {});
  var remount = (parentEl === oldParent);
  var hooksFound = false;

  for (var i = 0; i < hookNames.length; i++) {
    var hookName = hookNames[i];

    if (!remount && (child !== childEl) && (hookName in child)) {
      hooks[hookName] = (hooks[hookName] || 0) + 1;
    }
    if (hooks[hookName]) {
      hooksFound = true;
    }
  }

  if (!hooksFound) {
    childEl.__redom_mounted = true;
    return;
  }

  var traverse = parentEl;
  var triggered = false;

  if (remount || (!triggered && (traverse && traverse.__redom_mounted))) {
    trigger(childEl, remount ? 'onremount' : 'onmount');
    triggered = true;
  }

  if (remount) {
    return;
  }

  while (traverse) {
    var parent = traverse.parentNode;
    var parentHooks = traverse.__redom_lifecycle || (traverse.__redom_lifecycle = {});

    for (var hook in hooks) {
      parentHooks[hook] = (parentHooks[hook] || 0) + hooks[hook];
    }

    if (!triggered && (traverse === document || (parent && parent.__redom_mounted))) {
      trigger(traverse, remount ? 'onremount' : 'onmount');
      triggered = true;
    }

    traverse = parent;
  }
};

var doUnmount = function (child, childEl, parentEl) {
  var hooks = childEl.__redom_lifecycle;

  if (!hooks) {
    childEl.__redom_mounted = false;
    return;
  }

  var traverse = parentEl;

  if (childEl.__redom_mounted) {
    trigger(childEl, 'onunmount');
  }

  while (traverse) {
    var parentHooks = traverse.__redom_lifecycle || (traverse.__redom_lifecycle = {});
    var hooksFound = false;

    for (var hook in hooks) {
      if (parentHooks[hook]) {
        parentHooks[hook] -= hooks[hook];
      }
      if (parentHooks[hook]) {
        hooksFound = true;
      }
    }

    if (!hooksFound) {
      traverse.__redom_lifecycle = null;
    }

    traverse = traverse.parentNode;
  }
};

var trigger = function (el, eventName) {
  if (eventName === 'onmount') {
    el.__redom_mounted = true;
  } else if (eventName === 'onunmount') {
    el.__redom_mounted = false;
  }

  var hooks = el.__redom_lifecycle;

  if (!hooks) {
    return;
  }

  var view = el.__redom_view;
  var hookCount = 0;

  view && view[eventName] && view[eventName]();

  for (var hook in hooks) {
    if (hook) {
      hookCount++;
    }
  }

  if (hookCount) {
    var traverse = el.firstChild;

    while (traverse) {
      var next = traverse.nextSibling;

      trigger(traverse, eventName);

      traverse = next;
    }
  }
};

var setStyle = function (view, arg1, arg2) {
  var el = getEl(view);

  if (arg2 !== undefined) {
    el.style[arg1] = arg2;
  } else if (isString(arg1)) {
    el.setAttribute('style', arg1);
  } else {
    for (var key in arg1) {
      setStyle(el, key, arg1[key]);
    }
  }
};

var setAttr = function (view, arg1, arg2) {
  var el = getEl(view);
  var isSVG = el instanceof window.SVGElement;

  if (arg2 !== undefined) {
    if (arg1 === 'style') {
      setStyle(el, arg2);
    } else if (isSVG && isFunction(arg2)) {
      el[arg1] = arg2;
    } else if (!isSVG && (arg1 in el || isFunction(arg2))) {
      el[arg1] = arg2;
    } else {
      el.setAttribute(arg1, arg2);
    }
  } else {
    for (var key in arg1) {
      setAttr(el, key, arg1[key]);
    }
  }
};

var text = function (str) { return document.createTextNode(str); };

var parseArguments = function (element, args) {
  for (var i = 0; i < args.length; i++) {
    var arg = args[i];

    if (arg !== 0 && !arg) {
      continue;
    }

    // support middleware
    if (typeof arg === 'function') {
      arg(element);
    } else if (isString(arg) || isNumber(arg)) {
      element.appendChild(text(arg));
    } else if (isNode(getEl(arg))) {
      mount(element, arg);
    } else if (arg.length) {
      parseArguments(element, arg);
    } else if (typeof arg === 'object') {
      setAttr(element, arg);
    }
  }
};

var ensureEl = function (parent) { return isString(parent) ? html(parent) : getEl(parent); };
var getEl = function (parent) { return (parent.nodeType && parent) || (!parent.el && parent) || getEl(parent.el); };

var isString = function (a) { return typeof a === 'string'; };
var isNumber = function (a) { return typeof a === 'number'; };
var isFunction = function (a) { return typeof a === 'function'; };

var isNode = function (a) { return a && a.nodeType; };

var htmlCache = {};

var memoizeHTML = function (query) { return htmlCache[query] || (htmlCache[query] = createElement(query)); };

var html = function (query) {
  var args = [], len = arguments.length - 1;
  while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];

  var element;

  if (isString(query)) {
    element = memoizeHTML(query).cloneNode(false);
  } else if (isNode(query)) {
    element = query.cloneNode(false);
  } else {
    throw new Error('At least one argument required');
  }

  parseArguments(element, args);

  return element;
};

html.extend = function (query) {
  var clone = memoizeHTML(query);

  return html.bind(this, clone);
};

var el = html;

var setChildren = function (parent, children) {
  if (children.length === undefined) {
    return setChildren(parent, [children]);
  }

  var parentEl = getEl(parent);
  var traverse = parentEl.firstChild;

  for (var i = 0; i < children.length; i++) {
    var child = children[i];

    if (!child) {
      continue;
    }

    var childEl = getEl(child);

    if (childEl === traverse) {
      traverse = traverse.nextSibling;
      continue;
    }

    mount(parent, child, traverse);
  }

  while (traverse) {
    var next = traverse.nextSibling;

    unmount(parent, traverse);

    traverse = next;
  }
};

var propKey = function (key) { return function (item) { return item[key]; }; };

var list = function (parent, View, key, initData) {
  return new List(parent, View, key, initData);
};

var List = function List (parent, View, key, initData) {
  this.__redom_list = true;
  this.View = View;
  this.initData = initData;
  this.views = [];
  this.el = ensureEl(parent);

  if (key != null) {
    this.lookup = {};
    this.key = isFunction(key) ? key : propKey(key);
  }
};
List.prototype.update = function update (data) {
    var this$1 = this;
    if ( data === void 0 ) data = [];

  var View = this.View;
  var key = this.key;
  var keySet = key != null;
  var initData = this.initData;
  var newViews = new Array(data.length);
  var oldViews = this.views;
  var newLookup = key && {};
  var oldLookup = key && this.lookup;

  for (var i = 0; i < data.length; i++) {
    var item = data[i];
    var view = (void 0);

    if (keySet) {
      var id = key(item);
      view = newViews[i] = oldLookup[id] || new View(initData, item, i, data);
      newLookup[id] = view;
      view.__id = id;
    } else {
      view = newViews[i] = oldViews[i] || new View(initData, item, i, data);
    }
    var el = getEl(view.el);
    el.__redom_view = view;
    view.update && view.update(item, i, data);
  }

  if (keySet) {
    for (var i$1 = 0; i$1 < oldViews.length; i$1++) {
      var id$1 = oldViews[i$1].__id;

      if (!(id$1 in newLookup)) {
        unmount(this$1, oldLookup[id$1]);
      }
    }
  }

  setChildren(this, newViews);

  if (keySet) {
    this.lookup = newLookup;
  }
  this.views = newViews;
};

List.extend = function (parent, View, key, initData) {
  return List.bind(List, parent, View, key, initData);
};

list.extend = List.extend;

var place = function (View, initData) {
  return new Place(View, initData);
};

var Place = function Place (View, initData) {
  this.el = text('');
  this._placeholder = this.el;
  this._visible = false;
  this._View = View;
  this._initData = initData;
};
Place.prototype.update = function update (visible, data) {
  if (visible) {
    if (!this._visible) {
      var View = this._View;
      var view = new View(this._initData);

      this.el = getEl(view.el);
      this._view = view;

      var parentNode = this._placeholder.parentNode;

      mount(parentNode, this.el, this._placeholder);
      unmount(parentNode, this._placeholder);
    }
    this._view.update && this._view.update(data);
  } else {
    if (this._visible) {
      var parentNode$1 = this.el.parentNode;

      mount(parentNode$1, this._placeholder, this.el);
      unmount(parentNode$1, this.el);

      this.el = this._placeholder;
    }
  }
  this._visible = visible;
};

var router = function (parent, Views, initData) {
  return new Router(parent, Views, initData);
};

var Router = function Router (parent, Views, initData) {
  this.el = ensureEl(parent);
  this.Views = Views;
  this.initData = initData;
};
Router.prototype.update = function update (route, data) {
  if (route !== this.route) {
    var Views = this.Views;
    var View = Views[route];

    this.view = View && new View(this.initData, data);
    this.route = route;

    setChildren(this.el, [ this.view ]);
  }
  this.view && this.view.update && this.view.update(data, route);
};

var SVG = 'http://www.w3.org/2000/svg';

var svgCache = {};

var memoizeSVG = function (query) { return svgCache[query] || (svgCache[query] = createElement(query, SVG)); };

var svg = function (query) {
  var args = [], len = arguments.length - 1;
  while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];

  var element;

  if (isString(query)) {
    element = memoizeSVG(query).cloneNode(false);
  } else if (isNode(query)) {
    element = query.cloneNode(false);
  } else {
    throw new Error('At least one argument required');
  }

  parseArguments(element, args);

  return element;
};

svg.extend = function (query) {
  var clone = memoizeSVG(query);

  return svg.bind(this, clone);
};

exports.html = html;
exports.el = el;
exports.list = list;
exports.List = List;
exports.mount = mount;
exports.unmount = unmount;
exports.place = place;
exports.Place = Place;
exports.router = router;
exports.Router = Router;
exports.setAttr = setAttr;
exports.setStyle = setStyle;
exports.setChildren = setChildren;
exports.svg = svg;
exports.text = text;
<commit_msg>Build test<commit_after>'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var HASH = '#'.charCodeAt(0);
var DOT = '.'.charCodeAt(0);

var createElement = function (query, ns) {
  var tag;
  var id;
  var className;

  var mode = 0;
  var start = 0;

  for (var i = 0; i <= query.length; i++) {
    var char = query.charCodeAt(i);

    if (char === HASH || char === DOT || !char) {
      if (mode === 0) {
        if (i === 0) {
          tag = 'div';
        } else if (!char) {
          tag = query;
        } else {
          tag = query.substring(start, i);
        }
      } else {
        var slice = query.substring(start, i);

        if (mode === 1) {
          id = slice;
        } else if (className) {
          className += ' ' + slice;
        } else {
          className = slice;
        }
      }

      start = i + 1;

      if (char === HASH) {
        mode = 1;
      } else {
        mode = 2;
      }
    }
  }

  var element = ns ? document.createElementNS(ns, tag) : document.createElement(tag);

  if (id) {
    element.id = id;
  }

  if (className) {
    if (ns) {
      element.setAttribute('class', className);
    } else {
      element.className = className;
    }
  }

  return element;
};

var hookNames = ['onmount', 'onunmount'];

var mount = function (parent, child, before) {
  var parentEl = getEl(parent);
  var childEl = getEl(child);

  if (child === childEl && childEl.__redom_view) {
    // try to look up the view if not provided
    child = childEl.__redom_view;
  }

  if (child !== childEl) {
    childEl.__redom_view = child;
  }

  var wasMounted = childEl.__redom_mounted;
  var oldParent = childEl.parentNode;

  if (wasMounted && (oldParent !== parentEl)) {
    doUnmount(child, childEl, oldParent);
  }

  if (before != null) {
    parentEl.insertBefore(childEl, getEl(before));
  } else {
    parentEl.appendChild(childEl);
  }

  doMount(child, childEl, parentEl, oldParent);

  return child;
};

var unmount = function (parent, child) {
  var parentEl = getEl(parent);
  var childEl = getEl(child);

  if (child === childEl && childEl.__redom_view) {
    // try to look up the view if not provided
    child = childEl.__redom_view;
  }

  doUnmount(child, childEl, parentEl);

  parentEl.removeChild(childEl);

  return child;
};

var doMount = function (child, childEl, parentEl, oldParent) {
  var hooks = childEl.__redom_lifecycle || (childEl.__redom_lifecycle = {});
  var remount = (parentEl === oldParent);
  var hooksFound = false;

  for (var i = 0; i < hookNames.length; i++) {
    var hookName = hookNames[i];

    if (!remount && (child !== childEl) && (hookName in child)) {
      hooks[hookName] = (hooks[hookName] || 0) + 1;
    }
    if (hooks[hookName]) {
      hooksFound = true;
    }
  }

  if (!hooksFound) {
    childEl.__redom_mounted = true;
    return;
  }

  var traverse = parentEl;
  var triggered = false;

  if (remount || (!triggered && (traverse && traverse.__redom_mounted))) {
    trigger(childEl, remount ? 'onremount' : 'onmount');
    triggered = true;
  }

  if (remount) {
    return;
  }

  while (traverse) {
    var parent = traverse.parentNode;
    var parentHooks = traverse.__redom_lifecycle || (traverse.__redom_lifecycle = {});

    for (var hook in hooks) {
      parentHooks[hook] = (parentHooks[hook] || 0) + hooks[hook];
    }

    if (!triggered && (traverse === document || (parent && parent.__redom_mounted))) {
      trigger(traverse, remount ? 'onremount' : 'onmount');
      triggered = true;
    }

    traverse = parent;
  }
};

var doUnmount = function (child, childEl, parentEl) {
  var hooks = childEl.__redom_lifecycle;

  if (!hooks) {
    childEl.__redom_mounted = false;
    return;
  }

  var traverse = parentEl;

  if (childEl.__redom_mounted) {
    trigger(childEl, 'onunmount');
  }

  while (traverse) {
    var parentHooks = traverse.__redom_lifecycle || (traverse.__redom_lifecycle = {});
    var hooksFound = false;

    for (var hook in hooks) {
      if (parentHooks[hook]) {
        parentHooks[hook] -= hooks[hook];
      }
      if (parentHooks[hook]) {
        hooksFound = true;
      }
    }

    if (!hooksFound) {
      traverse.__redom_lifecycle = null;
    }

    traverse = traverse.parentNode;
  }
};

var trigger = function (el, eventName) {
  if (eventName === 'onmount') {
    el.__redom_mounted = true;
  } else if (eventName === 'onunmount') {
    el.__redom_mounted = false;
  }

  var hooks = el.__redom_lifecycle;

  if (!hooks) {
    return;
  }

  var view = el.__redom_view;
  var hookCount = 0;

  view && view[eventName] && view[eventName]();

  for (var hook in hooks) {
    if (hook) {
      hookCount++;
    }
  }

  if (hookCount) {
    var traverse = el.firstChild;

    while (traverse) {
      var next = traverse.nextSibling;

      trigger(traverse, eventName);

      traverse = next;
    }
  }
};

var setStyle = function (view, arg1, arg2) {
  var el = getEl(view);

  if (arg2 !== undefined) {
    el.style[arg1] = arg2;
  } else if (isString(arg1)) {
    el.setAttribute('style', arg1);
  } else {
    for (var key in arg1) {
      setStyle(el, key, arg1[key]);
    }
  }
};

var setAttr = function (view, arg1, arg2) {
  var el = getEl(view);
  var isSVG = el instanceof window.SVGElement;

  if (arg2 !== undefined) {
    if (arg1 === 'style') {
      setStyle(el, arg2);
    } else if (isSVG && isFunction(arg2)) {
      el[arg1] = arg2;
    } else if (!isSVG && (arg1 in el || isFunction(arg2))) {
      el[arg1] = arg2;
    } else {
      el.setAttribute(arg1, arg2);
    }
  } else {
    for (var key in arg1) {
      setAttr(el, key, arg1[key]);
    }
  }
};

var text = function (str) { return document.createTextNode(str); };

var parseArguments = function (element, args) {
  for (var i = 0; i < args.length; i++) {
    var arg = args[i];

    if (arg !== 0 && !arg) {
      continue;
    }

    // support middleware
    if (typeof arg === 'function') {
      arg(element);
    } else if (isString(arg) || isNumber(arg)) {
      element.appendChild(text(arg));
    } else if (isNode(getEl(arg))) {
      mount(element, arg);
    } else if (arg.length) {
      parseArguments(element, arg);
    } else if (typeof arg === 'object') {
      setAttr(element, arg);
    }
  }
};

var ensureEl = function (parent) { return isString(parent) ? html(parent) : getEl(parent); };
var getEl = function (parent) { return (parent.nodeType && parent) || (!parent.el && parent) || getEl(parent.el); };

var isString = function (a) { return typeof a === 'string'; };
var isNumber = function (a) { return typeof a === 'number'; };
var isFunction = function (a) { return typeof a === 'function'; };

var isNode = function (a) { return a && a.nodeType; };

var htmlCache = {};

var memoizeHTML = function (query) { return htmlCache[query] || (htmlCache[query] = createElement(query)); };

var html = function (query) {
  var args = [], len = arguments.length - 1;
  while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];

  var element;

  if (isString(query)) {
    element = memoizeHTML(query).cloneNode(false);
  } else if (isNode(query)) {
    element = query.cloneNode(false);
  } else {
    throw new Error('At least one argument required');
  }

  parseArguments(element, args);

  return element;
};

html.extend = function (query) {
  var clone = memoizeHTML(query);

  return html.bind(this, clone);
};

var el = html;

var setChildren = function (parent, children) {
  if (children.length === undefined) {
    return setChildren(parent, [children]);
  }

  var parentEl = getEl(parent);
  var traverse = parentEl.firstChild;

  for (var i = 0; i < children.length; i++) {
    var child = children[i];

    if (!child) {
      continue;
    }

    var childEl = getEl(child);

    if (childEl === traverse) {
      traverse = traverse.nextSibling;
      continue;
    }

    mount(parent, child, traverse);
  }

  while (traverse) {
    var next = traverse.nextSibling;

    unmount(parent, traverse);

    traverse = next;
  }
};

var propKey = function (key) { return function (item) { return item[key]; }; };

var list = function (parent, View, key, initData) {
  return new List(parent, View, key, initData);
};

var List = function List (parent, View, key, initData) {
  this.__redom_list = true;
  this.View = View;
  this.initData = initData;
  this.views = [];
  this.el = ensureEl(parent);

  if (key != null) {
    this.lookup = {};
    this.key = isFunction(key) ? key : propKey(key);
  }
};
List.prototype.update = function update (data) {
    var this$1 = this;
    if ( data === void 0 ) data = [];

  var View = this.View;
  var key = this.key;
  var keySet = key != null;
  var initData = this.initData;
  var newViews = new Array(data.length);
  var oldViews = this.views;
  var newLookup = key && {};
  var oldLookup = key && this.lookup;

  for (var i = 0; i < data.length; i++) {
    var item = data[i];
    var view = (void 0);

    if (keySet) {
      var id = key(item);
      view = newViews[i] = oldLookup[id] || new View(initData, item, i, data);
      newLookup[id] = view;
      view.__id = id;
    } else {
      view = newViews[i] = oldViews[i] || new View(initData, item, i, data);
    }
    var el = getEl(view.el);
    el.__redom_view = view;
    view.update && view.update(item, i, data);
  }

  if (keySet) {
    for (var i$1 = 0; i$1 < oldViews.length; i$1++) {
      var id$1 = oldViews[i$1].__id;

      if (!(id$1 in newLookup)) {
        unmount(this$1, oldLookup[id$1]);
      }
    }
  }

  setChildren(this, newViews);

  if (keySet) {
    this.lookup = newLookup;
  }
  this.views = newViews;
};

List.extend = function (parent, View, key, initData) {
  return List.bind(List, parent, View, key, initData);
};

list.extend = List.extend;

var place = function (View, initData) {
  return new Place(View, initData);
};

var Place = function Place (View, initData) {
  this.el = text('');
  this._placeholder = this.el;
  this._visible = false;
  this._View = View;
  this._initData = initData;
};
Place.prototype.update = function update (visible, data) {
  if (visible) {
    if (!this._visible) {
      var placeholder = this._placeholder;
      var parentNode = placeholder.parentNode;
      var View = this._View;
      var view = new View(this._initData);

      this.el = getEl(view.el);
      this._view = view;

      mount(parentNode, this.el, placeholder);
      unmount(parentNode, placeholder);
    }
    this._view.update && this._view.update(data);
  } else {
    if (this._visible) {
      var placeholder$1 = this._placeholder;
      var parentNode$1 = this.el.parentNode;

      mount(parentNode$1, placeholder$1, this.el);
      unmount(parentNode$1, this.el);

      this.el = placeholder$1;
    }
  }
  this._visible = visible;
};

var router = function (parent, Views, initData) {
  return new Router(parent, Views, initData);
};

var Router = function Router (parent, Views, initData) {
  this.el = ensureEl(parent);
  this.Views = Views;
  this.initData = initData;
};
Router.prototype.update = function update (route, data) {
  if (route !== this.route) {
    var Views = this.Views;
    var View = Views[route];

    this.view = View && new View(this.initData, data);
    this.route = route;

    setChildren(this.el, [ this.view ]);
  }
  this.view && this.view.update && this.view.update(data, route);
};

var SVG = 'http://www.w3.org/2000/svg';

var svgCache = {};

var memoizeSVG = function (query) { return svgCache[query] || (svgCache[query] = createElement(query, SVG)); };

var svg = function (query) {
  var args = [], len = arguments.length - 1;
  while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];

  var element;

  if (isString(query)) {
    element = memoizeSVG(query).cloneNode(false);
  } else if (isNode(query)) {
    element = query.cloneNode(false);
  } else {
    throw new Error('At least one argument required');
  }

  parseArguments(element, args);

  return element;
};

svg.extend = function (query) {
  var clone = memoizeSVG(query);

  return svg.bind(this, clone);
};

exports.html = html;
exports.el = el;
exports.list = list;
exports.List = List;
exports.mount = mount;
exports.unmount = unmount;
exports.place = place;
exports.Place = Place;
exports.router = router;
exports.Router = Router;
exports.setAttr = setAttr;
exports.setStyle = setStyle;
exports.setChildren = setChildren;
exports.svg = svg;
exports.text = text;
<|endoftext|>


<commit_before>/**
 * Created by aleckim on 2018. 1. 25..
 * kecoRequester로부터 새로운 imgPath를 받아, airkorea.dust.image.controller로
 * 제공해서 hourly forecast를 받아와서 저장
 * route(controllerTown)에서 데이터를 가져가는 interface제공
 */

'use strict';

const async = require('async');
const ControllerAirkoreaDustImage = require('./airkorea.dust.image.controller');
const MsrStn = require('../models/modelMsrStnInfo');
const ArpltnHourlyForecast = require('../models/arpltn.hourly.forecast');

class AirkoreaHourlyForecastController {
    constructor(imgPaths) {
        this.imgPaths = imgPaths;
        this.airkoreaDustImageMgr;
    }

    _getMsrStn(callback) {
        MsrStn.find().lean().exec(function (err, stnList) {
            if (err) {
                return callback(err);
            }
            if (stnList.length === 0) {
                return callback(new Error("airkroea msr stn list length is 0"));
            }
            callback(null, stnList);
        });
    }

    _updateDb(obj, callback) {
        ArpltnHourlyForecast.update({stationName: obj.stationName, date: obj.date, code: obj.code},
            obj,
            {upsert:true},
            callback);
    }

    _makeArpltnHourForecast(stationName, code, pubDate, hourData) {
        return {
            stationName: stationName,
            date: new Date(hourData.date),
            code: code,
            val: hourData.val,
            dataTime: hourData.date,
            pubDate: pubDate
        };
    }

    _updateForecastList(forecastsObj, callback) {
        async.map(forecastsObj.hourly,
            (hourData, callback) => {
                var arpltnHourForecast;
                try {
                    if (hourData.val == undefined) {
                       log.warn("invalid val "+ forecastsObj.stationName+ " "+forecastsObj.code+" "+hourData.date);
                    }
                    else {
                        arpltnHourForecast = this._makeArpltnHourForecast(
                            forecastsObj.stationName,
                            forecastsObj.code,
                            forecastsObj.pubDate,
                            hourData);
                    }
                }
                catch(err) {
                    log.error(err);
                }
                if (arpltnHourForecast) {
                    this._updateDb(arpltnHourForecast, function (err) {
                        if (err)  {
                            log.error(err);
                        }
                        callback();
                    });
                }
                else {
                    callback();
                }
            }, callback);
    }

    _updateDustInfo(stn, code, callback) {
        async.waterfall([
                (callback) => {
                    this.airkoreaDustImageMgr
                        .getDustInfo(stn.geo[1],
                            stn.geo[0],
                            code.toUpperCase(),
                            'airkorea',
                            function (err, hourlyForecastObj) {
                                if(err){
                                    log.warn('Invalid geocode for dust forecast:', stn.geo[1], stn.geo[0]);
                                }
                                hourlyForecastObj.stationName = stn.stationName;
                                hourlyForecastObj.code = code;
                                callback(err, hourlyForecastObj);
                            });
                },
                (hourlyForecasts, callback) => {
                    this._updateForecastList(hourlyForecasts, callback);
                }
            ],
            callback);
    }

    _updateHourlyForecastEach(stnList, code, callback) {
        async.map(stnList,
            (stn, callback) => {
                this._updateDustInfo(stn, code, callback);
            },
            callback);
    }

    _updateHourlyForecast(stnList, callback) {
        async.mapSeries(['pm10', 'pm25'],
            (code, callback) => {
                this._updateHourlyForecastEach(stnList, code, callback);
            },
            callback);
    }

    _removeOldData() {
        var removeDate = new Date();
        removeDate.setDate(removeDate.getDate()-1);
        ArpltnHourlyForecast.remove({"date": {$lt:removeDate}}, function (err) {
            log.info('removed airpltn hourly forecast from date : ' + removeDate);
            if(err) {
                log.error(err);
            }
        });
    }

    do() {
        async.waterfall([
                (callback) => {
                    this.airkoreaDustImageMgr = new ControllerAirkoreaDustImage();
                    this.airkoreaDustImageMgr.getDustImage(this.imgPaths, callback);
                },
                (result, callback) => {
                    this._getMsrStn(callback);
                },
                (stnList, callback) => {
                    this._updateHourlyForecast(stnList, callback);
                }
            ],
            (err) => {
                if (err) {
                    log.error(err);
                }
                log.info("Finish update hourly forecast");
                delete this.airkoreaDustImageMgr;
                this._removeOldData();
            });
    }

    getForecast(stationName, callback) {
       ArpltnHourlyForecast.find({stationName: stationName}).lean().sort({date:1}).exec(function (err, forecastList) {
           callback(err, forecastList);
       });
    }
}

module.exports = AirkoreaHourlyForecastController;
<commit_msg>skip invalid geocode of station #2139 #2114<commit_after>/**
 * Created by aleckim on 2018. 1. 25..
 * kecoRequester로부터 새로운 imgPath를 받아, airkorea.dust.image.controller로
 * 제공해서 hourly forecast를 받아와서 저장
 * route(controllerTown)에서 데이터를 가져가는 interface제공
 */

'use strict';

const async = require('async');
const ControllerAirkoreaDustImage = require('./airkorea.dust.image.controller');
const MsrStn = require('../models/modelMsrStnInfo');
const ArpltnHourlyForecast = require('../models/arpltn.hourly.forecast');

class AirkoreaHourlyForecastController {
    constructor(imgPaths) {
        this.imgPaths = imgPaths;
        this.airkoreaDustImageMgr;
    }

    _getMsrStn(callback) {
        MsrStn.find().lean().exec(function (err, stnList) {
            if (err) {
                return callback(err);
            }
            if (stnList.length === 0) {
                return callback(new Error("airkroea msr stn list length is 0"));
            }
            callback(null, stnList);
        });
    }

    _updateDb(obj, callback) {
        ArpltnHourlyForecast.update({stationName: obj.stationName, date: obj.date, code: obj.code},
            obj,
            {upsert:true},
            callback);
    }

    _makeArpltnHourForecast(stationName, code, pubDate, hourData) {
        return {
            stationName: stationName,
            date: new Date(hourData.date),
            code: code,
            val: hourData.val,
            dataTime: hourData.date,
            pubDate: pubDate
        };
    }

    _updateForecastList(forecastsObj, callback) {
        async.map(forecastsObj.hourly,
            (hourData, callback) => {
                var arpltnHourForecast;
                try {
                    if (hourData.val == undefined) {
                       log.warn("invalid val "+ forecastsObj.stationName+ " "+forecastsObj.code+" "+hourData.date);
                    }
                    else {
                        arpltnHourForecast = this._makeArpltnHourForecast(
                            forecastsObj.stationName,
                            forecastsObj.code,
                            forecastsObj.pubDate,
                            hourData);
                    }
                }
                catch(err) {
                    log.error(err);
                }
                if (arpltnHourForecast) {
                    this._updateDb(arpltnHourForecast, function (err) {
                        if (err)  {
                            log.error(err);
                        }
                        callback();
                    });
                }
                else {
                    callback();
                }
            }, callback);
    }

    /**
     * 특정 Stn의 예보를 가지고 와서 디비에 저장한다.
     * @param stn
     * @param code
     * @param callback
     * @private
     */
    _updateDustInfo(stn, code, callback) {
        async.waterfall([
                (callback) => {
                    this.airkoreaDustImageMgr
                        .getDustInfo(stn.geo[1],
                            stn.geo[0],
                            code.toUpperCase(),
                            'airkorea',
                            function (err, hourlyForecastObj) {
                                if(err){
                                    log.warn('Invalid geocode for dust forecast:', stn.geo[1], stn.geo[0]);
                                    return callback();
                                }
                                hourlyForecastObj.stationName = stn.stationName;
                                hourlyForecastObj.code = code;
                                callback(err, hourlyForecastObj);
                            });
                },
                (hourlyForecasts, callback) => {
                    if (hourlyForecasts == undefined) {
                        log.warn('pass update forecast list hourlyForecasts is undefined');
                        return callback();
                    }
                    this._updateForecastList(hourlyForecasts, callback);
                }
            ],
            callback);
    }

    /**
     * code(pm10, pm25, o3) 별로 모든 stn의 예보를 업데이트 요청한다.
     * @param stnList
     * @param code
     * @param callback
     * @private
     */
    _updateHourlyForecastEach(stnList, code, callback) {
        async.map(stnList,
            (stn, callback) => {
                this._updateDustInfo(stn, code, callback);
            },
            callback);
    }

    _updateHourlyForecast(stnList, callback) {
        async.mapSeries(['pm10', 'pm25'],
            (code, callback) => {
                this._updateHourlyForecastEach(stnList, code, callback);
            },
            callback);
    }

    _removeOldData() {
        var removeDate = new Date();
        removeDate.setDate(removeDate.getDate()-1);
        ArpltnHourlyForecast.remove({"date": {$lt:removeDate}}, function (err) {
            log.info('removed airpltn hourly forecast from date : ' + removeDate);
            if(err) {
                log.error(err);
            }
        });
    }

    do() {
        async.waterfall([
                (callback) => {
                    this.airkoreaDustImageMgr = new ControllerAirkoreaDustImage();
                    this.airkoreaDustImageMgr.getDustImage(this.imgPaths, callback);
                },
                (result, callback) => {
                    this._getMsrStn(callback);
                },
                (stnList, callback) => {
                    this._updateHourlyForecast(stnList, callback);
                }
            ],
            (err) => {
                if (err) {
                    log.error(err);
                }
                log.info("Finish update hourly forecast");
                delete this.airkoreaDustImageMgr;
                this._removeOldData();
            });
    }

    getForecast(stationName, callback) {
       ArpltnHourlyForecast.find({stationName: stationName}).lean().sort({date:1}).exec(function (err, forecastList) {
           callback(err, forecastList);
       });
    }
}

module.exports = AirkoreaHourlyForecastController;
<|endoftext|>


<commit_before>export default {
  today: 'Dnes',
  now: 'Nyní',
  backToToday: 'Zpět na dnešek',
  ok: 'Ok',
  clear: 'Vymazat',
  month: 'Měsíc',
  year: 'Rok',
  timeSelect: 'Vybrat čas',
  dateSelect: 'Vybrat datum',
  monthSelect: 'Vyberte měsíc',
  yearSelect: 'Vyberte rok',
  decadeSelect: 'Vyberte dekádu',
  yearFormat: 'YYYY',
  dateFormat: 'D.M.YYYY',
  dayFormat: 'D',
  dateTimeFormat: 'D.M.YYYY HH:mm:ss',
  monthFormat: 'MMMM',
  monthBeforeYear: true,
  previousMonth: 'Předchozí měsíc (PageUp)',
  nextMonth: 'Následující (PageDown)',
  previousYear: 'Předchozí rok (Control + left)',
  nextYear: 'Následující rok (Control + right)',
  previousDecade: 'Předchozí dekáda',
  nextDecade: 'Následující dekáda',
  previousCentury: 'Předchozí století',
  nextCentury: 'Následující století',
}
<commit_msg>Fix lint errors<commit_after>export default {
  today: 'Dnes',
  now: 'Nyní',
  backToToday: 'Zpět na dnešek',
  ok: 'Ok',
  clear: 'Vymazat',
  month: 'Měsíc',
  year: 'Rok',
  timeSelect: 'Vybrat čas',
  dateSelect: 'Vybrat datum',
  monthSelect: 'Vyberte měsíc',
  yearSelect: 'Vyberte rok',
  decadeSelect: 'Vyberte dekádu',
  yearFormat: 'YYYY',
  dateFormat: 'D.M.YYYY',
  dayFormat: 'D',
  dateTimeFormat: 'D.M.YYYY HH:mm:ss',
  monthFormat: 'MMMM',
  monthBeforeYear: true,
  previousMonth: 'Předchozí měsíc (PageUp)',
  nextMonth: 'Následující (PageDown)',
  previousYear: 'Předchozí rok (Control + left)',
  nextYear: 'Následující rok (Control + right)',
  previousDecade: 'Předchozí dekáda',
  nextDecade: 'Následující dekáda',
  previousCentury: 'Předchozí století',
  nextCentury: 'Následující století',
};
<|endoftext|>


<commit_before>describe("EasyStar.js", function() {

  beforeEach(function() { });

  it("It should find a path successfully with corner cutting enabled.", function(done) {
    var easyStar = new EasyStar.js();
    easyStar.enableDiagonals();
    var map = [[1,0,0,0,0],
               [0,1,0,0,0],
               [0,0,1,0,0],
               [0,0,0,1,0],
               [0,0,0,0,1]];

    easyStar.setGrid(map);

    easyStar.enableCornerCutting();

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(0,0,4,4,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        expect(path.length).toEqual(5);
        expect(path[0].x).toEqual(0);
        expect(path[0].y).toEqual(0);
        expect(path[3].x).toEqual(3);
        expect(path[3].y).toEqual(3);
        done()
    }
  });

  it("It should fail to find a path successfully with corner cutting disabled.", function(done) {
    var easyStar = new EasyStar.js();
    easyStar.enableDiagonals();
    var map = [[1,0,0,0,0],
               [0,1,0,0,0],
               [0,0,1,0,0],
               [0,0,0,1,0],
               [0,0,0,0,1]];

    easyStar.setGrid(map);

    easyStar.disableCornerCutting();

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(0,0,4,4,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).toBeNull();
        done();
    }
  });

  it("It should find a path successfully.", function(done) {
    var easyStar = new EasyStar.js();
    var map = [[1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(1,2,3,2,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        expect(path.length).toEqual(5);
        expect(path[0].x).toEqual(1);
        expect(path[0].y).toEqual(2);
        expect(path[2].x).toEqual(2);
        expect(path[2].y).toEqual(3);
        done();
    }
  });

  it("It should be able to cancel a path.", function(done) {
    var easyStar = new EasyStar.js();
    var map = [[1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.setAcceptableTiles([1]);

    var id = easyStar.findPath(1,2,3,2,onPathFound);

    easyStar.cancelPath(id);

    easyStar.calculate();

    function onPathFound(path) {
        fail("path wasn't cancelled");
    }

    setTimeout(done, 0);
  });

  it("Paths should have different IDs.", function() {
    var easyStar = new EasyStar.js();
    var map = [[1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.setAcceptableTiles([1]);

    var id1 = easyStar.findPath(1,2,3,2,onPathFound);
    var id2 = easyStar.findPath(3,2,1,2,onPathFound);
    expect(id1).toBeGreaterThan(0);
    expect(id2).toBeGreaterThan(0);
    expect(id1).not.toEqual(id2);

    function onPathFound(path) {
    }
  });

  it("It should be able to avoid a separate point successfully.", function(done) {
    var easyStar = new EasyStar.js();
    var map = [[1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.avoidAdditionalPoint(2,3);

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(1,2,3,2,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        expect(path.length).toEqual(7);
        expect(path[0].x).toEqual(1);
        expect(path[0].y).toEqual(2);
        expect(path[2].x).toEqual(1);
        expect(path[2].y).toEqual(4);
        done();
    }
  });

  it("It should work with diagonals", function(done) {
    var easyStar = new EasyStar.js();
    easyStar.enableDiagonals();
    var map = [[1,1,1,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(0,0,4,4,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        expect(path.length).toEqual(5);
        expect(path[0].x).toEqual(0);
        expect(path[0].y).toEqual(0);
        expect(path[1].x).toEqual(1);
        expect(path[1].y).toEqual(1);
        expect(path[2].x).toEqual(2);
        expect(path[2].y).toEqual(2);
        expect(path[3].x).toEqual(3);
        expect(path[3].y).toEqual(3);
        expect(path[4].x).toEqual(4);
        expect(path[4].y).toEqual(4);
        done();
    }
  });

  it("It should move in a straight line with diagonals", function(done) {
    var easyStar = new EasyStar.js();
    easyStar.enableDiagonals();
    var map = [[1,1,1,1,1,1,1,1,1,1],
               [1,1,0,1,1,1,1,0,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.enableDiagonals();

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(0,0,9,0,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        for (var i = 0; i < path.length; i++) {
            expect(path[i].y).toEqual(0);
        }
        done();
    }
  });

  it("It should return empty path when start and end are the same tile.", function(done) {
    var easyStar = new EasyStar.js();
    var map = [[1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(1,2,1,2,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        expect(path.length).toEqual(0);
        done();
    }
  });

  it("It should prefer straight paths when possible", function(done) {
    var easyStar = new EasyStar.js();
    easyStar.setAcceptableTiles([0]);
    easyStar.enableDiagonals();
    easyStar.setGrid([
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]
    ]);

    easyStar.findPath(0, 1, 2, 1, function(path){
        expect(path).not.toBeNull();
        expect(path[1].x).toEqual(1);
        expect(path[1].y).toEqual(1);
        done();
    });

    easyStar.calculate();
  });

  it("It should prefer diagonal paths when they are faster", function(done) {
    var easyStar = new EasyStar.js();
    var grid = [];
    for (var i = 0; i < 20; i++) {
        grid[i] = [];
        for (var j = 0; j < 20; j++) {
          grid[i][j] = 0;
        }
    }
    easyStar.setGrid(grid);
    easyStar.setAcceptableTiles([0]);
    easyStar.enableDiagonals();

    easyStar.findPath(4, 4, 2, 2, function(path){
        expect(path).not.toBeNull();
        expect(path.length).toEqual(3);
        expect(path[1].x).toEqual(3);
        expect(path[1].y).toEqual(3);
        done();
    });

    easyStar.calculate();
  })

  it("It should handle tiles with a directional condition", function (done) {
      var easyStar = new EasyStar.js();
      var grid = [
          [0, 1, 0],
          [0, 0, 0],
          [0, 0, 0],
      ];
      easyStar.setGrid(grid);
      easyStar.setAcceptableTiles([0]);
      easyStar.setDirectionalCondition(1,1, [EasyStar.RIGHT, EasyStar.TOP_RIGHT]);
      easyStar.setDirectionalCondition(1,2, [EasyStar.LEFT]);

      easyStar.findPath(0, 0, 2, 0, function (path) {
          expect(path).not.toBeNull();
          expect(path[3]).toEqual({ x: 1, y: 2})
          expect(path.length).toEqual(7);
          done();
      });

      easyStar.calculate();
  })
});
<commit_msg>Extend the directional-condition test case<commit_after>describe("EasyStar.js", function() {

  beforeEach(function() { });

  it("It should find a path successfully with corner cutting enabled.", function(done) {
    var easyStar = new EasyStar.js();
    easyStar.enableDiagonals();
    var map = [[1,0,0,0,0],
               [0,1,0,0,0],
               [0,0,1,0,0],
               [0,0,0,1,0],
               [0,0,0,0,1]];

    easyStar.setGrid(map);

    easyStar.enableCornerCutting();

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(0,0,4,4,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        expect(path.length).toEqual(5);
        expect(path[0].x).toEqual(0);
        expect(path[0].y).toEqual(0);
        expect(path[3].x).toEqual(3);
        expect(path[3].y).toEqual(3);
        done()
    }
  });

  it("It should fail to find a path successfully with corner cutting disabled.", function(done) {
    var easyStar = new EasyStar.js();
    easyStar.enableDiagonals();
    var map = [[1,0,0,0,0],
               [0,1,0,0,0],
               [0,0,1,0,0],
               [0,0,0,1,0],
               [0,0,0,0,1]];

    easyStar.setGrid(map);

    easyStar.disableCornerCutting();

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(0,0,4,4,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).toBeNull();
        done();
    }
  });

  it("It should find a path successfully.", function(done) {
    var easyStar = new EasyStar.js();
    var map = [[1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(1,2,3,2,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        expect(path.length).toEqual(5);
        expect(path[0].x).toEqual(1);
        expect(path[0].y).toEqual(2);
        expect(path[2].x).toEqual(2);
        expect(path[2].y).toEqual(3);
        done();
    }
  });

  it("It should be able to cancel a path.", function(done) {
    var easyStar = new EasyStar.js();
    var map = [[1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.setAcceptableTiles([1]);

    var id = easyStar.findPath(1,2,3,2,onPathFound);

    easyStar.cancelPath(id);

    easyStar.calculate();

    function onPathFound(path) {
        fail("path wasn't cancelled");
    }

    setTimeout(done, 0);
  });

  it("Paths should have different IDs.", function() {
    var easyStar = new EasyStar.js();
    var map = [[1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.setAcceptableTiles([1]);

    var id1 = easyStar.findPath(1,2,3,2,onPathFound);
    var id2 = easyStar.findPath(3,2,1,2,onPathFound);
    expect(id1).toBeGreaterThan(0);
    expect(id2).toBeGreaterThan(0);
    expect(id1).not.toEqual(id2);

    function onPathFound(path) {
    }
  });

  it("It should be able to avoid a separate point successfully.", function(done) {
    var easyStar = new EasyStar.js();
    var map = [[1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.avoidAdditionalPoint(2,3);

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(1,2,3,2,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        expect(path.length).toEqual(7);
        expect(path[0].x).toEqual(1);
        expect(path[0].y).toEqual(2);
        expect(path[2].x).toEqual(1);
        expect(path[2].y).toEqual(4);
        done();
    }
  });

  it("It should work with diagonals", function(done) {
    var easyStar = new EasyStar.js();
    easyStar.enableDiagonals();
    var map = [[1,1,1,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(0,0,4,4,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        expect(path.length).toEqual(5);
        expect(path[0].x).toEqual(0);
        expect(path[0].y).toEqual(0);
        expect(path[1].x).toEqual(1);
        expect(path[1].y).toEqual(1);
        expect(path[2].x).toEqual(2);
        expect(path[2].y).toEqual(2);
        expect(path[3].x).toEqual(3);
        expect(path[3].y).toEqual(3);
        expect(path[4].x).toEqual(4);
        expect(path[4].y).toEqual(4);
        done();
    }
  });

  it("It should move in a straight line with diagonals", function(done) {
    var easyStar = new EasyStar.js();
    easyStar.enableDiagonals();
    var map = [[1,1,1,1,1,1,1,1,1,1],
               [1,1,0,1,1,1,1,0,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1],
               [1,1,1,1,1,1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.enableDiagonals();

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(0,0,9,0,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        for (var i = 0; i < path.length; i++) {
            expect(path[i].y).toEqual(0);
        }
        done();
    }
  });

  it("It should return empty path when start and end are the same tile.", function(done) {
    var easyStar = new EasyStar.js();
    var map = [[1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,0,1,1],
               [1,1,1,1,1],
               [1,1,1,1,1]];

    easyStar.setGrid(map);

    easyStar.setAcceptableTiles([1]);

    easyStar.findPath(1,2,1,2,onPathFound);

    easyStar.calculate();

    function onPathFound(path) {
        expect(path).not.toBeNull();
        expect(path.length).toEqual(0);
        done();
    }
  });

  it("It should prefer straight paths when possible", function(done) {
    var easyStar = new EasyStar.js();
    easyStar.setAcceptableTiles([0]);
    easyStar.enableDiagonals();
    easyStar.setGrid([
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]
    ]);

    easyStar.findPath(0, 1, 2, 1, function(path){
        expect(path).not.toBeNull();
        expect(path[1].x).toEqual(1);
        expect(path[1].y).toEqual(1);
        done();
    });

    easyStar.calculate();
  });

  it("It should prefer diagonal paths when they are faster", function(done) {
    var easyStar = new EasyStar.js();
    var grid = [];
    for (var i = 0; i < 20; i++) {
        grid[i] = [];
        for (var j = 0; j < 20; j++) {
          grid[i][j] = 0;
        }
    }
    easyStar.setGrid(grid);
    easyStar.setAcceptableTiles([0]);
    easyStar.enableDiagonals();

    easyStar.findPath(4, 4, 2, 2, function(path){
        expect(path).not.toBeNull();
        expect(path.length).toEqual(3);
        expect(path[1].x).toEqual(3);
        expect(path[1].y).toEqual(3);
        done();
    });

    easyStar.calculate();
  })

  it("It should handle tiles with a directional condition", function (done) {
      var easyStar = new EasyStar.js();
      var grid = [
          [0, 1, 0],
          [0, 0, 0],
          [0, 0, 0],
      ];
      easyStar.setGrid(grid);
      easyStar.enableDiagonals();
      easyStar.setAcceptableTiles([0]);
      easyStar.setDirectionalCondition(2, 1, [EasyStar.TOP]);
      easyStar.setDirectionalCondition(1, 2, [EasyStar.TOP_RIGHT]);
      easyStar.setDirectionalCondition(2, 2, [EasyStar.LEFT]);
      easyStar.setDirectionalCondition(1, 1, [EasyStar.BOTTOM_RIGHT]);
      easyStar.setDirectionalCondition(0, 1, [EasyStar.RIGHT]);
      easyStar.setDirectionalCondition(0, 0, [EasyStar.BOTTOM]);

      easyStar.findPath(2, 0, 0, 0, function (path) {
          expect(path).not.toBeNull();
          expect(path.length).toEqual(7);
          expect(path[3]).toEqual({ x: 2, y: 2})
          done();
      });

      easyStar.calculate();
  })
});
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var allocCluster = require('./lib/alloc-cluster');
var TChannel = require('../channel');
var RelayHandler = require('../relay_handler');
var RelayNetwork = require('./lib/relay_network');
var CountedReady = require('ready-signal/counted');

allocCluster.test('send relay requests', {
    numPeers: 2
}, function t(cluster, assert) {
    var one = cluster.channels[0];
    var two = cluster.channels[1];

    var oneToTwo = one.makeSubChannel({
        serviceName: 'two',
        peers: [two.hostPort]
    });
    oneToTwo.handler = new RelayHandler(oneToTwo);

    var twoSvc = two.makeSubChannel({
        serviceName: 'two'
    });
    twoSvc.register('echo', echo);

    var client = TChannel({
        logger: one.logger,
        timeoutFuzz: 0
    });
    var twoClient = client.makeSubChannel({
        serviceName: 'two',
        peers: [one.hostPort],
        requestDefaults: {
            serviceName: 'two',
            headers: {
                as: 'raw',
                cn: 'wat'
            }
        }
    });

    twoClient.request({
        hasNoParent: true
    }).send('echo', 'foo', 'bar', function done(err, res, arg2, arg3) {
        assert.ifError(err, 'no unexpected error');
        assert.equal(String(arg2), 'foo', 'expected arg2');
        assert.equal(String(arg3), 'bar', 'expected arg3');

        client.close();
        assert.end();
    });
});

allocCluster.test('send relay with tiny timeout', {
    numPeers: 2
}, function t(cluster, assert) {
    var one = cluster.channels[0];
    var two = cluster.channels[1];

    var oneToTwo = one.makeSubChannel({
        serviceName: 'two',
        peers: [two.hostPort]
    });
    oneToTwo.handler = new RelayHandler(oneToTwo);

    var twoSvc = two.makeSubChannel({
        serviceName: 'two'
    });
    twoSvc.register('echo', echo);

    var client = TChannel({
        logger: one.logger,
        timeoutFuzz: 0
    });
    var twoClient = client.makeSubChannel({
        serviceName: 'two',
        peers: [one.hostPort],
        requestDefaults: {
            serviceName: 'two',
            headers: {
                as: 'raw',
                cn: 'wat'
            }
        }
    });

    twoClient.waitForIdentified({
        host: one.hostPort
    }, onIdentified);

    function onIdentified(err1) {
        assert.ifError(err1);

        twoClient.request({
            hasNoParent: true,
            host: one.hostPort,
            timeout: 5
        }).send('echo', 'foo', 'bar', function done(err2, res, arg2, arg3) {
            assert.ifError(err2, 'no unexpected error');
            assert.equal(String(arg2), 'foo', 'expected arg2');
            assert.equal(String(arg3), 'bar', 'expected arg3');

            client.close();
            assert.end();
        });
    }
});

allocCluster.test('relay respects ttl', {
    numPeers: 3
}, function t(cluster, assert) {
    var relay = cluster.channels[0];
    var source = cluster.channels[1];
    var dest = cluster.channels[2];

    var relayChan = relay.makeSubChannel({
        serviceName: 'dest',
        peers: [dest.hostPort]
    });
    relayChan.handler = new RelayHandler(relayChan);

    var destChan = dest.makeSubChannel({
        serviceName: 'dest'
    });
    destChan.register('echoTTL', function echoTTL(req, res) {
        res.headers.as = 'raw';
        res.sendOk(null, String(req.timeout));
    });

    var sourceChan = source.makeSubChannel({
        serviceName: 'dest',
        peers: [relay.hostPort],
        requestDefaults: {
            headers: {
                as: 'raw',
                cn: 'wat'
            }
        }
    });

    sourceChan.request({
        serviceName: 'dest',
        hasNoParent: true,
        timeout: 250
    }).send('echoTTL', null, null, onResponse);

    function onResponse(err, res, arg2, arg3) {
        assert.ifError(err);
        assert.ok(res.ok);

        var ttl = Number(String(arg3));
        assert.ok(ttl >= 240 && ttl <= 250);

        assert.end();
    }
});

allocCluster.test('relay an error frame', {
    numPeers: 4
}, function t(cluster, assert) {
    cluster.logger.whitelist('warn', 'forwarding error frame');

    var one = cluster.channels[0];
    var two = cluster.channels[1];
    var three = cluster.channels[2];
    var four = cluster.channels[3];

    var oneToTwo = one.makeSubChannel({
        serviceName: 'two',
        peers: [two.hostPort, three.hostPort]
    });
    oneToTwo.handler = new RelayHandler(oneToTwo);
    var fourToTwo = four.makeSubChannel({
        serviceName: 'two',
        peers: [two.hostPort, three.hostPort]
    });
    fourToTwo.handler = new RelayHandler(fourToTwo);

    var twoSvc2 = three.makeSubChannel({
        serviceName: 'two'
    });
    twoSvc2.register('decline', declineError);

    var twoSvc = two.makeSubChannel({
        serviceName: 'two'
    });
    twoSvc.register('decline', declineError);

    var client = TChannel({
        logger: one.logger,
        timeoutFuzz: 0
    });
    var twoClient = client.makeSubChannel({
        serviceName: 'two',
        peers: [one.hostPort, four.hostPort],
        requestDefaults: {
            serviceName: 'two',
            headers: {
                as: 'raw',
                cn: 'wat'
            }
        }
    });

    twoClient.request({
        hasNoParent: true,
        headers: {
            as: 'raw',
            cn: 'wat'
        }
    }).send('decline', 'foo', 'bar', function done(err, res, arg2, arg3) {
        assert.equal(err.type, 'tchannel.declined', 'expected declined error');

        assert.ok(cluster.logger.items().length >= 1);
        client.close();
        assert.end();
    });

    function declineError(req, res, arg2, arg3) {
        res.sendError('Declined', 'lul');
    }
});

function echo(req, res, arg2, arg3) {
    res.headers.as = 'raw';
    res.sendOk(arg2, arg3);
}

RelayNetwork.test('relay through a network', {
    serviceNames: ['alice', 'bob'],
    numInstancesPerService: 1,
    kValue: 1,
    numRelays: 2
}, function t(network, assert) {
    network.forEachSubChannel(function register(c, service, index) {
        c.register('ping', function ping(req, res) {
            res.headers.as = 'raw';
            res.sendOk('' + index, service);
        });
    });

    network.subChannelsByName.alice[0].request({
        hasNoParent: true,
        serviceName: 'bob',
        headers: {
            cn: 'alice',
            as: 'raw'
        }
    }).send('ping', 'foo', 'bar', function onResponse(err, res, arg2, arg3) {
        if (err) {
            return assert.end(err);
        }
        assert.equal(arg3.toString(), 'bob',
            'response relayed to and from requested service');
        assert.end();
    });
});

RelayNetwork.test('relay respects relayFlags', {
    serviceNames: ['alice', 'bob'],
    numInstancesPerService: 3,
    kValue: 1,
    numRelays: 2
}, function t(network, assert) {
    network.cluster.logger.whitelist('warn', 'forwarding error frame');

    var counters = {
        alice: 0,
        bob: 0
    };

    network.forEachSubChannel(function register(c, service) {
        c.register('ping', function ping(req, res) {
            counters[service]++;
            res.sendError('UnexpectedError', 'oops');
        });
    });

    network.subChannelsByName.alice[0].request({
        hasNoParent: true,
        serviceName: 'bob',
        headers: {
            cn: 'alice',
            as: 'raw'
        },
        retryFlags: {
            never: true
        }
    }).send('ping', 'foo', 'bar', onResponse);

    function onResponse(err, res, arg2, arg3) {
        assert.ok(err);
        assert.equal(err.message, 'oops');

        assert.equal(counters.alice, 0);
        assert.equal(counters.bob, 1);

        assert.end();
    }
});

RelayNetwork.test('relay network changes dont break', {
    serviceNames: ['alice', 'bob'],
    numInstancesPerService: 1,
    kValue: 1,
    numRelays: 2
}, function t(network, assert) {
    network.cluster.logger.whitelist('info', 'Changing to forward node');

    var aliceHosts = network.topology.alice;
    var bobHosts = network.topology.bob;
    network.topology.bob = aliceHosts;
    network.topology.alice = bobHosts;

    var ready = CountedReady(2);

    network.relayChannels[0].handler.roleTransitionEvent
        .on(function forwardChange(stuff) {
            assert.equals(stuff.newMode, 'forward');
            ready.signal();
        });

    network.relayChannels[1].handler.roleTransitionEvent
        .on(function forwardChange(stuff) {
            assert.equals(stuff.newMode, 'forward');
            ready.signal();
        });

    network.egressNodesForRelay[0].membershipChangedEvent.emit();
    network.egressNodesForRelay[1].membershipChangedEvent.emit();

    ready(assert.end);
});
<commit_msg>bump timeout to avoid flappy flap<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var allocCluster = require('./lib/alloc-cluster');
var TChannel = require('../channel');
var RelayHandler = require('../relay_handler');
var RelayNetwork = require('./lib/relay_network');
var CountedReady = require('ready-signal/counted');

allocCluster.test('send relay requests', {
    numPeers: 2
}, function t(cluster, assert) {
    var one = cluster.channels[0];
    var two = cluster.channels[1];

    var oneToTwo = one.makeSubChannel({
        serviceName: 'two',
        peers: [two.hostPort]
    });
    oneToTwo.handler = new RelayHandler(oneToTwo);

    var twoSvc = two.makeSubChannel({
        serviceName: 'two'
    });
    twoSvc.register('echo', echo);

    var client = TChannel({
        logger: one.logger,
        timeoutFuzz: 0
    });
    var twoClient = client.makeSubChannel({
        serviceName: 'two',
        peers: [one.hostPort],
        requestDefaults: {
            serviceName: 'two',
            headers: {
                as: 'raw',
                cn: 'wat'
            }
        }
    });

    twoClient.request({
        hasNoParent: true
    }).send('echo', 'foo', 'bar', function done(err, res, arg2, arg3) {
        assert.ifError(err, 'no unexpected error');
        assert.equal(String(arg2), 'foo', 'expected arg2');
        assert.equal(String(arg3), 'bar', 'expected arg3');

        client.close();
        assert.end();
    });
});

allocCluster.test('send relay with tiny timeout', {
    numPeers: 2
}, function t(cluster, assert) {
    var one = cluster.channels[0];
    var two = cluster.channels[1];

    var oneToTwo = one.makeSubChannel({
        serviceName: 'two',
        peers: [two.hostPort]
    });
    oneToTwo.handler = new RelayHandler(oneToTwo);

    var twoSvc = two.makeSubChannel({
        serviceName: 'two'
    });
    twoSvc.register('echo', echo);

    var client = TChannel({
        logger: one.logger,
        timeoutFuzz: 0
    });
    var twoClient = client.makeSubChannel({
        serviceName: 'two',
        peers: [one.hostPort],
        requestDefaults: {
            serviceName: 'two',
            headers: {
                as: 'raw',
                cn: 'wat'
            }
        }
    });

    twoClient.waitForIdentified({
        host: one.hostPort
    }, onIdentified);

    function onIdentified(err1) {
        assert.ifError(err1);

        twoClient.request({
            hasNoParent: true,
            host: one.hostPort,
            timeout: 15
        }).send('echo', 'foo', 'bar', function done(err2, res, arg2, arg3) {
            assert.ifError(err2, 'no unexpected error');
            assert.equal(String(arg2), 'foo', 'expected arg2');
            assert.equal(String(arg3), 'bar', 'expected arg3');

            client.close();
            assert.end();
        });
    }
});

allocCluster.test('relay respects ttl', {
    numPeers: 3
}, function t(cluster, assert) {
    var relay = cluster.channels[0];
    var source = cluster.channels[1];
    var dest = cluster.channels[2];

    var relayChan = relay.makeSubChannel({
        serviceName: 'dest',
        peers: [dest.hostPort]
    });
    relayChan.handler = new RelayHandler(relayChan);

    var destChan = dest.makeSubChannel({
        serviceName: 'dest'
    });
    destChan.register('echoTTL', function echoTTL(req, res) {
        res.headers.as = 'raw';
        res.sendOk(null, String(req.timeout));
    });

    var sourceChan = source.makeSubChannel({
        serviceName: 'dest',
        peers: [relay.hostPort],
        requestDefaults: {
            headers: {
                as: 'raw',
                cn: 'wat'
            }
        }
    });

    sourceChan.request({
        serviceName: 'dest',
        hasNoParent: true,
        timeout: 250
    }).send('echoTTL', null, null, onResponse);

    function onResponse(err, res, arg2, arg3) {
        assert.ifError(err);
        assert.ok(res.ok);

        var ttl = Number(String(arg3));
        assert.ok(ttl >= 240 && ttl <= 250);

        assert.end();
    }
});

allocCluster.test('relay an error frame', {
    numPeers: 4
}, function t(cluster, assert) {
    cluster.logger.whitelist('warn', 'forwarding error frame');

    var one = cluster.channels[0];
    var two = cluster.channels[1];
    var three = cluster.channels[2];
    var four = cluster.channels[3];

    var oneToTwo = one.makeSubChannel({
        serviceName: 'two',
        peers: [two.hostPort, three.hostPort]
    });
    oneToTwo.handler = new RelayHandler(oneToTwo);
    var fourToTwo = four.makeSubChannel({
        serviceName: 'two',
        peers: [two.hostPort, three.hostPort]
    });
    fourToTwo.handler = new RelayHandler(fourToTwo);

    var twoSvc2 = three.makeSubChannel({
        serviceName: 'two'
    });
    twoSvc2.register('decline', declineError);

    var twoSvc = two.makeSubChannel({
        serviceName: 'two'
    });
    twoSvc.register('decline', declineError);

    var client = TChannel({
        logger: one.logger,
        timeoutFuzz: 0
    });
    var twoClient = client.makeSubChannel({
        serviceName: 'two',
        peers: [one.hostPort, four.hostPort],
        requestDefaults: {
            serviceName: 'two',
            headers: {
                as: 'raw',
                cn: 'wat'
            }
        }
    });

    twoClient.request({
        hasNoParent: true,
        headers: {
            as: 'raw',
            cn: 'wat'
        }
    }).send('decline', 'foo', 'bar', function done(err, res, arg2, arg3) {
        assert.equal(err.type, 'tchannel.declined', 'expected declined error');

        assert.ok(cluster.logger.items().length >= 1);
        client.close();
        assert.end();
    });

    function declineError(req, res, arg2, arg3) {
        res.sendError('Declined', 'lul');
    }
});

function echo(req, res, arg2, arg3) {
    res.headers.as = 'raw';
    res.sendOk(arg2, arg3);
}

RelayNetwork.test('relay through a network', {
    serviceNames: ['alice', 'bob'],
    numInstancesPerService: 1,
    kValue: 1,
    numRelays: 2
}, function t(network, assert) {
    network.forEachSubChannel(function register(c, service, index) {
        c.register('ping', function ping(req, res) {
            res.headers.as = 'raw';
            res.sendOk('' + index, service);
        });
    });

    network.subChannelsByName.alice[0].request({
        hasNoParent: true,
        serviceName: 'bob',
        headers: {
            cn: 'alice',
            as: 'raw'
        }
    }).send('ping', 'foo', 'bar', function onResponse(err, res, arg2, arg3) {
        if (err) {
            return assert.end(err);
        }
        assert.equal(arg3.toString(), 'bob',
            'response relayed to and from requested service');
        assert.end();
    });
});

RelayNetwork.test('relay respects relayFlags', {
    serviceNames: ['alice', 'bob'],
    numInstancesPerService: 3,
    kValue: 1,
    numRelays: 2
}, function t(network, assert) {
    network.cluster.logger.whitelist('warn', 'forwarding error frame');

    var counters = {
        alice: 0,
        bob: 0
    };

    network.forEachSubChannel(function register(c, service) {
        c.register('ping', function ping(req, res) {
            counters[service]++;
            res.sendError('UnexpectedError', 'oops');
        });
    });

    network.subChannelsByName.alice[0].request({
        hasNoParent: true,
        serviceName: 'bob',
        headers: {
            cn: 'alice',
            as: 'raw'
        },
        retryFlags: {
            never: true
        }
    }).send('ping', 'foo', 'bar', onResponse);

    function onResponse(err, res, arg2, arg3) {
        assert.ok(err);
        assert.equal(err.message, 'oops');

        assert.equal(counters.alice, 0);
        assert.equal(counters.bob, 1);

        assert.end();
    }
});

RelayNetwork.test('relay network changes dont break', {
    serviceNames: ['alice', 'bob'],
    numInstancesPerService: 1,
    kValue: 1,
    numRelays: 2
}, function t(network, assert) {
    network.cluster.logger.whitelist('info', 'Changing to forward node');

    var aliceHosts = network.topology.alice;
    var bobHosts = network.topology.bob;
    network.topology.bob = aliceHosts;
    network.topology.alice = bobHosts;

    var ready = CountedReady(2);

    network.relayChannels[0].handler.roleTransitionEvent
        .on(function forwardChange(stuff) {
            assert.equals(stuff.newMode, 'forward');
            ready.signal();
        });

    network.relayChannels[1].handler.roleTransitionEvent
        .on(function forwardChange(stuff) {
            assert.equals(stuff.newMode, 'forward');
            ready.signal();
        });

    network.egressNodesForRelay[0].membershipChangedEvent.emit();
    network.egressNodesForRelay[1].membershipChangedEvent.emit();

    ready(assert.end);
});
<|endoftext|>


<commit_before>const AWS = require('aws-sdk');
const db = require('../../../../models');
const AmazonEmail = require('./amazon');
const { wrapLink, insertUnsubscribeLink, insertTrackingPixel } = require('./analytics');

module.exports = (req, res) => {
  const { testEmail, campaignId } = req.body;
  const userId = req.user.id;

  let campaign = {}; // eslint-disable-line
  let amazonSettings = {}; // eslint-disable-line

  const campaignBelongsToUser = new Promise((resolve, reject) => {
    return db.campaign.findOne({
      where: {
        id: campaignId,
        userId: userId
      }
    }).then(campaignInstance => {
      if (!campaignInstance) {
        reject();
        res.status(401).send();
      } else {
        const campaignObject = campaignInstance.get({ plain:true });
        const listId = campaignObject.listId;
        const {
          fromName,
          fromEmail,
          emailSubject,
          emailBody,
          type,
          name,
          trackLinksEnabled,
          trackingPixelEnabled,
          unsubscribeLinkEnabled
        } = campaignObject;

        campaign = {
          listId,
          fromName,
          fromEmail,
          emailSubject,
          emailBody,
          campaignId,
          type,
          name,
          trackLinksEnabled,
          trackingPixelEnabled,
          unsubscribeLinkEnabled
        };

        resolve();
      }
    }).catch(err => {
      reject();
      throw err;
    });
  });

  const getAmazonKeysAndRegion = new Promise((resolve, reject) => {
    return db.setting.findOne({
      where: {
        userId: userId
      }
    }).then(settingInstance => {
      if (!settingInstance) {
        // This should never happen as settings are created on account creation
        reject();
        res.status(500).send();
      } else {
        const settingObject = settingInstance.get({ plain:true });
        const {
          amazonSimpleEmailServiceAccessKey: accessKey,
          amazonSimpleEmailServiceSecretKey: secretKey,
          region,
          whiteLabelUrl
        } = settingObject;
        // If either key is blank, the user needs to set their settings
        if ((accessKey === '' || secretKey === '' || region === '') && process.env.NODE_ENV === 'production') {
          res.status(400).send({ message:'Please provide your details for your Amazon account under "Settings".' });
          reject();
        } else {
          // handling of default whitelabel url?
          amazonSettings = { accessKey, secretKey, region, whiteLabelUrl };
          resolve();
          return null;
        }
      }
    }).catch(err => {
      reject();
      res.status(500).send(err);
    });
  });

  Promise.all([campaignBelongsToUser, getAmazonKeysAndRegion])
    .then(() => {
      const { accessKey, secretKey, region, whiteLabelUrl } = amazonSettings;
      const isDevMode = process.env.NODE_ENV === 'development' || false;

      const ses = isDevMode
        ? new AWS.SES({ accessKeyId: accessKey, secretAccessKey: secretKey, region, endpoint: 'http://localhost:9999' })
        : new AWS.SES({ accessKeyId: accessKey, secretAccessKey: secretKey, region });

      if (campaign.trackLinksEnabled) {
        campaign.emailBody = wrapLink(campaign.emailBody, 'example-tracking-id', campaign.type, whiteLabelUrl);
      }

      if (campaign.trackingPixelEnabled) {
        campaign.emailBody = insertTrackingPixel(campaign.emailBody, 'example-tracking-id', campaign.type);
      }

      if (campaign.unsubscribeLinkEnabled) {
        campaign.emailBody = insertUnsubscribeLink(campaign.emailBody, 'example-unsubscribe-id', campaign.type, whiteLabelUrl);
      }

      const emailFormat = AmazonEmail({ email: testEmail }, campaign);

      ses.sendEmail(emailFormat, err => {
        if (err)
          res.status(400).send(err);
        else
          res.send();
      });

    })
    .catch(err => {
      res.status(500).send(err);
      throw err;
    });

};
<commit_msg>Integrate test email functionality with mail merge<commit_after>const AWS = require('aws-sdk');
const db = require('../../../../models');
const AmazonEmail = require('./amazon');
const { wrapLink, insertUnsubscribeLink, insertTrackingPixel } = require('./analytics');
const mailMerge = require('./mail-merge');

module.exports = (req, res) => {
  const { testEmail, campaignId } = req.body;
  const userId = req.user.id;

  let campaign = {}; // eslint-disable-line
  let amazonSettings = {}; // eslint-disable-line

  const campaignBelongsToUser = new Promise((resolve, reject) => {
    return db.campaign.findOne({
      where: {
        id: campaignId,
        userId: userId
      }
    }).then(campaignInstance => {
      if (!campaignInstance) {
        reject();
        res.status(401).send();
      } else {
        const campaignObject = campaignInstance.get({ plain:true });
        const listId = campaignObject.listId;
        const {
          fromName,
          fromEmail,
          emailSubject,
          emailBody,
          type,
          name,
          trackLinksEnabled,
          trackingPixelEnabled,
          unsubscribeLinkEnabled
        } = campaignObject;

        campaign = {
          listId,
          fromName,
          fromEmail,
          emailSubject,
          emailBody,
          campaignId,
          type,
          name,
          trackLinksEnabled,
          trackingPixelEnabled,
          unsubscribeLinkEnabled
        };

        resolve();
      }
    }).catch(err => {
      reject();
      throw err;
    });
  });

  const getAmazonKeysAndRegion = new Promise((resolve, reject) => {
    return db.setting.findOne({
      where: {
        userId: userId
      }
    }).then(settingInstance => {
      if (!settingInstance) {
        // This should never happen as settings are created on account creation
        reject();
        res.status(500).send();
      } else {
        const settingObject = settingInstance.get({ plain:true });
        const {
          amazonSimpleEmailServiceAccessKey: accessKey,
          amazonSimpleEmailServiceSecretKey: secretKey,
          region,
          whiteLabelUrl
        } = settingObject;
        // If either key is blank, the user needs to set their settings
        if ((accessKey === '' || secretKey === '' || region === '') && process.env.NODE_ENV === 'production') {
          res.status(400).send({ message:'Please provide your details for your Amazon account under "Settings".' });
          reject();
        } else {
          // handling of default whitelabel url?
          amazonSettings = { accessKey, secretKey, region, whiteLabelUrl };
          resolve();
          return null;
        }
      }
    }).catch(err => {
      reject();
      res.status(500).send(err);
    });
  });

  Promise.all([campaignBelongsToUser, getAmazonKeysAndRegion])
    .then(() => {
      const { accessKey, secretKey, region, whiteLabelUrl } = amazonSettings;
      const isDevMode = process.env.NODE_ENV === 'development' || false;

      const ses = isDevMode
        ? new AWS.SES({ accessKeyId: accessKey, secretAccessKey: secretKey, region, endpoint: 'http://localhost:9999' })
        : new AWS.SES({ accessKeyId: accessKey, secretAccessKey: secretKey, region });

      // Modify email body for analytics
      if (campaign.trackLinksEnabled) {
        campaign.emailBody = wrapLink(campaign.emailBody, 'example-tracking-id', campaign.type, whiteLabelUrl);
      }
      if (campaign.trackingPixelEnabled) {
        campaign.emailBody = insertTrackingPixel(campaign.emailBody, 'example-tracking-id', campaign.type);
      }
      if (campaign.unsubscribeLinkEnabled) {
        campaign.emailBody = insertUnsubscribeLink(campaign.emailBody, 'example-unsubscribe-id', campaign.type, whiteLabelUrl);
      }

      // Get custom/additional data (extra columns) needed for mail merge feature
      db.list.findById(campaign.listId, {
        attributes: ['additionalFields'],
        raw: true
      }).then(list => {
        // Add sample/example data to the custom fields
        const additionalData = list.additionalFields.reduce((additionalData, field) => {
          additionalData[field] = `EXAMPLE ${field}`
          return additionalData;
        }, {});
        campaign.emailBody = mailMerge({ email: testEmail, additionalData }, campaign);

        const emailFormat = AmazonEmail({ email: testEmail }, campaign);

        ses.sendEmail(emailFormat, err => {
          if (err)
            res.status(400).send(err);
          else
            res.send();
        });
      })
    })
    .catch(err => {
      res.status(500).send(err);
      throw err;
    });
};
<|endoftext|>


<commit_before>define(['marionette', 'views/dialog', 'jquery', 'jquery.color'], function(Marionette, DialogView, $) {

  var utils = {

    // Convert timestamp string into javascript date object
    _date_to_unix: function(strtime) {
       var dt = strtime.trim().split(' ')
       var dmy = dt[0].split('-')
       var hms = dt[1].split(':')
       var date = new Date(dmy[2], dmy[1]-1, dmy[0], hms[0], hms[1], hms[2], 0)
       return date
    },

    // Reasonable default plot options
    default_plot: {
      grid: {
        borderWidth: 0,
      },
      series: {
        points: {
          show: true,
          radius: 1,
        }
      },
    },

    labelFormatter: function (label, series) {
        return "
" + label + "
" + Math.round(series.percent) + "%
(" + series.data[0][1].toFixed(1) + "hr)
";
    },

    labelFormatterNo: function (label, series) {
        return "
" + label + "
" + Math.round(series.percent) + "%
(" + series.data[0][1] + ")
";
    },

    // Generic Message View
    generic_msg: Marionette.ItemView.extend({
        className: 'content',
        template: _.template('
<%=title%>

<%=msg%>

'),
        templateHelpers: function() {
            return {
                msg: this.message,
                title: this.title,
            }
        },

        initialize: function(options) {
            this.message = options.message
            this.title = options.title
        }
    }),

    // Alert Message View
    alert: Marionette.ItemView.extend({
        persist: false,
        scrollTo: true,
        dismissible: false,

        className: function() {
            return 'message ' + (this.getOption('notify') ? 'notify' : 'alert')
        },
        tagName: 'p',

        getTemplate: function() {
            return this.getOption('dismissible') ? _.template('<%=msg%>')
                : _.template('<%=msg%>')
        },

        templateHelpers: function() {
            return {
                msg: this.getOption('message'),
            }
        },

        events: {
            'click a.dismiss': 'disimss',
        },

        disimss: function(e) {
            e.preventDefault()
            this.destroy()
        },

        onRender: function() {
            var self = this

            if (this.getOption('scrollTo')) {
                $('html, body').animate({
                    scrollTop: this.$el.offset().top }, 500, function() { self.$el.toggle('highlight')
                })
            }

            if (this.getOption('persist')) this.$el.addClass(this.getOption('persist'))

            if (!this.getOption('persist')) {
                setTimeout(function() {
                    self.$el.fadeOut(function() {
                        self.destroy()
                    })

                }, 10000)
            }
        },
    }),

    // Confirmation dialog
    confirm: function(options) {
        var ConfirmDialog = DialogView.extend({
            title: options.title,
            template: _.template(options.content),
            buttons: {
                'Ok': 'onOK',
                'Cancel': 'closeDialog',
            },

            onOK: function() {
                options.callback()
                this.closeDialog()
            },
        })
        app.dialog.show(new ConfirmDialog())
    },

    // Return x,y coordinate for a mouse event
    get_xy: function(e, obj) {
        if (e.offsetX == undefined) {
            return [e.pageX-$(obj).offset().left, e.pageY-$(obj).offset().top]
        } else {
            return [e.offsetX, e.offsetY]
        }
    },

    // Generate colour gradient from 0-1
    rainbow: function(val, width, cent) {
        var col = val*2*Math.PI
        if (width === undefined) width = 126
        if (cent === undefined) cent = 127
        return 'rgb('+ Math.floor(Math.sin(col)*width+cent) + ',' + Math.floor(Math.sin(col+2*Math.PI/3)*width+cent) + ',' + Math.floor(Math.sin(col+4*Math.PI/3)*width+cent)+ ')'
    },

    getColors: function(len) {
        return _.map(_.range(len), function (o, i) {
            return $.Color({ hue: (i*360/len), saturation: 0.90, lightness: 0.40, alpha: 1 }).toHexString();
        })
    },

    shadeColor: function(color, percent) {
        var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
        return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);
    },

    // Check if an element is in view
    inView: function(element, threshold) {
        var $w = $(window)
        var th = threshold || 0

        if (element.is(":hidden")) return;

        var wt = $w.scrollTop(),
        wb = wt + $w.height(),
        et = element.offset().top,
        eb = et + element.height();

        return (eb >= wt - th && et <= wb + th)
    },

    sign: function(options) {
        Backbone.ajax({
            url: app.apiurl+'/download/sign',
            method: 'POST',
            data: {
                validity: options.url.replace(app.apiurl, ''),
            },
            success: function(resp) {
                if (options && options.callback) options.callback(resp)
            }
        })
    },

    signHandler: function(e) {
        e.preventDefault()
        var target = $(e.target)
        if (target.is('i') || target.is('img')) target = target.closest('a')

        var url = target.attr('href')
        utils.sign({
            url: url,
            callback: function(resp) {
                window.location = url+'?token='+resp.token
            }
        })
    }

  }

  return utils

})<commit_msg>add array shuffle<commit_after>define(['marionette', 'views/dialog', 'jquery', 'jquery.color'], function(Marionette, DialogView, $) {

  var utils = {

    // Convert timestamp string into javascript date object
    _date_to_unix: function(strtime) {
       var dt = strtime.trim().split(' ')
       var dmy = dt[0].split('-')
       var hms = dt[1].split(':')
       var date = new Date(dmy[2], dmy[1]-1, dmy[0], hms[0], hms[1], hms[2], 0)
       return date
    },

    // Reasonable default plot options
    default_plot: {
      grid: {
        borderWidth: 0,
      },
      series: {
        points: {
          show: true,
          radius: 1,
        }
      },
    },

    labelFormatter: function (label, series) {
        return "
" + label + "
" + Math.round(series.percent) + "%
(" + series.data[0][1].toFixed(1) + "hr)
";
    },

    labelFormatterNo: function (label, series) {
        return "
" + label + "
" + Math.round(series.percent) + "%
(" + series.data[0][1] + ")
";
    },

    // Generic Message View
    generic_msg: Marionette.ItemView.extend({
        className: 'content',
        template: _.template('
<%=title%>

<%=msg%>

'),
        templateHelpers: function() {
            return {
                msg: this.message,
                title: this.title,
            }
        },

        initialize: function(options) {
            this.message = options.message
            this.title = options.title
        }
    }),

    // Alert Message View
    alert: Marionette.ItemView.extend({
        persist: false,
        scrollTo: true,
        dismissible: false,

        className: function() {
            return 'message ' + (this.getOption('notify') ? 'notify' : 'alert')
        },
        tagName: 'p',

        getTemplate: function() {
            return this.getOption('dismissible') ? _.template('<%=msg%>')
                : _.template('<%=msg%>')
        },

        templateHelpers: function() {
            return {
                msg: this.getOption('message'),
            }
        },

        events: {
            'click a.dismiss': 'disimss',
        },

        disimss: function(e) {
            e.preventDefault()
            this.destroy()
        },

        onRender: function() {
            var self = this

            if (this.getOption('scrollTo')) {
                $('html, body').animate({
                    scrollTop: this.$el.offset().top }, 500, function() { self.$el.toggle('highlight')
                })
            }

            if (this.getOption('persist')) this.$el.addClass(this.getOption('persist'))

            if (!this.getOption('persist')) {
                setTimeout(function() {
                    self.$el.fadeOut(function() {
                        self.destroy()
                    })

                }, 10000)
            }
        },
    }),

    // Confirmation dialog
    confirm: function(options) {
        var ConfirmDialog = DialogView.extend({
            title: options.title,
            template: _.template(options.content),
            buttons: {
                'Ok': 'onOK',
                'Cancel': 'closeDialog',
            },

            onOK: function() {
                options.callback()
                this.closeDialog()
            },
        })
        app.dialog.show(new ConfirmDialog())
    },

    // Return x,y coordinate for a mouse event
    get_xy: function(e, obj) {
        if (e.offsetX == undefined) {
            return [e.pageX-$(obj).offset().left, e.pageY-$(obj).offset().top]
        } else {
            return [e.offsetX, e.offsetY]
        }
    },

    // Generate colour gradient from 0-1
    rainbow: function(val, width, cent) {
        var col = val*2*Math.PI
        if (width === undefined) width = 126
        if (cent === undefined) cent = 127
        return 'rgb('+ Math.floor(Math.sin(col)*width+cent) + ',' + Math.floor(Math.sin(col+2*Math.PI/3)*width+cent) + ',' + Math.floor(Math.sin(col+4*Math.PI/3)*width+cent)+ ')'
    },

    getColors: function(len) {
        return _.map(_.range(len), function (o, i) {
            return $.Color({ hue: (i*360/len), saturation: 0.90, lightness: 0.40, alpha: 1 }).toHexString();
        })
    },

    shadeColor: function(color, percent) {
        var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
        return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);
    },

    // Check if an element is in view
    inView: function(element, threshold) {
        var $w = $(window)
        var th = threshold || 0

        if (element.is(":hidden")) return;

        var wt = $w.scrollTop(),
        wb = wt + $w.height(),
        et = element.offset().top,
        eb = et + element.height();

        return (eb >= wt - th && et <= wb + th)
    },

    sign: function(options) {
        Backbone.ajax({
            url: app.apiurl+'/download/sign',
            method: 'POST',
            data: {
                validity: options.url.replace(app.apiurl, ''),
            },
            success: function(resp) {
                if (options && options.callback) options.callback(resp)
            }
        })
    },

    signHandler: function(e) {
        e.preventDefault()
        var target = $(e.target)
        if (target.is('i') || target.is('img')) target = target.closest('a')

        var url = target.attr('href')
        utils.sign({
            url: url,
            callback: function(resp) {
                window.location = url+'?token='+resp.token
            }
        })
    },

    shuffle: function(array) {
        var currentIndex = array.length, temporaryValue, randomIndex
        while (0 !== currentIndex) {
            randomIndex = Math.floor(Math.random() * currentIndex)
            currentIndex -= 1
            temporaryValue = array[currentIndex]
            array[currentIndex] = array[randomIndex]
            array[randomIndex] = temporaryValue
        }

        return array
    }

  }

  return utils

})<|endoftext|>