<commit_before>Router = function () {
  this.globals = [];
  this.subscriptions = Function.prototype;

  this._tracker = this._buildTracker();
  this._current = {};

  // tracks the current path change
  this._onEveryPath = new Tracker.Dependency();

  this._globalRoute = new Route(this);

  this._triggersEnter = [];
  this._triggersExit = [];
  this._middleware = [];
  this._routes = [];
  this._routesMap = {};
  this._updateCallbacks();
  this.notFound = this.notfound = null;
  // indicate it's okay (or not okay) to run the tracker
  // when doing subscriptions
  // using a number and increment it help us to support FlowRouter.go()
  // and legitimate reruns inside tracker on the same event loop.
  // this is a solution for #145
  this.safeToRun = 0;

  var self = this;
  this.triggers = {
    enter: self._getRegisterTriggersFn(self._triggersEnter),
    exit: self._getRegisterTriggersFn(self._triggersExit)
  };

  this.env = {
    replaceState: new Meteor.EnvironmentVariable()
  };
};

Router.prototype.route = function(path, options, group) {
  if (!/^\/.*/.test(path)) {
    var message = "route's path must start with '/'";
    throw new Error(message);
  }

  options = options || {};
  var self = this;
  var route = new Route(this, path, options, group);

  route._handler = function (context, next) {
    var oldRoute = self._current.route;

    self._current = {
      path: context.path,
      context: context,
      params: context.params,
      queryParams: self._qs.parse(context.querystring),
      route: route,
      oldRoute: oldRoute
    };

    // to backward compatibility
    self._current.params.query = self._current.queryParams;

    self._invalidateTracker();
  };

  this._routes.push(route);
  if (options.name) {
    this._routesMap[options.name] = route;
  }

  this._updateCallbacks();

  return route;
};

Router.prototype.group = function(options) {
  return new Group(this, options);
};

Router.prototype.path = function(pathDef, fields, queryParams) {
  if (this._routesMap[pathDef]) {
    pathDef = this._routesMap[pathDef].path;
  }

  // remove trailing slash(es)
  // but keep the root slash if it's the only one
  pathDef = pathDef.match(/^\/{1}$/) ? pathDef : pathDef.replace(/\/+$/, "");

  fields = fields || {};
  var regExp = /(:[\w\(\)\\\+\*\.\?]+)+/g;
  var path = pathDef.replace(regExp, function(key) {
    var firstRegexpChar = key.indexOf("(");
    // get the content behind : and (\\d+/)
    key = key.substring(1, (firstRegexpChar > 0)? firstRegexpChar: undefined);
    // remove +?*
    key = key.replace(/[\+\*\?]+/g, "");

    return fields[key] || "";
  });

  var strQueryParams = this._qs.stringify(queryParams || {});
  if(strQueryParams) {
    path += "?" + strQueryParams;
  }

  return path;
};

Router.prototype.go = function(pathDef, fields, queryParams) {
  var path = this.path(pathDef, fields, queryParams);

  if (this._current.path === path) {
    return;
  }

  var useReplaceState = this.env.replaceState.get();
  if(useReplaceState) {
    this._page.redirect(path);
  } else {
    this._page(path);
  }
};

Router.prototype.reload = function() {
  this._page.replace(this._current.path);
};

Router.prototype.redirect = function(path) {
  this._page.redirect(path);
};

Router.prototype.setParams = function(newParams) {
  if(!this._current.route) {return false;}

  var pathDef = this._current.route.path;
  var existingParams = this._current.params;
  var params = {};
  _.each(_.keys(existingParams), function(key) {
    params[key] = existingParams[key];
  });

  params = _.extend(params, newParams);
  var queryParams = this._current.queryParams;

  this.go(pathDef, params, queryParams);
  return true;
};

Router.prototype.setQueryParams = function(newParams) {
  if(!this._current.route) {return false;}

  var queryParams = _.clone(this._current.queryParams);
  _.extend(queryParams, newParams);

  for (var k in queryParams) {
    if (queryParams[k] === null || queryParams[k] === undefined) {
      delete queryParams[k];
    }
  }

  var pathDef = this._current.route.path;
  var params = this._current.params;
  this.go(pathDef, params, queryParams);
  return true;
};

// .current is not reactive
// This is by design. use .getParam() instead
// If you really need to watch the path change, use .watchPathChange()
Router.prototype.current = function() {
  return this._current;
};

Router.prototype.reactiveCurrent = function() {
  var warnMessage =
    ".reactiveCurrent() is deprecated. " +
    "Use .watchPathChange() instead";
  console.warn(warnMessage);

  this.watchPathChange();
  return this.current();
};

// Implementing Reactive APIs
var reactiveApis = [
  'getParam', 'getQueryParam',
  'getRouteName', 'watchPathChange'
];
reactiveApis.forEach(function(api) {
  Router.prototype[api] = function(arg1) {
    // when this is calling, there may not be any route initiated
    // so we need to handle it
    var currentRoute = this._current.route;
    if(!currentRoute) {
      this._onEveryPath.depend();
      return;
    }

    // currently, there is only one argument. If we've more let's add more args
    // this is not clean code, but better in performance
    return currentRoute[api].call(currentRoute, arg1);
  };
});

Router.prototype.middleware = function(middlewareFn) {
  console.warn("'middleware' is deprecated. Use 'triggers' instead");
  var self = this;
  var mw = function(ctx, next) {
    // make sure middlewars run after Meteor has been initialized
    // this is very important for specially for fast render and Meteor.user()
    // availability
    Meteor.startup(function() {
      middlewareFn(ctx.pathname, processNext);
    });

    function processNext(path) {
      if(path) {
        return self._page.redirect(path);
      }
      next();
    }
  };

  this._middleware.push(mw);
  this._updateCallbacks();
  return this;
};

Router.prototype.ready = function() {
  console.warn("'FlowRouter.ready()' is deprecated. Use 'FlowRouter.subsReady()' instead");
  return this.subsReady.apply(this, arguments);
};

Router.prototype.subsReady = function() {
  var callback = null;
  var args = _.toArray(arguments);

  if (typeof _.last(args) === "function") {
    callback = args.pop();
  }

  var currentRoute = this.current().route;
  var globalRoute = this._globalRoute;

  // we need to depend for every route change and
  // rerun subscriptions to check the ready state
  this._onEveryPath.depend();

  if(!currentRoute) {
    return false;
  }

  var subscriptions;
  if(args.length === 0) {
    subscriptions = _.values(globalRoute.getAllSubscriptions());
    subscriptions = subscriptions.concat(_.values(currentRoute.getAllSubscriptions()));
  } else {
    subscriptions = _.map(args, function(subName) {
      return globalRoute.getSubscription(subName) || currentRoute.getSubscription(subName);
    });
  }

  var isReady = function() {
    var ready =  _.every(subscriptions, function(sub) {
      return sub && sub.ready();
    });

    return ready;
  };

  if (callback) {
    Tracker.autorun(function(c) {
      if (isReady()) {
        callback();
        c.stop();
      }
    });
  } else {
    return isReady();
  }
};

Router.prototype.withReplaceState = function(fn) {
  return this.env.replaceState.withValue(true, fn);
};

Router.prototype._notfoundRoute = function(context) {
  this._current = {
    path: context.path,
    context: context,
    params: [],
    queryParams: {},
  };

  // XXX this.notfound kept for backwards compatibility
  this.notFound = this.notFound || this.notfound;
  if(!this.notFound) {
    console.error("There is no route for the path:", context.path);
    return;
  }

  this._current.route = new Route(this, "*", this.notFound);
  this._invalidateTracker();
};

Router.prototype.initialize = function() {
  var self = this;
  this._updateCallbacks();
  // initialize
  this._page();
};

Router.prototype._buildTracker = function() {
  var self = this;

  // main autorun function
  var tracker = Tracker.autorun(function () {
    if(!self._current || !self._current.route) {
      return;
    }

    var route = self._current.route;
    var path = self._current.path;

    if(self.safeToRun === 0) {
      var message =
        "You can't use reactive data sources like Session" +
        " inside the `.subscriptions` method!";
      throw new Error(message);
    }

    // We need to run subscriptions inside a Tracker
    // to stop subs when switching between routes
    // But we don't need to run this tracker with
    // other reactive changes inside the .subscription method
    // We tackle this with the `safeToRun` variable
    self._globalRoute.clearSubscriptions();
    self.subscriptions.call(self._globalRoute, path);
    route.callSubscriptions(self._current);

    // otherwise, computations inside action will trigger to re-run
    // this computation. which we do not need.
    Tracker.nonreactive(function() {
      var currentContext = self._current;

      self._processTriggersEnter(currentContext);

      var isRouteChange = currentContext.oldRoute !== currentContext.route;
      var isFirstRoute = !currentContext.oldRoute;
      // first route is not a route change
      if(isFirstRoute) {
        isRouteChange = false;
      }

      currentContext.route.registerRouteChange(currentContext, isRouteChange);
      route.callAction(currentContext);

      Tracker.afterFlush(function() {
        self._onEveryPath.changed();
        if(isRouteChange) {
          // We need to trigger that route (definition itself) has changed.
          // So, we need to re-run all the register callbacks to current route
          // This is pretty important, otherwise tracker
          // can't identify new route's items

          // We also need to afterFlush, otherwise this will re-run
          // helpers on templates which are marked for destroying
          currentContext.oldRoute.registerRouteClose();
        }
      });
    });

    self.safeToRun--;
  });

  return tracker;
};

Router.prototype._invalidateTracker = function() {
  this.safeToRun++;
  this._tracker.invalidate();
};

Router.prototype._getRegisterTriggersFn = function(triggers) {
  var fn = function(triggerFns, options) {
    options = options || {};

    if (options.only && options.except) {
      var message = "triggers does not support 'only' and 'except' at the same time.";
      throw new Error(message);
    }

    _.each(triggerFns, function(fn) {
      if (typeof fn !== 'function') {
        return;
      }

      if (options.only) {
        fn._only = {};
        _.each(options.only, function(name) {
          fn._only[name] = 1;
        });
      }

      if (options.except) {
        fn._except = {};
        _.each(options.except, function(name) {
          fn._except[name] = 1;
        });
      }

      triggers.push(fn);
    });
  };

  return fn;
};

Router.prototype._shouldCallTrigger = function(current, fn) {
  var name = current.route.name;
  var shouldCall;

  if (typeof fn !== 'function') {
    return false;
  }

  if (fn._only) {
    shouldCall = !!fn._only[name];
  } else if (fn._except) {
    shouldCall = !fn._except[name];
  } else {
    shouldCall = true;
  }

  return shouldCall;
};

Router.prototype._processTriggersEnter = function(current) {
  var self = this;

  _.each(this._triggersEnter, function(fn) {
    if (self._shouldCallTrigger(current, fn)) {
      fn(current);
    }
  });
};

Router.prototype._processTriggersExit = function(ctx, next) {
  var self = this;

  _.each(self._triggersExit, function(fn) {
    if (self._shouldCallTrigger(self._current, fn)) {
      fn(self._current);
    }
  });

  next();
};

Router.prototype._registerRouteTriggersExit = function(route) {
  var self = this;

  if (route._triggersExit.length > 0) {
    // add route's exit triggers
    self._page.exit(route.path, function(ctx, next) {
      _.each(route._triggersExit, function(fn) {
        if (typeof fn === 'function') {
          fn(self._current);
        }
      });

      next();
    });
  }
};

Router.prototype._updateCallbacks = function () {
  var self = this;

  self._page.callbacks = [];
  self._page.exits = [];

  // add global middleware
  _.each(self._middleware, function(fn) {
    self._page("*", fn);
  });

  _.each(self._routes, function(route) {
    self._page(route.path, route._handler);
    self._registerRouteTriggersExit(route);
  });

  self._page.exit("*", self._processTriggersExit.bind(self));

  self._page("*", function(context) {
    self._notfoundRoute(context);
  });
};

Router.prototype._page = page;
Router.prototype._qs = qs;
<commit_msg>Implemting idempotant routes except exit triggers<commit_after>Router = function () {
  this.globals = [];
  this.subscriptions = Function.prototype;

  this._tracker = this._buildTracker();
  this._current = {};

  // tracks the current path change
  this._onEveryPath = new Tracker.Dependency();

  this._globalRoute = new Route(this);

  this._triggersEnter = [];
  this._triggersExit = [];
  this._middleware = [];
  this._routes = [];
  this._routesMap = {};
  this._updateCallbacks();
  this.notFound = this.notfound = null;
  // indicate it's okay (or not okay) to run the tracker
  // when doing subscriptions
  // using a number and increment it help us to support FlowRouter.go()
  // and legitimate reruns inside tracker on the same event loop.
  // this is a solution for #145
  this.safeToRun = 0;

  var self = this;
  this.triggers = {
    enter: self._getRegisterTriggersFn(self._triggersEnter),
    exit: self._getRegisterTriggersFn(self._triggersExit)
  };

  this.env = {
    replaceState: new Meteor.EnvironmentVariable(),
    reload: new Meteor.EnvironmentVariable()
  };
};

Router.prototype.route = function(path, options, group) {
  if (!/^\/.*/.test(path)) {
    var message = "route's path must start with '/'";
    throw new Error(message);
  }

  options = options || {};
  var self = this;
  var route = new Route(this, path, options, group);

  route._handler = function (context, next) {
    var reload = self.env.reload.get();
    if (!reload && self._current.path === context.path) {
      return;
    }

    var oldRoute = self._current.route;

    self._current = {
      path: context.path,
      context: context,
      params: context.params,
      queryParams: self._qs.parse(context.querystring),
      route: route,
      oldRoute: oldRoute
    };

    // to backward compatibility
    self._current.params.query = self._current.queryParams;

    self._invalidateTracker();
  };

  this._routes.push(route);
  if (options.name) {
    this._routesMap[options.name] = route;
  }

  this._updateCallbacks();

  return route;
};

Router.prototype.group = function(options) {
  return new Group(this, options);
};

Router.prototype.path = function(pathDef, fields, queryParams) {
  if (this._routesMap[pathDef]) {
    pathDef = this._routesMap[pathDef].path;
  }

  // remove trailing slash(es)
  // but keep the root slash if it's the only one
  pathDef = pathDef.match(/^\/{1}$/) ? pathDef : pathDef.replace(/\/+$/, "");

  fields = fields || {};
  var regExp = /(:[\w\(\)\\\+\*\.\?]+)+/g;
  var path = pathDef.replace(regExp, function(key) {
    var firstRegexpChar = key.indexOf("(");
    // get the content behind : and (\\d+/)
    key = key.substring(1, (firstRegexpChar > 0)? firstRegexpChar: undefined);
    // remove +?*
    key = key.replace(/[\+\*\?]+/g, "");

    return fields[key] || "";
  });

  var strQueryParams = this._qs.stringify(queryParams || {});
  if(strQueryParams) {
    path += "?" + strQueryParams;
  }

  return path;
};

Router.prototype.go = function(pathDef, fields, queryParams) {
  var path = this.path(pathDef, fields, queryParams);

  var useReplaceState = this.env.replaceState.get();
  if(useReplaceState) {
    this._page.redirect(path);
  } else {
    this._page(path);
  }
};

Router.prototype.reload = function() {
  var self = this;

  self.env.reload.withValue(true, function() {
    self._page.replace(self._current.path);
  });
};

Router.prototype.redirect = function(path) {
  this._page.redirect(path);
};

Router.prototype.setParams = function(newParams) {
  if(!this._current.route) {return false;}

  var pathDef = this._current.route.path;
  var existingParams = this._current.params;
  var params = {};
  _.each(_.keys(existingParams), function(key) {
    params[key] = existingParams[key];
  });

  params = _.extend(params, newParams);
  var queryParams = this._current.queryParams;

  this.go(pathDef, params, queryParams);
  return true;
};

Router.prototype.setQueryParams = function(newParams) {
  if(!this._current.route) {return false;}

  var queryParams = _.clone(this._current.queryParams);
  _.extend(queryParams, newParams);

  for (var k in queryParams) {
    if (queryParams[k] === null || queryParams[k] === undefined) {
      delete queryParams[k];
    }
  }

  var pathDef = this._current.route.path;
  var params = this._current.params;
  this.go(pathDef, params, queryParams);
  return true;
};

// .current is not reactive
// This is by design. use .getParam() instead
// If you really need to watch the path change, use .watchPathChange()
Router.prototype.current = function() {
  return this._current;
};

Router.prototype.reactiveCurrent = function() {
  var warnMessage =
    ".reactiveCurrent() is deprecated. " +
    "Use .watchPathChange() instead";
  console.warn(warnMessage);

  this.watchPathChange();
  return this.current();
};

// Implementing Reactive APIs
var reactiveApis = [
  'getParam', 'getQueryParam',
  'getRouteName', 'watchPathChange'
];
reactiveApis.forEach(function(api) {
  Router.prototype[api] = function(arg1) {
    // when this is calling, there may not be any route initiated
    // so we need to handle it
    var currentRoute = this._current.route;
    if(!currentRoute) {
      this._onEveryPath.depend();
      return;
    }

    // currently, there is only one argument. If we've more let's add more args
    // this is not clean code, but better in performance
    return currentRoute[api].call(currentRoute, arg1);
  };
});

Router.prototype.middleware = function(middlewareFn) {
  console.warn("'middleware' is deprecated. Use 'triggers' instead");
  var self = this;
  var mw = function(ctx, next) {
    // make sure middlewars run after Meteor has been initialized
    // this is very important for specially for fast render and Meteor.user()
    // availability
    Meteor.startup(function() {
      middlewareFn(ctx.pathname, processNext);
    });

    function processNext(path) {
      if(path) {
        return self._page.redirect(path);
      }
      next();
    }
  };

  this._middleware.push(mw);
  this._updateCallbacks();
  return this;
};

Router.prototype.ready = function() {
  console.warn("'FlowRouter.ready()' is deprecated. Use 'FlowRouter.subsReady()' instead");
  return this.subsReady.apply(this, arguments);
};

Router.prototype.subsReady = function() {
  var callback = null;
  var args = _.toArray(arguments);

  if (typeof _.last(args) === "function") {
    callback = args.pop();
  }

  var currentRoute = this.current().route;
  var globalRoute = this._globalRoute;

  // we need to depend for every route change and
  // rerun subscriptions to check the ready state
  this._onEveryPath.depend();

  if(!currentRoute) {
    return false;
  }

  var subscriptions;
  if(args.length === 0) {
    subscriptions = _.values(globalRoute.getAllSubscriptions());
    subscriptions = subscriptions.concat(_.values(currentRoute.getAllSubscriptions()));
  } else {
    subscriptions = _.map(args, function(subName) {
      return globalRoute.getSubscription(subName) || currentRoute.getSubscription(subName);
    });
  }

  var isReady = function() {
    var ready =  _.every(subscriptions, function(sub) {
      return sub && sub.ready();
    });

    return ready;
  };

  if (callback) {
    Tracker.autorun(function(c) {
      if (isReady()) {
        callback();
        c.stop();
      }
    });
  } else {
    return isReady();
  }
};

Router.prototype.withReplaceState = function(fn) {
  return this.env.replaceState.withValue(true, fn);
};

Router.prototype._notfoundRoute = function(context) {
  this._current = {
    path: context.path,
    context: context,
    params: [],
    queryParams: {},
  };

  // XXX this.notfound kept for backwards compatibility
  this.notFound = this.notFound || this.notfound;
  if(!this.notFound) {
    console.error("There is no route for the path:", context.path);
    return;
  }

  this._current.route = new Route(this, "*", this.notFound);
  this._invalidateTracker();
};

Router.prototype.initialize = function() {
  var self = this;
  this._updateCallbacks();
  // initialize
  this._page();
};

Router.prototype._buildTracker = function() {
  var self = this;

  // main autorun function
  var tracker = Tracker.autorun(function () {
    if(!self._current || !self._current.route) {
      return;
    }

    var route = self._current.route;
    var path = self._current.path;

    if(self.safeToRun === 0) {
      var message =
        "You can't use reactive data sources like Session" +
        " inside the `.subscriptions` method!";
      throw new Error(message);
    }

    // We need to run subscriptions inside a Tracker
    // to stop subs when switching between routes
    // But we don't need to run this tracker with
    // other reactive changes inside the .subscription method
    // We tackle this with the `safeToRun` variable
    self._globalRoute.clearSubscriptions();
    self.subscriptions.call(self._globalRoute, path);
    route.callSubscriptions(self._current);

    // otherwise, computations inside action will trigger to re-run
    // this computation. which we do not need.
    Tracker.nonreactive(function() {
      var currentContext = self._current;

      self._processTriggersEnter(currentContext);

      var isRouteChange = currentContext.oldRoute !== currentContext.route;
      var isFirstRoute = !currentContext.oldRoute;
      // first route is not a route change
      if(isFirstRoute) {
        isRouteChange = false;
      }

      currentContext.route.registerRouteChange(currentContext, isRouteChange);
      route.callAction(currentContext);

      Tracker.afterFlush(function() {
        self._onEveryPath.changed();
        if(isRouteChange) {
          // We need to trigger that route (definition itself) has changed.
          // So, we need to re-run all the register callbacks to current route
          // This is pretty important, otherwise tracker
          // can't identify new route's items

          // We also need to afterFlush, otherwise this will re-run
          // helpers on templates which are marked for destroying
          currentContext.oldRoute.registerRouteClose();
        }
      });
    });

    self.safeToRun--;
  });

  return tracker;
};

Router.prototype._invalidateTracker = function() {
  this.safeToRun++;
  this._tracker.invalidate();
};

Router.prototype._getRegisterTriggersFn = function(triggers) {
  var fn = function(triggerFns, options) {
    options = options || {};

    if (options.only && options.except) {
      var message = "triggers does not support 'only' and 'except' at the same time.";
      throw new Error(message);
    }

    _.each(triggerFns, function(fn) {
      if (typeof fn !== 'function') {
        return;
      }

      if (options.only) {
        fn._only = {};
        _.each(options.only, function(name) {
          fn._only[name] = 1;
        });
      }

      if (options.except) {
        fn._except = {};
        _.each(options.except, function(name) {
          fn._except[name] = 1;
        });
      }

      triggers.push(fn);
    });
  };

  return fn;
};

Router.prototype._shouldCallTrigger = function(current, fn) {
  var name = current.route.name;
  var shouldCall;

  if (typeof fn !== 'function') {
    return false;
  }

  if (fn._only) {
    shouldCall = !!fn._only[name];
  } else if (fn._except) {
    shouldCall = !fn._except[name];
  } else {
    shouldCall = true;
  }

  return shouldCall;
};

Router.prototype._processTriggersEnter = function(current) {
  var self = this;

  _.each(this._triggersEnter, function(fn) {
    if (self._shouldCallTrigger(current, fn)) {
      fn(current);
    }
  });
};

Router.prototype._processTriggersExit = function(ctx, next) {
  var self = this;

  _.each(self._triggersExit, function(fn) {
    if (self._shouldCallTrigger(self._current, fn)) {
      fn(self._current);
    }
  });

  next();
};

Router.prototype._registerRouteTriggersExit = function(route) {
  var self = this;

  if (route._triggersExit.length > 0) {
    // add route's exit triggers
    self._page.exit(route.path, function(ctx, next) {
      _.each(route._triggersExit, function(fn) {
        if (typeof fn === 'function') {
          fn(self._current);
        }
      });

      next();
    });
  }
};

Router.prototype._updateCallbacks = function () {
  var self = this;

  self._page.callbacks = [];
  self._page.exits = [];

  // add global middleware
  _.each(self._middleware, function(fn) {
    self._page("*", fn);
  });

  _.each(self._routes, function(route) {
    self._page(route.path, route._handler);
    self._registerRouteTriggersExit(route);
  });

  self._page.exit("*", self._processTriggersExit.bind(self));

  self._page("*", function(context) {
    self._notfoundRoute(context);
  });
};

Router.prototype._page = page;
Router.prototype._qs = qs;
<|endoftext|>


<commit_before><commit_msg>Add completedMessage to watch task<commit_after><|endoftext|>


<commit_before>(function ($, CDT, undefined) {
  "use strict";

  if (CDT.widget === undefined) {
    CDT.widget = {};
  }

  CDT.widget.list = function () {
    var list, elm, thead, cols, tbody;

    cols = [];
    thead = $('<thead/>');
    tbody = $('<tbody/>');
    elm = $('<div/>')
      .addClass('cdt-list')
      .addClass('ui-widget-content')
      .addClass('ui-corner-all')
      .append($('<table/>')
        .append(thead.append($('<tr/>')))
        .append(tbody)
      );

    thead.find('tr').append(
      $('<th/>').addClass('ui-widget-header').text(' ')
    );

    list = { elm: elm };
    eventuality(list);

    list.addColumn = function (lbl, renderer) {
      var dataIndex;
      if (typeof renderer === 'string') {
        dataIndex = renderer;
        renderer = function (rowData) {
          return rowData[dataIndex];
        };
      }
      cols.push(renderer);

      thead.find('tr').append(
        $('<th/>').addClass('ui-widget-header').text(lbl)
      );
    };

    list.addRow = function (rowData) {
      var chk, row;

      row = $('<tr/>');

      chk = $('<input type="checkbox"/>')
        .data('row-attributes', rowData)
        .click(function () {
          list.fire('selection-change');
        });
      row.append($('<td/>')
        .addClass('ui-widget-content')
        .append(chk)
      );

      $.each(cols, function (idx, col) {
        row.append(
          $('<td/>')
            .addClass('ui-widget-content')
            .append(col(rowData))
        );
      });

      tbody.append(row);
    };

    list.getSelected = function () {
      return tbody.find('input:checked');
    };

    list.populate = function (data, mapper) {
      tbody.empty();

      $.each(data, function (idx, rowData) {
        if (mapper !== undefined) {
          rowData = mapper(rowData);
        }
        list.addRow(rowData);
      });
    };

    return list;
  };

} (jQuery, CDT));
<commit_msg>Update list js widget to fire an event whenever it is populated<commit_after>(function ($, CDT, undefined) {
  "use strict";

  if (CDT.widget === undefined) {
    CDT.widget = {};
  }

  CDT.widget.list = function () {
    var list, elm, thead, cols, tbody;

    cols = [];
    thead = $('<thead/>');
    tbody = $('<tbody/>');
    elm = $('<div/>')
      .addClass('cdt-list')
      .addClass('ui-widget-content')
      .addClass('ui-corner-all')
      .append($('<table/>')
        .append(thead.append($('<tr/>')))
        .append(tbody)
      );

    thead.find('tr').append(
      $('<th/>').addClass('ui-widget-header').text(' ')
    );

    list = { elm: elm };
    eventuality(list);

    list.addColumn = function (lbl, renderer) {
      var dataIndex;
      if (typeof renderer === 'string') {
        dataIndex = renderer;
        renderer = function (rowData) {
          return rowData[dataIndex];
        };
      }
      cols.push(renderer);

      thead.find('tr').append(
        $('<th/>').addClass('ui-widget-header').text(lbl)
      );
    };

    list.addRow = function (rowData) {
      var chk, row;

      row = $('<tr/>');

      chk = $('<input type="checkbox"/>')
        .data('row-attributes', rowData)
        .click(function () {
          list.fire('selection-change');
        });
      row.append($('<td/>')
        .addClass('ui-widget-content')
        .append(chk)
      );

      $.each(cols, function (idx, col) {
        row.append(
          $('<td/>')
            .addClass('ui-widget-content')
            .append(col(rowData))
        );
      });

      tbody.append(row);
    };

    list.getSelected = function () {
      return tbody.find('input:checked');
    };

    list.populate = function (data, mapper) {
      tbody.empty();

      $.each(data, function (idx, rowData) {
        if (mapper !== undefined) {
          rowData = mapper(rowData);
        }
        list.addRow(rowData);
      });

      list.fire('load');
    };

    return list;
  };

} (jQuery, CDT));
<|endoftext|>


<commit_before>Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading'
});

RouteController = {};
RouteController.home =

Router.map( function () {

  this.route('home', {
    path: '/'
  });

  this.route('about', {
    path: 'about'
  });

});<commit_msg>clean up<commit_after>Router.configure({
  layoutTemplate: 'layout',
  loadingTemplate: 'loading'
});

Router.map( function () {

  this.route('home', {
    path: '/'
  });

  this.route('about', {
    path: 'about'
  });

});<|endoftext|>


<commit_before>(function(){
    'use strict';
    angular
        .module('SAKapp')
        .controller('AlerterController', ['$scope', '$http', '$log', '$q',  'toaster', AlerterController]);

    function AlerterController ($scope, $http, $log, $q, toaster) {

        $scope.getEvents = function () {
        $scope.events = "";
        $scope.errorMsg = "";

        $http({
            method: "GET",
            url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
        }).then(function(result) {

            $http({
                method: "GET",
                url: "/proxy?url=http://" + result.data.host + "/v1/events",
            }).then(function successCallback( html ) {
                $scope.alerts = html.data;
            }, function errorCallback(response){
                console.log("alerts.controller fail"+response.status);
                toaster.pop('error', "Error", "There was an issue with retrieving the events.");
            });

        });

    };

    $scope.postEvent = function(){
        $scope.errorMsg = "";

        var currentTime = moment().utc().toISOString();
        var alertMessage = $scope.alertMessage;
        var dataObj = {
            type: "string event",
            date: currentTime,
            data: {
                one: "one",
                two: "two",
                three: "three"
            }
        }
        $http({
            method: "GET",
            url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
        }).then(function(result) {

            $http.post(
                "/proxy?url=http://" + result.data.host + "/v1/events",
                dataObj
            ).then(function successCallback(res) {
                $scope.message = res;
                $scope.getEvents();
                $scope.alertMessage = null;
                toaster.pop('success', "Success", "The alert was successfully posted.")

            }, function errorCallback(res) {
                console.log("alerter.controller fail"+res.status);

                toaster.pop('error', "Error", "There was a problem submitting the alert message.");
            });
        })
    }

        $scope.getConditions = function () {
            $scope.conditions = "";
            $scope.errorMsg = "";

            $http({
                method: "GET",
                url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
            }).then(function(result) {

                $http({
                    method: "GET",
                    url: "/proxy?url=http://" + result.data.host + "/v1/conditions",
                }).then(function successCallback( html ) {
                    $scope.conditions = html.data;
                }, function errorCallback(response){
                    console.log("alerts.controller fail"+response.status);
                    toaster.pop('error', "Error", "There was an issue with retrieving the conditions.");
                });

            });

        };

        $scope.postConditions = function(){
            $scope.errorMsg = "";

            var currentTime = moment().utc().toISOString();
            var alertMessage = $scope.alertMessage;
            var dataObj = {
                title: "short string",
                description: "long string",
                type: "event type string",
                userid: "just some string for now"
            }
            $http({
                method: "GET",
                url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
            }).then(function(result) {

                $http.post(
                    "/proxy?url=http://" + result.data.host + "/v1/conditions",
                    dataObj
                ).then(function successCallback(res) {
                    $scope.message = res;
                    $scope.getEvents();
                    $scope.alertMessage = null;
                    toaster.pop('success', "Success", "The condition was successfully posted.")

                }, function errorCallback(res) {
                    console.log("alerter.controller fail"+res.status);

                    toaster.pop('error', "Error", "There was a problem submitting the condition message.");
                });
            })
        }

        $scope.getConditionById = function () {
            $scope.conditionById = "";
            $scope.conditionId = "";
            $scope.errorMsg = "";

            $http({
                method: "GET",
                url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
            }).then(function(result) {

                $http({
                    method: "GET",
                    url: "/proxy?url=http://" + result.data.host + "/v1/conditions/:"+conditionId,
                }).then(function successCallback( html ) {
                    $scope.condition = html.data;
                }, function errorCallback(response){
                    console.log("alerts.controller fail"+response.status);
                    toaster.pop('error', "Error", "There was an issue with retrieving the condition.");
                });

            });

        };

        $scope.deleteConditionById = function(){
            $scope.errorMsg = "";
            $scope.conditionId = "";
            var alertMessage = $scope.alertMessage;

            $http({
                method: "GET",
                url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
            }).then(function(result) {

                $http.delete(
                    "/proxy?url=http://" + result.data.host + "/v1/conditions/:id"+conditionId
                    ).then(function successCallback(res) {
                    $scope.message = res;
                    $scope.getEvents();
                    $scope.alertMessage = null;
                    toaster.pop('success', "Success", "The condition was successfully deleted.")

                }, function errorCallback(res) {
                    console.log("alerter.controller fail"+res.status);

                    toaster.pop('error', "Error", "There was a problem deleting the condition message.");
                });
            })
        }

}

})();<commit_msg>sak640 changes to API required changes to alerter code.<commit_after>(function(){
    'use strict';
    angular
        .module('SAKapp')
        .controller('AlerterController', ['$scope', '$http', '$log', '$q',  'toaster', AlerterController]);

    function AlerterController ($scope, $http, $log, $q, toaster) {

        $scope.getEvents = function () {
        $scope.events = "";
        $scope.errorMsg = "";

        $http({
            method: "GET",
            url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
        }).then(function(result) {

            $http({
                method: "GET",
                url: "/proxy?url=http://" + result.data.host + "/v1/events",
            }).then(function successCallback( html ) {
                $scope.alerts = html.data;
            }, function errorCallback(response){
                console.log("alerts.controller fail"+response.status);
                toaster.pop('error', "Error", "There was an issue with retrieving the events.");
            });

        });

    };

    $scope.postEvent = function(){
        $scope.errorMsg = "";

        var currentTime = moment().utc().toISOString();
        var alertMessage = $scope.alertMessage;
        var dataObj = {
            type: "string event",
            date: currentTime,
            data: {
                one: "one",
                two: "two",
                three: "three"
            }
        }
        $http({
            method: "GET",
            url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
        }).then(function(result) {

            $http.post(
                "/proxy?url=http://" + result.data.host + "/v1/events",
                dataObj
            ).then(function successCallback(res) {
                $scope.message = res;
                $scope.getEvents();
                $scope.alertMessage = null;
                toaster.pop('success', "Success", "The alert was successfully posted.")

            }, function errorCallback(res) {
                console.log("alerter.controller fail"+res.status);

                toaster.pop('error', "Error", "There was a problem submitting the alert message.");
            });
        })
    }

        $scope.getTriggers = function () {
            $scope.triggerss = "";
            $scope.errorMsg = "";

            $http({
                method: "GET",
                url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
            }).then(function(result) {

                $http({
                    method: "GET",
                    url: "/proxy?url=http://" + result.data.host + "/v1/triggerss",
                }).then(function successCallback( html ) {
                    $scope.conditions = html.data;
                }, function errorCallback(response){
                    console.log("alerts.controller fail"+response.status);
                    toaster.pop('error', "Error", "There was an issue with retrieving the triggerss.");
                });

            });

        };

        $scope.postTriggers = function(){
            $scope.errorMsg = "";

            var currentTime = moment().utc().toISOString();
            var triggerMessage = $scope.triggerMessage;
            var dataObj = {
                title: "short string",
                description: "long string",
                type: "event type string",
                userid: "just some string for now"
            }
            $http({
                method: "GET",
                url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
            }).then(function(result) {

                $http.post(
                    "/proxy?url=http://" + result.data.host + "/v1/triggers",
                    dataObj
                ).then(function successCallback(res) {
                    $scope.message = res;
                    $scope.getEvents();
                    $scope.alertMessage = null;
                    toaster.pop('success', "Success", "The trigger was successfully posted.")

                }, function errorCallback(res) {
                    console.log("alerter.controller fail"+res.status);

                    toaster.pop('error', "Error", "There was a problem submitting the trigger message.");
                });
            })
        }

        $scope.getTriggerById = function () {
            $scope.triggerById = "";
            $scope.triggerId = "";
            $scope.errorMsg = "";

            $http({
                method: "GET",
                url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
            }).then(function(result) {

                $http({
                    method: "GET",
                    url: "/proxy?url=http://" + result.data.host + "/v1/conditions/:"+triggerId,
                }).then(function successCallback( html ) {
                    $scope.trigger = html.data;
                }, function errorCallback(response){
                    console.log("alerts.controller fail"+response.status);
                    toaster.pop('error', "Error", "There was an issue with retrieving the trigger.");
                });

            });

        };

        $scope.deleteTriggerById = function(){
            $scope.errorMsg = "";
            $scope.triggerId = "";
            var alertMessage = $scope.alertMessage;

            $http({
                method: "GET",
                url: "/proxy?url=http://pz-discover.cf.piazzageo.io/api/v1/resources/pz-alerter"
            }).then(function(result) {

                $http.delete(
                    "/proxy?url=http://" + result.data.host + "/v1/conditions/:id"+triggerId
                    ).then(function successCallback(res) {
                    $scope.message = res;
                    $scope.getTriggers();
                    $scope.alertMessage = null;
                    toaster.pop('success', "Success", "The trigger was successfully deleted.")

                }, function errorCallback(res) {
                    console.log("alerter.controller fail"+res.status);

                    toaster.pop('error', "Error", "There was a problem deleting the trigger message.");
                });
            })
        }

}

})();<|endoftext|>


<commit_before>'use strict';

angular.module('linkToMyApp').controller('MainCtrl', function ($scope, $http, $rootScope) {

    $rootScope.app_name = "Dashboard";

    $scope.chartReady = function (){

    };
    //TODO : Make a Service for Graph creation
    function createTableData(){

        var tableData = {
                      "type": "Table",
                      "displayed": true,
                      "cssStyle": "height:300px; width:100%; margin-top: 20px;",
                      "data": {
                        "cols": [],
                        "rows": [],
                      },
                      "options": {
                        "title": "Installs & Clicks",
                        "isStacked": "true",
                        "fill": 20,
                        "displayExactValues": true
                        },
                      "formatters": {}
                    };

            var cols = [{
                            "id": "name",
                            "label": "Name",
                            "type": "string"
                          },
                          {
                            "id": "clicks",
                            "label": "Clicks",
                            "type": "number"
                          },
                          {
                            "id": "installs",
                            "label": "Installs",
                            "type": "number"
                          }];

            var rows = [];

            var length = $scope.referers.length,element = null;
            for (var i = 0; i < length; i++) {
              element = $scope.referers[i];

              var object = {
                            "c": [
                              {
                                "f": element.referal
                              },
                              {
                                "v": element.link_clicks_count
                              },
                              {
                                "v": element.installs_count
                              }
                            ]
                          };
                rows.push(object);
            }

            tableData.data.cols = cols;
            tableData.data.rows = rows;

            return tableData;
        }

    function createGraphData(){

        var graphData = {
                      "type": "AreaChart",
                      "displayed": true,
                      "cssStyle": "height:600px; width:100%;",
                      "data": {
                        "cols": [],
                        "rows": []
                      },
                      "options": {
                        "title": $scope.currentType,
                        "isStacked": "true",
                        "fill": 20,
                        "displayExactValues": true,
                        "vAxis": {
                          "title": $scope.currentType,
                          "gridlines": {
                            "count": 5
                          }
                        },
                        "hAxis": {
                          "title": "Date"
                        }
                      },
                      "formatters": {}
                    };

            var cols = [{"id":"date","label":"date","type":"string"}];
            var rows = [];
            var order = ["date"];

            for (var col in $scope.graphData) {

                var element = $scope.graphData[col];
                var ref = null;

                for (var index = element.length - 1; index >= 0; index--) {
                    ref = element[index];
                    var indexOrder = order.indexOf(ref.referal);
                    if (indexOrder == -1) {
                        order.push(ref.referal);
                        indexOrder = order.indexOf(ref.referal);
                        cols[indexOrder] = {"id":ref.referal,"label":ref.referal,"type":"number"};
                    };
                };
            }

            graphData.data.cols = cols;

            for (var col in $scope.graphData) {

                var element = $scope.graphData[col];
                var ref = null;
                var object = {
                            "c": [
                              {
                                "f": col
                              }]
                          };

                for (var i = order.length - 1; i > 0; i--) {
                    object.c.push({"v":0});
                };

                for (var index = element.length - 1; index >= 0; index--) {
                    ref = element[index];
                    var indexOrder = order.indexOf(ref.referal);

                    object.c[indexOrder] = {"v":ref.link_clicks_count};
                };

                rows.push(object);
            }

            graphData.data.rows = rows;

            return graphData;
        }

    /**************************** Data **************************/
    //TODO : Make a Service for Http calls
        function updateReferrers(){
            console.log("update");
            $http({method: 'GET', url: 'http://'+window.location.host+'/api/app_links'}).
                success(function(data, status, headers, config) {

            $scope.referers = data;
            $scope.table = createTableData();

            }).
            error(function(data, status, headers, config) {

            });
        }

        updateReferrers();

        /*setInterval(function(){

            updateReferrers();

        }, 30000);*/

        $scope.$watch("referers",function(){

            callGraph();

        });

    //TODO : Make a Service for Http calls
    function callGraph(){

            var params = "?";
            for (var i = $scope.ref.length - 1; i >= 0; i--) {
                var refName = $scope.ref[i];
                params += "referals[]="+refName;
                if (i != 0) {
                    params += "&"
                };
            };

            //TODO : make ng-hide work
            if ($scope.ref.length == 0) {
                $("#graph").css("visibility","hidden");
            }
            else{
                $("#graph").css("visibility","visible");
            }

            if ($scope.currentType == "Clicks") {
                var url = 'http://'+window.location.host+'/api/app_links/clicks'+params;
            }
            else{
                var url = 'http://'+window.location.host+'/api/app_links/installs'+params;
            }

            console.log(url);
            $http({method: 'GET', url:url}).
              success(function(data, status, headers, config) {
                console.log(JSON.stringify(data));
                $scope.graphData = data;
                $scope.graph = createGraphData();
            }).
            error(function(data, status, headers, config) {

            });
    };

    $scope.ref = [];
    $scope.indexChecks = {};

    $scope.change = function(index){

        if($("#check_"+index).is(':checked')){
            $scope.ref.push($scope.referers[index].referal);
        }
        else{
            var index = $scope.ref.indexOf($scope.referers[index].referal);
            $scope.ref.splice(index,1);
        }

        console.log("indexes "+JSON.stringify($scope.indexChecks));
        callGraph();
    }

    $scope.currentType = "Clicks";
    //TODO : bind a model object
    $scope.click = function (){
        $scope.currentType = "Clicks";
        callGraph();
    }

    $scope.install = function(){
        $scope.currentType = "Installs";
        callGraph();
    }

  });<commit_msg>fix installs<commit_after>'use strict';

angular.module('linkToMyApp').controller('MainCtrl', function ($scope, $http, $rootScope) {

    $rootScope.app_name = "Dashboard";

    $scope.chartReady = function (){

    };
    //TODO : Make a Service for Graph creation
    function createTableData(){

        var tableData = {
                      "type": "Table",
                      "displayed": true,
                      "cssStyle": "height:300px; width:100%; margin-top: 20px;",
                      "data": {
                        "cols": [],
                        "rows": [],
                      },
                      "options": {
                        "title": "Installs & Clicks",
                        "isStacked": "true",
                        "fill": 20,
                        "displayExactValues": true
                        },
                      "formatters": {}
                    };

            var cols = [{
                            "id": "name",
                            "label": "Name",
                            "type": "string"
                          },
                          {
                            "id": "clicks",
                            "label": "Clicks",
                            "type": "number"
                          },
                          {
                            "id": "installs",
                            "label": "Installs",
                            "type": "number"
                          }];

            var rows = [];

            var length = $scope.referers.length,element = null;
            for (var i = 0; i < length; i++) {
              element = $scope.referers[i];

              var object = {
                            "c": [
                              {
                                "f": element.referal
                              },
                              {
                                "v": element.link_clicks_count
                              },
                              {
                                "v": element.installs_count
                              }
                            ]
                          };
                rows.push(object);
            }

            tableData.data.cols = cols;
            tableData.data.rows = rows;

            return tableData;
        }

    function createGraphData(){

        var graphData = {
                      "type": "AreaChart",
                      "displayed": true,
                      "cssStyle": "height:600px; width:100%;",
                      "data": {
                        "cols": [],
                        "rows": []
                      },
                      "options": {
                        "title": $scope.currentType,
                        "isStacked": "true",
                        "fill": 20,
                        "displayExactValues": true,
                        "vAxis": {
                          "title": $scope.currentType,
                          "gridlines": {
                            "count": 5
                          }
                        },
                        "hAxis": {
                          "title": "Date"
                        }
                      },
                      "formatters": {}
                    };

            var cols = [{"id":"date","label":"date","type":"string"}];
            var rows = [];
            var order = ["date"];

            for (var col in $scope.graphData) {

                var element = $scope.graphData[col];
                var ref = null;

                for (var index = element.length - 1; index >= 0; index--) {
                    ref = element[index];
                    var indexOrder = order.indexOf(ref.referal);
                    if (indexOrder == -1) {
                        order.push(ref.referal);
                        indexOrder = order.indexOf(ref.referal);
                        cols[indexOrder] = {"id":ref.referal,"label":ref.referal,"type":"number"};
                    };
                };
            }

            graphData.data.cols = cols;

            for (var col in $scope.graphData) {

                var element = $scope.graphData[col];
                var ref = null;
                var object = {
                            "c": [
                              {
                                "f": col
                              }]
                          };

                for (var i = order.length - 1; i > 0; i--) {
                    object.c.push({"v":0});
                };

                for (var index = element.length - 1; index >= 0; index--) {
                    ref = element[index];
                    var indexOrder = order.indexOf(ref.referal);

                    if ($scope.currentType == "Clicks") {
                        object.c[indexOrder] = {"v":ref.link_clicks_count};
                    }
                    else{
                        object.c[indexOrder] = {"v":ref.installs_count};
                    }

                };

                rows.push(object);
            }

            graphData.data.rows = rows;

            return graphData;
        }

    /**************************** Data **************************/
    //TODO : Make a Service for Http calls
        function updateReferrers(){
            console.log("update");
            $http({method: 'GET', url: 'http://'+window.location.host+'/api/app_links'}).
                success(function(data, status, headers, config) {

            $scope.referers = data;
            $scope.table = createTableData();

            }).
            error(function(data, status, headers, config) {

            });
        }

        updateReferrers();

        /*setInterval(function(){

            updateReferrers();

        }, 30000);*/

        $scope.$watch("referers",function(){

            callGraph();

        });

    //TODO : Make a Service for Http calls
    function callGraph(){

            var params = "?";
            for (var i = $scope.ref.length - 1; i >= 0; i--) {
                var refName = $scope.ref[i];
                params += "referals[]="+refName;
                if (i != 0) {
                    params += "&"
                };
            };

            //TODO : make ng-hide work
            if ($scope.ref.length == 0) {
                $("#graph").css("visibility","hidden");
            }
            else{
                $("#graph").css("visibility","visible");
            }

            if ($scope.currentType == "Clicks") {
                var url = 'http://'+window.location.host+'/api/app_links/clicks'+params;
            }
            else{
                var url = 'http://'+window.location.host+'/api/app_links/installs'+params;
            }

            console.log(url);
            $http({method: 'GET', url:url}).
              success(function(data, status, headers, config) {
                console.log(JSON.stringify(data));
                $scope.graphData = data;
                $scope.graph = createGraphData();
            }).
            error(function(data, status, headers, config) {

            });
    };

    $scope.ref = [];
    $scope.indexChecks = {};

    $scope.change = function(index){

        if($("#check_"+index).is(':checked')){
            $scope.ref.push($scope.referers[index].referal);
        }
        else{
            var index = $scope.ref.indexOf($scope.referers[index].referal);
            $scope.ref.splice(index,1);
        }

        console.log("indexes "+JSON.stringify($scope.indexChecks));
        callGraph();
    }

    $scope.currentType = "Clicks";
    //TODO : bind a model object
    $scope.click = function (){
        $scope.currentType = "Clicks";
        callGraph();
    }

    $scope.install = function(){
        $scope.currentType = "Installs";
        callGraph();
    }

  });<|endoftext|>


<commit_before>import {
  activeSaleArtworks,
  featuredAuction,
  featuredFair,
  featuredGene,
  geneArtworks,
  popularArtists,
} from "./fetch"
import { map, assign, keys, without, shuffle, slice } from "lodash"
import Artwork from "schema/artwork/index"
import { GraphQLList } from "graphql"

const RESULTS_SIZE = 20

const moduleResults = {
  active_bids: ({ rootValue: { lotStandingLoader } }) =>
    activeSaleArtworks(lotStandingLoader),
  current_fairs: ({ rootValue: { fairsLoader, filterArtworksLoader } }) => {
    return featuredFair(fairsLoader).then(fair => {
      if (fair) {
        return filterArtworksLoader({
          fair_id: fair.id,
          for_sale: true,
          size: 60,
        }).then(({ hits }) => {
          return slice(shuffle(hits), 0, RESULTS_SIZE)
        })
      }
    })
  },
  followed_artist: ({ rootValue: { filterArtworksLoader }, params }) => {
    return filterArtworksLoader({
      artist_id: params.followed_artist_id,
      for_sale: true,
      size: RESULTS_SIZE,
    }).then(({ hits }) => hits)
  },
  followed_artists: ({ rootValue: { followedArtistsArtworksLoader } }) => {
    return followedArtistsArtworksLoader({
      for_sale: true,
      size: RESULTS_SIZE,
    }).then(({ body }) => body)
  },
  followed_galleries: ({ rootValue: { followedProfilesArtworksLoader } }) => {
    return followedProfilesArtworksLoader({
      for_sale: true,
      size: 60,
    }).then(({ body }) => {
      return slice(shuffle(body), 0, RESULTS_SIZE)
    })
  },
  genes: ({
    rootValue: { filterArtworksLoader, followedGenesLoader },
    params: { id },
  }) => {
    if (id) {
      return geneArtworks(filterArtworksLoader, id, RESULTS_SIZE)
    }
    // Backward compatibility for Force.
    return featuredGene(followedGenesLoader).then(gene => {
      if (gene) {
        return geneArtworks(filterArtworksLoader, gene.id, RESULTS_SIZE)
      }
    })
  },
  generic_gene: ({ rootValue: { filterArtworksLoader }, params }) => {
    return filterArtworksLoader(
      assign({}, params, { size: RESULTS_SIZE, for_sale: true })
    ).then(({ hits }) => hits)
  },
  live_auctions: ({ rootValue: { salesLoader, saleArtworksLoader } }) => {
    return featuredAuction(salesLoader).then(auction => {
      if (auction) {
        return saleArtworksLoader(auction.id, {
          size: RESULTS_SIZE,
        }).then(({ body }) => {
          return map(body, "artwork")
        })
      }
    })
  },
  popular_artists: ({ rootValue: { filterArtworksLoader, deltaLoader } }) => {
    // TODO This appears to largely replicate Gravity’s /api/v1/artists/popular endpoint
    return popularArtists(deltaLoader).then(artists => {
      const ids = without(keys(artists), "cached", "context_type")
      return filterArtworksLoader({
        artist_ids: ids,
        size: RESULTS_SIZE,
        sort: "-partner_updated_at",
      }).then(({ hits }) => hits)
    })
  },
  recommended_works: ({ rootValue: { homepageSuggestedArtworksLoader } }) => {
    return homepageSuggestedArtworksLoader({
      limit: RESULTS_SIZE,
    })
  },
  related_artists: ({ rootValue: { filterArtworksLoader }, params }) => {
    return filterArtworksLoader({
      artist_id: params.related_artist_id,
      for_sale: true,
      size: RESULTS_SIZE,
    }).then(({ hits }) => hits)
  },
  saved_works: ({ rootValue: { savedArtworksLoader } }) => {
    return savedArtworksLoader({
      size: RESULTS_SIZE,
      sort: "-position",
    })
  },
  similar_to_saved_works: ({
    rootValue: { savedArtworksLoader, similarArtworksLoader, artworksLoader },
  }) => {
    return savedArtworksLoader({
      size: RESULTS_SIZE,
      sort: "-position",
    }).then(works => {
      return similarArtworksLoader(
        { artwork_id: map(works, "_id").slice(0, 7) }
      ).then(ids => {
        return artworksLoader({ ids })
      })
    })
  },
  similar_to_recently_viewed: ({
    rootValue: { meLoader, similarArtworksLoader, artworksLoader },
  }) => {
    return meLoader().then(({ recently_viewed_artwork_ids }) => {
      if (recently_viewed_artwork_ids.length === 0) {
        return []
      }
      const ids = recently_viewed_artwork_ids.slice(0, 7)
      return similarArtworksLoader({ artwork_id: ids }).then(similarIds => {
        return artworksLoader({ similarIds })
      })
    })
  },
  recently_viewed_works: ({ rootValue: { meLoader, artworksLoader } }) => {
    return meLoader().then(({ recently_viewed_artwork_ids }) => {
      if (recently_viewed_artwork_ids.length === 0) {
        return []
      }
      const ids = recently_viewed_artwork_ids.slice(0, RESULTS_SIZE)
      return artworksLoader({ ids })
    })
  },
}

export default {
  type: new GraphQLList(Artwork.type),
  resolve: ({ key, display, params }, options, request, { rootValue }) => {
    if (display) {
      return moduleResults[key]({ rootValue, params: params || {} })
    }
  },
}
<commit_msg>fix data issue in similar to recently viewed module<commit_after>import {
  activeSaleArtworks,
  featuredAuction,
  featuredFair,
  featuredGene,
  geneArtworks,
  popularArtists,
} from "./fetch"
import { map, assign, keys, without, shuffle, slice } from "lodash"
import Artwork from "schema/artwork/index"
import { GraphQLList } from "graphql"

const RESULTS_SIZE = 20

const moduleResults = {
  active_bids: ({ rootValue: { lotStandingLoader } }) =>
    activeSaleArtworks(lotStandingLoader),
  current_fairs: ({ rootValue: { fairsLoader, filterArtworksLoader } }) => {
    return featuredFair(fairsLoader).then(fair => {
      if (fair) {
        return filterArtworksLoader({
          fair_id: fair.id,
          for_sale: true,
          size: 60,
        }).then(({ hits }) => {
          return slice(shuffle(hits), 0, RESULTS_SIZE)
        })
      }
    })
  },
  followed_artist: ({ rootValue: { filterArtworksLoader }, params }) => {
    return filterArtworksLoader({
      artist_id: params.followed_artist_id,
      for_sale: true,
      size: RESULTS_SIZE,
    }).then(({ hits }) => hits)
  },
  followed_artists: ({ rootValue: { followedArtistsArtworksLoader } }) => {
    return followedArtistsArtworksLoader({
      for_sale: true,
      size: RESULTS_SIZE,
    }).then(({ body }) => body)
  },
  followed_galleries: ({ rootValue: { followedProfilesArtworksLoader } }) => {
    return followedProfilesArtworksLoader({
      for_sale: true,
      size: 60,
    }).then(({ body }) => {
      return slice(shuffle(body), 0, RESULTS_SIZE)
    })
  },
  genes: ({
    rootValue: { filterArtworksLoader, followedGenesLoader },
    params: { id },
  }) => {
    if (id) {
      return geneArtworks(filterArtworksLoader, id, RESULTS_SIZE)
    }
    // Backward compatibility for Force.
    return featuredGene(followedGenesLoader).then(gene => {
      if (gene) {
        return geneArtworks(filterArtworksLoader, gene.id, RESULTS_SIZE)
      }
    })
  },
  generic_gene: ({ rootValue: { filterArtworksLoader }, params }) => {
    return filterArtworksLoader(
      assign({}, params, { size: RESULTS_SIZE, for_sale: true })
    ).then(({ hits }) => hits)
  },
  live_auctions: ({ rootValue: { salesLoader, saleArtworksLoader } }) => {
    return featuredAuction(salesLoader).then(auction => {
      if (auction) {
        return saleArtworksLoader(auction.id, {
          size: RESULTS_SIZE,
        }).then(({ body }) => {
          return map(body, "artwork")
        })
      }
    })
  },
  popular_artists: ({ rootValue: { filterArtworksLoader, deltaLoader } }) => {
    // TODO This appears to largely replicate Gravity’s /api/v1/artists/popular endpoint
    return popularArtists(deltaLoader).then(artists => {
      const ids = without(keys(artists), "cached", "context_type")
      return filterArtworksLoader({
        artist_ids: ids,
        size: RESULTS_SIZE,
        sort: "-partner_updated_at",
      }).then(({ hits }) => hits)
    })
  },
  recommended_works: ({ rootValue: { homepageSuggestedArtworksLoader } }) => {
    return homepageSuggestedArtworksLoader({
      limit: RESULTS_SIZE,
    })
  },
  related_artists: ({ rootValue: { filterArtworksLoader }, params }) => {
    return filterArtworksLoader({
      artist_id: params.related_artist_id,
      for_sale: true,
      size: RESULTS_SIZE,
    }).then(({ hits }) => hits)
  },
  saved_works: ({ rootValue: { savedArtworksLoader } }) => {
    return savedArtworksLoader({
      size: RESULTS_SIZE,
      sort: "-position",
    })
  },
  similar_to_saved_works: ({
    rootValue: { savedArtworksLoader, similarArtworksLoader, artworksLoader },
  }) => {
    return savedArtworksLoader({
      size: RESULTS_SIZE,
      sort: "-position",
    }).then(works => {
      return similarArtworksLoader(
        { artwork_id: map(works, "_id").slice(0, 7) }
      ).then(ids => {
        return artworksLoader({ ids })
      })
    })
  },
  similar_to_recently_viewed: ({
    rootValue: { meLoader, similarArtworksLoader, artworksLoader },
  }) => {
    return meLoader().then(({ recently_viewed_artwork_ids }) => {
      if (recently_viewed_artwork_ids.length === 0) {
        return []
      }
      const recentlyViewedIds = recently_viewed_artwork_ids.slice(0, 7)
      return similarArtworksLoader({ artwork_id: recentlyViewedIds }).then(ids => {
        return artworksLoader({ ids })
      })
    })
  },
  recently_viewed_works: ({ rootValue: { meLoader, artworksLoader } }) => {
    return meLoader().then(({ recently_viewed_artwork_ids }) => {
      if (recently_viewed_artwork_ids.length === 0) {
        return []
      }
      const ids = recently_viewed_artwork_ids.slice(0, RESULTS_SIZE)
      return artworksLoader({ ids })
    })
  },
}

export default {
  type: new GraphQLList(Artwork.type),
  resolve: ({ key, display, params }, options, request, { rootValue }) => {
    if (display) {
      return moduleResults[key]({ rootValue, params: params || {} })
    }
  },
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014, Gregg Tavares.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Gregg Tavares. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

"use strict";

define(
  [ './virtualsocket',
    './netplayer',
  ], function(VirtualSocket, NetPlayer) {

  var emptyMsg = { };

  /**
   * @typedef {Object} GameServer~Options
   * @property {string} gameId id of game needed to rendezvous
   *           with controllers.
   * @property {string=} controllerUrl url of the controller. If
   *       not passed in assumes it's the same as the game except
   *       'index.html'. In other words if the game's url is
   *       http://foo/bar/game.html it will assume the controller
   *       is at http://foo/bar/index.html if you don't set the
   *       controllerUrl.
   */

  /**
   * GameServer is used by a game to talk to the various
   * controllers of the people playing the game.
   *
   * @alias GameServer
   * @constructor
   * @param {GameServer~Options} options options.
   */
  var GameServer = function(options) {
    var _connected = false;
    var _socket;
    // Used in case the game tries to send messages to the server before it's connected.
    // This way the game does not have to wait for a connection to send startup messages.
    // Not sure there's a point to that :(
    var _sendQueue = [];
    var _numPlayers = 0;
    var _players = {};  // by id
    var _totalPlayerCount = 0;
    var _eventListeners = {};

    /**
     * Event that we've connected to the relaysever
     *
     * @event GameServer#connected
     */

    /**
     * Event that we've been disconnected from the relaysever
     *
     * @event GameServer#disconnected
     */

    /**
     * Event that a new player has joined the game.
     *
     * @event GameServer#playerconnected
     * @param {NetPlayer} netPlayer a NetPlayer used to communicate
     *        with the controller for the player.
     */

    /**
     * Adds an event listener for the given event type. Valid
     * commands include 'connect' (we connected to the relayserver),
     * 'disconnect' we were disconnected from the relaysefver,
     * 'playerconnect' a new player has joined the game.
     *
     * @param {string} eventType name of event
     * @param {GameServer~Listener} listener callback to call for
     *        event.
     */
    this.addEventListener = function(eventType, listener) {
      _eventListeners[eventType] = listener;
    };

    /**
     * @callback GameServer~Listener
     * @param {Object} data data from sender.
     */

    /**
     * Removes an eventListener
     * @param {string} eventType name of event
     */
    this.removeEventListener = function(eventType) {
      _eventListeners[eventType] = undefined;
    };

    var sendEvent_ = function(eventType, args) {
      var fn = _eventListeners[eventType];
      if (fn) {
        fn.apply(this, args);
      }
    }.bind(this);

    var startPlayer_ = function(msg) {
      var id = msg.id;
      var name = msg.name;

      // Ignore if we already have a player with this id.
      if (_players[id]) {
        return;
      }

      // Make up a name if no name given.
      if (!name) {
        name = "Player" + (++_totalPlayerCount);
      }

      var player = new NetPlayer(this, id);
      _players[id] = player;
      ++_numPlayers;
      sendEvent_('playerconnect', [player, name]);
    }.bind(this);

    var getPlayer_ = function(id) {
      var player = _players[id];
      return player;
    }.bind(this);

    var updatePlayer_ = function(msg) {
      var player = getPlayer_(msg.id);
      if (!player)
        return;
      player.sendEvent_(msg.data.cmd, [msg.data.data]); // FIX: Seems like gameserver should not know how to deal with this.
    }.bind(this);

    var removePlayer_ = function(msg) {
      var id = msg.id;
      var player = _players[id];
      if (player) {
        player.sendEvent_('disconnect', []);
        delete _players[id];
        --_numPlayers;
      }
    }.bind(this);

    var messageHandlers = {
      start: startPlayer_,
      update: updatePlayer_,
      remove: removePlayer_,
    };

    var processMessage_ = function(msg) {
      var fn = messageHandlers[msg.cmd];
      if (fn) {
        fn(msg);
      } else {
        console.error("Unknown Message: " + msg.cmd);
      }
    }.bind(this);

    /**
     * True if we're connected to the relayserver
     * @returns {Boolean} true if were connected.
     */
    this.isConnected = function() {
      return _connected;
    };

    var connect_ = function() {
      _socket = new VirtualSocket();
      _sendQueue = [];
      _socket.on('connect', connected_.bind(this));
      _socket.on('message', processMessage_.bind(this));
      _socket.on('disconnect', disconnected_.bind(this));
    }.bind(this);

    var connected_ = function() {
      _connected = true;
      for (var ii = 0; ii < _sendQueue.length; ++ii) {
        _socket.send(_sendQueue[ii]);
      }
      _sendQueue = [];
      sendEvent_('connect');
    }.bind(this);

    var disconnected_ = function() {
      _connected = false;
      sendEvent_('disconnect');
      while (_numPlayers > 0) {
        for (var id in _players) {
          if (Object.hasOwnProperty.call(_players, id)) {
            removePlayer_(id);
          }
          break;
        }
      }
      connect_();
    }.bind(this);

    var send_ = function(msg) {
      if (_connected == WebSocket.CONNECTING) {
        _sendQueue.push(msg);
      } else {
        _socket.send(msg);
      }
    }.bind(this);

    /**
     * This sends a command to the 'relayserver'. The relaysever uses 'cmd' to figure out what to do
     *  and 'id' to figure out which client this is for. 'data' will be delieved to that client.
     *
     * Only NetPlayer should call this.
     *
     * @private
     * @param {String} cmd name of command to send
     * @param {String} id id of client to relay command to
     * @param {Object=} data a jsonifiable object to send.
     */
    this.sendCmd = function(cmd, id, data) {
      if (data === undefined) {
        data = emptyMsg;
      }
      var msg = {
        cmd: cmd,
        id: id,
        data: data
      };
      send_(msg);
    };

    /**
     * Sends a command to all controllers connected to this server.
     * It's effectively the same as iterating over all NetPlayers
     * returned in playerconnect events and calling sendCmd on each
     * one. The difference is only one message is sent from the
     * server (here) to the relayserver. The relayserver then sends
     * the same message to each client.
     *
     * @param {String} cmd name of command to send
     * @param {Object=} data a jsonifyable object.
     */
    this.broadcastCmd = function(cmd, data) {
      if (data === undefined) {
        data = emptyMsg;
      }
      this.sendCmd('broadcast', {cmd: cmd, data: data});
    };

    connect_();

    if (!options.controllerUrl) {
      var url = window.location.href;
      var subs = {"#": 1, "?": 1, "/": 0};
      Object.keys(subs).forEach(function(c) {
        var ndx = subs[c] ? url.indexOf(c) : url.lastIndexOf(c);
        if (ndx >= 0) {
          url = url.substr(0, ndx);
        }
      });
      options.controllerUrl = url + "/index.html";
    }
    this.sendCmd("server", -1, options);
  };

  return GameServer;
});

<commit_msg>make it possible to use GameServer from node.<commit_after>/*
 * Copyright 2014, Gregg Tavares.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Gregg Tavares. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

(function() {

var globalObject = this;

define(
  [ './virtualsocket',
    './netplayer',
  ], function(VirtualSocket, NetPlayer) {

  "use strict";

  var emptyMsg = { };

  /**
   * @typedef {Object} GameServer~Options
   * @property {string} gameId id of game needed to rendezvous
   *           with controllers.
   * @property {string=} controllerUrl url of the controller. If
   *       not passed in assumes it's the same as the game except
   *       'index.html'. In other words if the game's url is
   *       http://foo/bar/game.html it will assume the controller
   *       is at http://foo/bar/index.html if you don't set the
   *       controllerUrl.
   * @property {Socket?} socket Socket to use for communications
   */

  /**
   * GameServer is used by a game to talk to the various
   * controllers of the people playing the game.
   *
   * @alias GameServer
   * @constructor
   * @param {GameServer~Options} options options.
   */
  var GameServer = function(options) {
    var _connected = false;
    var _socket;
    // Used in case the game tries to send messages to the server before it's connected.
    // This way the game does not have to wait for a connection to send startup messages.
    // Not sure there's a point to that :(
    var _sendQueue = [];
    var _numPlayers = 0;
    var _players = {};  // by id
    var _totalPlayerCount = 0;
    var _eventListeners = {};

    /**
     * Event that we've connected to the relaysever
     *
     * @event GameServer#connected
     */

    /**
     * Event that we've been disconnected from the relaysever
     *
     * @event GameServer#disconnected
     */

    /**
     * Event that a new player has joined the game.
     *
     * @event GameServer#playerconnected
     * @param {NetPlayer} netPlayer a NetPlayer used to communicate
     *        with the controller for the player.
     */

    /**
     * Adds an event listener for the given event type. Valid
     * commands include 'connect' (we connected to the relayserver),
     * 'disconnect' we were disconnected from the relaysefver,
     * 'playerconnect' a new player has joined the game.
     *
     * @param {string} eventType name of event
     * @param {GameServer~Listener} listener callback to call for
     *        event.
     */
    this.addEventListener = function(eventType, listener) {
      _eventListeners[eventType] = listener;
    };

    /**
     * @callback GameServer~Listener
     * @param {Object} data data from sender.
     */

    /**
     * Removes an eventListener
     * @param {string} eventType name of event
     */
    this.removeEventListener = function(eventType) {
      _eventListeners[eventType] = undefined;
    };

    var sendEvent_ = function(eventType, args) {
      var fn = _eventListeners[eventType];
      if (fn) {
        fn.apply(this, args);
      }
    }.bind(this);

    var startPlayer_ = function(msg) {
      var id = msg.id;
      var name = msg.name;

      // Ignore if we already have a player with this id.
      if (_players[id]) {
        return;
      }

      // Make up a name if no name given.
      if (!name) {
        name = "Player" + (++_totalPlayerCount);
      }

      var player = new NetPlayer(this, id);
      _players[id] = player;
      ++_numPlayers;
      sendEvent_('playerconnect', [player, name]);
    }.bind(this);

    var getPlayer_ = function(id) {
      var player = _players[id];
      return player;
    }.bind(this);

    var updatePlayer_ = function(msg) {
      var player = getPlayer_(msg.id);
      if (!player)
        return;
      player.sendEvent_(msg.data.cmd, [msg.data.data]); // FIX: Seems like gameserver should not know how to deal with this.
    }.bind(this);

    var removePlayer_ = function(msg) {
      var id = msg.id;
      var player = _players[id];
      if (player) {
        player.sendEvent_('disconnect', []);
        delete _players[id];
        --_numPlayers;
      }
    }.bind(this);

    var messageHandlers = {
      start: startPlayer_,
      update: updatePlayer_,
      remove: removePlayer_,
    };

    var processMessage_ = function(msg) {
      var fn = messageHandlers[msg.cmd];
      if (fn) {
        fn(msg);
      } else {
        console.error("Unknown Message: " + msg.cmd);
      }
    }.bind(this);

    /**
     * True if we're connected to the relayserver
     * @returns {Boolean} true if were connected.
     */
    this.isConnected = function() {
      return _connected;
    };

    var connect_ = function() {
      _socket = options.socket || new VirtualSocket();
      _sendQueue = [];
      _socket.on('connect', connected_.bind(this));
      _socket.on('message', processMessage_.bind(this));
      _socket.on('disconnect', disconnected_.bind(this));
    }.bind(this);

    var connected_ = function() {
      _connected = true;
      for (var ii = 0; ii < _sendQueue.length; ++ii) {
        _socket.send(_sendQueue[ii]);
      }
      _sendQueue = [];
      sendEvent_('connect');
    }.bind(this);

    var disconnected_ = function() {
      _connected = false;
      sendEvent_('disconnect');
      while (_numPlayers > 0) {
        for (var id in _players) {
          if (Object.hasOwnProperty.call(_players, id)) {
            removePlayer_(id);
          }
          break;
        }
      }
      connect_();
    }.bind(this);

    var send_ = function(msg) {
      if (globalObject.WebSocket && _connected == globalObject.WebSocket.CONNECTING) {
        _sendQueue.push(msg);
      } else {
        _socket.send(msg);
      }
    }.bind(this);

    /**
     * This sends a command to the 'relayserver'. The relaysever uses 'cmd' to figure out what to do
     *  and 'id' to figure out which client this is for. 'data' will be delieved to that client.
     *
     * Only NetPlayer should call this.
     *
     * @private
     * @param {String} cmd name of command to send
     * @param {String} id id of client to relay command to
     * @param {Object=} data a jsonifiable object to send.
     */
    this.sendCmd = function(cmd, id, data) {
      if (data === undefined) {
        data = emptyMsg;
      }
      var msg = {
        cmd: cmd,
        id: id,
        data: data
      };
      send_(msg);
    };

    /**
     * Sends a command to all controllers connected to this server.
     * It's effectively the same as iterating over all NetPlayers
     * returned in playerconnect events and calling sendCmd on each
     * one. The difference is only one message is sent from the
     * server (here) to the relayserver. The relayserver then sends
     * the same message to each client.
     *
     * @param {String} cmd name of command to send
     * @param {Object=} data a jsonifyable object.
     */
    this.broadcastCmd = function(cmd, data) {
      if (data === undefined) {
        data = emptyMsg;
      }
      this.sendCmd('broadcast', {cmd: cmd, data: data});
    };

    connect_();

    if (!options.controllerUrl && globalObject.location) {
      var url = globalObject.location.href;
      var subs = {"#": 1, "?": 1, "/": 0};
      Object.keys(subs).forEach(function(c) {
        var ndx = subs[c] ? url.indexOf(c) : url.lastIndexOf(c);
        if (ndx >= 0) {
          url = url.substr(0, ndx);
        }
      });
      options.controllerUrl = url + "/index.html";
    }
    this.sendCmd("server", -1, options);
  };

  return GameServer;
});

}());

<|endoftext|>


<commit_before>DV.Schema = function() {
  this.models       = {};
  this.views        = {};
  this.states       = {};
  this.helpers      = {};
  this.events       = {};
  this.elements     = {};
  this.text         = {};
  this.data         = {
    zoomLevel               : 700,
    pageWidthPadding        : 20,
    additionalPaddingOnPage : 30,
    state                   : { page: { previous: 0, current: 0, next: 1 } }
  };
};

// Imports the document's JSON representation into the DV.Schema form that
// the models expect.
DV.Schema.prototype.importCanonicalDocument = function(json) {
  // Ensure that IDs start with 1 as the lowest id.
  _.uniqueId();
  // Ensure at least empty arrays for sections.
  json.sections               = _.sortBy(json.sections || [], function(sec){ return sec.page; });
  json.annotations            = json.annotations || [];
  json.canonicalURL           = json.canonical_url;
  this.document               = DV.jQuery.extend(true, {}, json);
  // Everything after this line is for back-compatibility.
  this.data.title             = json.title;
  this.data.totalPages        = json.pages;
  this.data.totalAnnotations  = json.annotations.length;
  this.data.sections          = json.sections;
  this.data.chapters          = [];
  this.data.annotationsById   = {};
  this.data.annotationsByPage = {};
  this.data.translationsURL   = json.translations_url;
  _.each(json.annotations, DV.jQuery.proxy(this.loadAnnotation, this));
};

// Load an annotation into the Schema, starting from the canonical format.
DV.Schema.prototype.loadAnnotation = function(anno) {
  if (anno.id) anno.server_id = anno.id;
  var idx     = anno.page - 1;
  anno.id     = anno.id || _.uniqueId();
  anno.title  = anno.title || 'Untitled Note'; // TRANSLATE
  anno.text   = anno.content || '';
  anno.access = anno.access || 'public';
  anno.type   = anno.location && anno.location.image ? 'region' : 'page';
  if (anno.type === 'region') {
    var loc = DV.jQuery.map(anno.location.image.split(','), function(n, i) { return parseInt(n, 10); });
    anno.y1 = loc[0]; anno.x2 = loc[1]; anno.y2 = loc[2]; anno.x1 = loc[3];
  }else if(anno.type === 'page'){
    anno.y1 = 0; anno.x2 = 0; anno.y2 = 0; anno.x1 = 0;
  }
  this.data.annotationsById[anno.id] = anno;
  var page = this.data.annotationsByPage[idx] = this.data.annotationsByPage[idx] || [];
  var insertionIndex = _.sortedIndex(page, anno, function(a){ return a.y1; });
  page.splice(insertionIndex, 0, anno);
  return anno;
};
<commit_msg>Moved translations endpoint to under resources<commit_after>DV.Schema = function() {
  this.models       = {};
  this.views        = {};
  this.states       = {};
  this.helpers      = {};
  this.events       = {};
  this.elements     = {};
  this.text         = {};
  this.data         = {
    zoomLevel               : 700,
    pageWidthPadding        : 20,
    additionalPaddingOnPage : 30,
    state                   : { page: { previous: 0, current: 0, next: 1 } }
  };
};

// Imports the document's JSON representation into the DV.Schema form that
// the models expect.
DV.Schema.prototype.importCanonicalDocument = function(json) {
  // Ensure that IDs start with 1 as the lowest id.
  _.uniqueId();
  // Ensure at least empty arrays for sections.
  json.sections               = _.sortBy(json.sections || [], function(sec){ return sec.page; });
  json.annotations            = json.annotations || [];
  json.canonicalURL           = json.canonical_url;
  this.document               = DV.jQuery.extend(true, {}, json);
  // Everything after this line is for back-compatibility.
  this.data.title             = json.title;
  this.data.totalPages        = json.pages;
  this.data.totalAnnotations  = json.annotations.length;
  this.data.sections          = json.sections;
  this.data.chapters          = [];
  this.data.annotationsById   = {};
  this.data.annotationsByPage = {};
  this.data.translationsURL   = json.resources.translations_url;
  _.each(json.annotations, DV.jQuery.proxy(this.loadAnnotation, this));
};

// Load an annotation into the Schema, starting from the canonical format.
DV.Schema.prototype.loadAnnotation = function(anno) {
  if (anno.id) anno.server_id = anno.id;
  var idx     = anno.page - 1;
  anno.id     = anno.id || _.uniqueId();
  anno.title  = anno.title || 'Untitled Note'; // TRANSLATE
  anno.text   = anno.content || '';
  anno.access = anno.access || 'public';
  anno.type   = anno.location && anno.location.image ? 'region' : 'page';
  if (anno.type === 'region') {
    var loc = DV.jQuery.map(anno.location.image.split(','), function(n, i) { return parseInt(n, 10); });
    anno.y1 = loc[0]; anno.x2 = loc[1]; anno.y2 = loc[2]; anno.x1 = loc[3];
  }else if(anno.type === 'page'){
    anno.y1 = 0; anno.x2 = 0; anno.y2 = 0; anno.x1 = 0;
  }
  this.data.annotationsById[anno.id] = anno;
  var page = this.data.annotationsByPage[idx] = this.data.annotationsByPage[idx] || [];
  var insertionIndex = _.sortedIndex(page, anno, function(a){ return a.y1; });
  page.splice(insertionIndex, 0, anno);
  return anno;
};
<|endoftext|>