<commit_before>import isShallowEqual from 'is-equal-shallow';
import { RequestCanceledError } from './errors';

import * as uriInfo from './uri-info';

/**
 * @typedef {import('./direct-link-query').Query} Query
 */

/**
 * Hypothesis-related state for a specific tab.
 *
 * This should really be named `TabState` but that will conflict with the
 * `TabState` class below. That class needs to be renamed first.
 *
 * @typedef State
 * @prop {'active'|'inactive'|'errored'} state - Whether or not H is active on the page
 * @prop {number} annotationCount -
 *   The count of annotations on the page visible to the user, as returned by
 *   the badge API
 * @prop {boolean} extensionSidebarInstalled - Has the client been loaded into this tab?
 * @prop {boolean} ready - Is the tab loaded and ready for the client to be loaded?
 * @prop {Error} [error]
 * @prop {Query} [directLinkQuery]
 */

/**
 * The default H state for a new browser tab.
 *
 * @type {State}
 */
const DEFAULT_STATE = {
  state: 'inactive',
  annotationCount: 0,
  extensionSidebarInstalled: false,
  ready: false,
  error: undefined,
};

/**
 * @typedef {{ [tabId: number]: State }} TabStateMap
 */

/**
 * TabState stores the Hypothesis-related state for tabs in the current browser
 * session.
 *
 * @param {TabStateMap} initialState - Initial state information for tabs, eg.
 *   from a persistent store.
 * @param {(tabId: number, current: undefined|State) => any} onchange -
 *   Callback invoked when state for a tab changes
 */
export default function TabState(initialState, onchange) {
  const self = this;

  /**
   * Current Hypothesis-related state for each tab.
   *
   * @type {TabStateMap}
   */
  let currentState;

  /**
   * @typedef BadgeRequest - this object is used to cancel a pending badge request
   * @prop {function} cancel - cancelation function to abort pending request
   * @prop {number} waitMs - the current waiting time after which the request will be invoked
   */

  // This variable contains a map between the tabId and current badge request.
  /** @type {Map<number, BadgeRequest>} */
  const pendingAnnotationCountRequests = new Map();

  // This variable contains a map between a URI and the associated annotation count.
  /** @type {Map<string, number>} */
  const annotationCountCache = new Map();

  this.onchange = onchange || null;

  /**
   * Replaces the H state for all tabs.
   *
   * @param {TabStateMap} newState - A dictionary mapping tab ID to tab state objects.
   *                   The provided state will be merged with the default
   *                   state for a tab.
   */
  this.load = function (newState) {
    /** @type {TabStateMap} */
    const newCurrentState = {};
    Object.keys(newState).forEach(function (tabId) {
      newCurrentState[tabId] = Object.assign(
        {},
        DEFAULT_STATE,
        newState[tabId]
      );
    });
    currentState = newCurrentState;
  };

  /**
   * Mark a tab as having Hypothesis loaded in it.
   *
   * @param {number} tabId
   */
  this.activateTab = function (tabId) {
    this.setState(tabId, { state: 'active' });
  };

  /**
   * Mark a tab as not having Hypothesis loaded.
   *
   * @param {number} tabId
   */
  this.deactivateTab = function (tabId) {
    this.setState(tabId, { state: 'inactive' });
  };

  /**
   * Mark a tab as having encountered an error when trying to load Hypothesis into it.
   *
   * @param {number} tabId
   * @param {Error} error
   */
  this.errorTab = function (tabId, error) {
    this.setState(tabId, {
      state: 'errored',
      error: error,
    });
  };

  /**
   * Remove Hypothesis-related state about a given tab.
   *
   * @param {number} tabId
   */
  this.clearTab = function (tabId) {
    this.setState(tabId, null);
    pendingAnnotationCountRequests.delete(tabId);
  };

  /**
   * Return the current Hypothesis-related state for a tab.
   *
   * @param {number} tabId
   * @return {State}
   */
  this.getState = function (tabId) {
    if (!currentState[tabId]) {
      return DEFAULT_STATE;
    }
    return currentState[tabId];
  };

  /**
   * Return the badge count for a given tab.
   *
   * @param {number} tabId
   */
  this.annotationCount = function (tabId) {
    return this.getState(tabId).annotationCount;
  };

  /**
   * Return `true` if Hypothesis is loaded into a given tab.
   *
   * @param {number} tabId
   */
  this.isTabActive = function (tabId) {
    return this.getState(tabId).state === 'active';
  };

  /**
   * Return `true` if Hypothesis is not loaded in a given tab.
   *
   * @param {number} tabId
   */
  this.isTabInactive = function (tabId) {
    return this.getState(tabId).state === 'inactive';
  };

  /**
   * Return `true` if an error occurred while trying to load Hypothesis into a given tab.
   *
   * @param {number} tabId
   */
  this.isTabErrored = function (tabId) {
    return this.getState(tabId).state === 'errored';
  };

  /**
   * Updates the H state for a tab.
   *
   * @param {number} tabId - The ID of the tab being updated
   * @param {Partial<State>|null} stateUpdate
   */
  this.setState = function (tabId, stateUpdate) {
    /** @type {State|undefined} */
    let newState;
    if (stateUpdate) {
      newState = Object.assign({}, this.getState(tabId), stateUpdate);
      if (newState.state !== 'errored') {
        newState.error = undefined;
      }
    }

    if (isShallowEqual(newState, currentState[tabId])) {
      return;
    }

    if (newState) {
      currentState[tabId] = newState;
    } else {
      delete currentState[tabId];
    }

    if (self.onchange) {
      self.onchange(tabId, newState);
    }
  };

  /**
   * Request the current annotation count for the tab's URL.
   *
   * @param {number} tabId The id of the tab.
   * @param {string} tabUrl The URL of the tab.
   * @return {Promise<void>}
   */
  this.updateAnnotationCount = async function (tabId, tabUrl) {
    const INITIAL_WAIT_MS = 1000;
    const MAX_WAIT_MS = 3000;
    const CACHE_EXPIRATION_MS = 3000;

    const pendingRequest = pendingAnnotationCountRequests.get(tabId);

    let url;
    try {
      url = uriInfo.uriForBadgeRequest(tabUrl);
    } catch {
      return;
    }

    const annotationCount = annotationCountCache.get(url);
    if (annotationCount !== undefined) {
      this.setState(tabId, { annotationCount });
      return;
    }

    const wait = Math.min(
      pendingRequest?.waitMs ?? INITIAL_WAIT_MS,
      MAX_WAIT_MS
    );

    pendingRequest?.cancel();

    const debouncedFetch = new Promise((resolve, reject) => {
      const timerId = setTimeout(async () => {
        let count = annotationCountCache.get(url);
        if (count !== undefined) {
          resolve(count);
          return;
        }

        try {
          count = await uriInfo.fetchAnnotationCount(url);
          annotationCountCache.set(url, count);
          setTimeout(
            () => annotationCountCache.delete(url),
            CACHE_EXPIRATION_MS
          );
        } catch {
          count = 0;
        }
        pendingAnnotationCountRequests.delete(tabId);
        resolve(count);
      }, wait);

      pendingAnnotationCountRequests.set(tabId, {
        cancel: () => {
          clearTimeout(timerId);
          reject(new RequestCanceledError('Badge request canceled'));
        },
        waitMs: wait * 2,
      });
    });

    try {
      const annotationCount = await debouncedFetch;
      this.setState(tabId, { annotationCount });
    } catch (error) {
      if (error instanceof RequestCanceledError) {
        // Do nothing
        return;
      }
      throw error;
    }
  };

  this.load(initialState || {});
}
<commit_msg>Simplify handling of optional `onchange` constructor argument<commit_after>import isShallowEqual from 'is-equal-shallow';
import { RequestCanceledError } from './errors';

import * as uriInfo from './uri-info';

/**
 * @typedef {import('./direct-link-query').Query} Query
 */

/**
 * Hypothesis-related state for a specific tab.
 *
 * This should really be named `TabState` but that will conflict with the
 * `TabState` class below. That class needs to be renamed first.
 *
 * @typedef State
 * @prop {'active'|'inactive'|'errored'} state - Whether or not H is active on the page
 * @prop {number} annotationCount -
 *   The count of annotations on the page visible to the user, as returned by
 *   the badge API
 * @prop {boolean} extensionSidebarInstalled - Has the client been loaded into this tab?
 * @prop {boolean} ready - Is the tab loaded and ready for the client to be loaded?
 * @prop {Error} [error]
 * @prop {Query} [directLinkQuery]
 */

/**
 * The default H state for a new browser tab.
 *
 * @type {State}
 */
const DEFAULT_STATE = {
  state: 'inactive',
  annotationCount: 0,
  extensionSidebarInstalled: false,
  ready: false,
  error: undefined,
};

/**
 * @typedef {{ [tabId: number]: State }} TabStateMap
 */

/**
 * TabState stores the Hypothesis-related state for tabs in the current browser
 * session.
 *
 * @param {TabStateMap} initialState - Initial state information for tabs, eg.
 *   from a persistent store.
 * @param {(tabId: number, current: undefined|State) => any} [onchange] -
 *   Callback invoked when state for a tab changes
 */
export default function TabState(initialState, onchange) {
  /**
   * Current Hypothesis-related state for each tab.
   *
   * @type {TabStateMap}
   */
  let currentState;

  /**
   * @typedef BadgeRequest - this object is used to cancel a pending badge request
   * @prop {function} cancel - cancelation function to abort pending request
   * @prop {number} waitMs - the current waiting time after which the request will be invoked
   */

  // This variable contains a map between the tabId and current badge request.
  /** @type {Map<number, BadgeRequest>} */
  const pendingAnnotationCountRequests = new Map();

  // This variable contains a map between a URI and the associated annotation count.
  /** @type {Map<string, number>} */
  const annotationCountCache = new Map();

  this.onchange = onchange;

  /**
   * Replaces the H state for all tabs.
   *
   * @param {TabStateMap} newState - A dictionary mapping tab ID to tab state objects.
   *                   The provided state will be merged with the default
   *                   state for a tab.
   */
  this.load = function (newState) {
    /** @type {TabStateMap} */
    const newCurrentState = {};
    Object.keys(newState).forEach(function (tabId) {
      newCurrentState[tabId] = Object.assign(
        {},
        DEFAULT_STATE,
        newState[tabId]
      );
    });
    currentState = newCurrentState;
  };

  /**
   * Mark a tab as having Hypothesis loaded in it.
   *
   * @param {number} tabId
   */
  this.activateTab = function (tabId) {
    this.setState(tabId, { state: 'active' });
  };

  /**
   * Mark a tab as not having Hypothesis loaded.
   *
   * @param {number} tabId
   */
  this.deactivateTab = function (tabId) {
    this.setState(tabId, { state: 'inactive' });
  };

  /**
   * Mark a tab as having encountered an error when trying to load Hypothesis into it.
   *
   * @param {number} tabId
   * @param {Error} error
   */
  this.errorTab = function (tabId, error) {
    this.setState(tabId, {
      state: 'errored',
      error: error,
    });
  };

  /**
   * Remove Hypothesis-related state about a given tab.
   *
   * @param {number} tabId
   */
  this.clearTab = function (tabId) {
    this.setState(tabId, null);
    pendingAnnotationCountRequests.delete(tabId);
  };

  /**
   * Return the current Hypothesis-related state for a tab.
   *
   * @param {number} tabId
   * @return {State}
   */
  this.getState = function (tabId) {
    if (!currentState[tabId]) {
      return DEFAULT_STATE;
    }
    return currentState[tabId];
  };

  /**
   * Return the badge count for a given tab.
   *
   * @param {number} tabId
   */
  this.annotationCount = function (tabId) {
    return this.getState(tabId).annotationCount;
  };

  /**
   * Return `true` if Hypothesis is loaded into a given tab.
   *
   * @param {number} tabId
   */
  this.isTabActive = function (tabId) {
    return this.getState(tabId).state === 'active';
  };

  /**
   * Return `true` if Hypothesis is not loaded in a given tab.
   *
   * @param {number} tabId
   */
  this.isTabInactive = function (tabId) {
    return this.getState(tabId).state === 'inactive';
  };

  /**
   * Return `true` if an error occurred while trying to load Hypothesis into a given tab.
   *
   * @param {number} tabId
   */
  this.isTabErrored = function (tabId) {
    return this.getState(tabId).state === 'errored';
  };

  /**
   * Updates the H state for a tab.
   *
   * @param {number} tabId - The ID of the tab being updated
   * @param {Partial<State>|null} stateUpdate
   */
  this.setState = function (tabId, stateUpdate) {
    /** @type {State|undefined} */
    let newState;
    if (stateUpdate) {
      newState = Object.assign({}, this.getState(tabId), stateUpdate);
      if (newState.state !== 'errored') {
        newState.error = undefined;
      }
    }

    if (isShallowEqual(newState, currentState[tabId])) {
      return;
    }

    if (newState) {
      currentState[tabId] = newState;
    } else {
      delete currentState[tabId];
    }

    if (this.onchange) {
      this.onchange(tabId, newState);
    }
  };

  /**
   * Request the current annotation count for the tab's URL.
   *
   * @param {number} tabId The id of the tab.
   * @param {string} tabUrl The URL of the tab.
   * @return {Promise<void>}
   */
  this.updateAnnotationCount = async function (tabId, tabUrl) {
    const INITIAL_WAIT_MS = 1000;
    const MAX_WAIT_MS = 3000;
    const CACHE_EXPIRATION_MS = 3000;

    const pendingRequest = pendingAnnotationCountRequests.get(tabId);

    let url;
    try {
      url = uriInfo.uriForBadgeRequest(tabUrl);
    } catch {
      return;
    }

    const annotationCount = annotationCountCache.get(url);
    if (annotationCount !== undefined) {
      this.setState(tabId, { annotationCount });
      return;
    }

    const wait = Math.min(
      pendingRequest?.waitMs ?? INITIAL_WAIT_MS,
      MAX_WAIT_MS
    );

    pendingRequest?.cancel();

    const debouncedFetch = new Promise((resolve, reject) => {
      const timerId = setTimeout(async () => {
        let count = annotationCountCache.get(url);
        if (count !== undefined) {
          resolve(count);
          return;
        }

        try {
          count = await uriInfo.fetchAnnotationCount(url);
          annotationCountCache.set(url, count);
          setTimeout(
            () => annotationCountCache.delete(url),
            CACHE_EXPIRATION_MS
          );
        } catch {
          count = 0;
        }
        pendingAnnotationCountRequests.delete(tabId);
        resolve(count);
      }, wait);

      pendingAnnotationCountRequests.set(tabId, {
        cancel: () => {
          clearTimeout(timerId);
          reject(new RequestCanceledError('Badge request canceled'));
        },
        waitMs: wait * 2,
      });
    });

    try {
      const annotationCount = await debouncedFetch;
      this.setState(tabId, { annotationCount });
    } catch (error) {
      if (error instanceof RequestCanceledError) {
        // Do nothing
        return;
      }
      throw error;
    }
  };

  this.load(initialState || {});
}
<|endoftext|>


<commit_before>function restoreRestClientTab(aEvent) {
  var store = Components.classes["@mozilla.org/browser/sessionstore;1"].
               getService(Components.interfaces.nsISessionStore);

  var theTab = aEvent.originalTarget;
  var theTabBrowser = gBrowser.getBrowserForTab(theTab);

  var tbRequestBody = theTabBrowser.contentDocument.getElementById('tbRequestBody');
  var tbRequestUrl = theTabBrowser.contentDocument.getElementById('tbRequestUrl');
  var requestMethod = theTabBrowser.contentDocument.getElementById('requestMethod');
  var reqMethodChilds = theTabBrowser.contentDocument.getElementById('requestMethod').childNodes[0].childNodes;
  var reqHeaderChilds = theTabBrowser.contentDocument.getElementById('reqHeaderChilds');
  if (tbRequestBody != null) {
    var data = store.getTabValue(aEvent.originalTarget, "tab-controller");
    var tabController = new TabController();
    if (data != "undefined") {
        tabController.fromString(data);
    }

    tbRequestBody.wrappedJSObject.value = tabController.requestBody;
    tbRequestUrl.wrappedJSObject.value = tabController.requestUrl;
    var reqHeaderList = tabController.headerList;
    for (headerKey in reqHeaderList) {
      var headerValue = reqHeaderList[headerKey];

      var item = theTabBrowser.contentDocument.createElement('treeitem');
      var row  = theTabBrowser.contentDocument.createElement('treerow');
      var c1   =  theTabBrowser.contentDocument.createElement('treecell');
      var c2   =  theTabBrowser.contentDocument.createElement('treecell');
      c1.setAttribute('label', headerKey);
      c2.setAttribute('label', headerValue);
      row.appendChild(c1);
      row.appendChild(c2);
      item.appendChild(row);
      reqHeaderChilds.appendChild(item);
    }

    var child = null;
    for (childIndex in reqMethodChilds) {
      var child = reqMethodChilds[childIndex];
      if (typeof child == 'object' && child.getAttribute('label') == tabController.requestMethod) {
        break;
      }
    }
    if (child != null) {
      requestMethod.wrappedJSObject.selectedItem = child;
    }

    // bring back the icon
    var func = function () { gBrowser.setIcon(theTab, "chrome://restclient/skin/logo16.png"); };
    setTimeout(func, 500);
  }
}
document.addEventListener("SSTabRestored", restoreRestClientTab, false);

function storeRestClientTab(aEvent) {
  var store = Components.classes["@mozilla.org/browser/sessionstore;1"].
              getService(Components.interfaces.nsISessionStore);

  var theTab = aEvent.originalTarget;
  var theTabBrowser = gBrowser.getBrowserForTab(theTab);
  var tabDocument = theTabBrowser.contentDocument.wrappedJSObject;

  var requestUrl = tabDocument.getElementById("tbRequestUrl").value;
  var requestMethod = tabDocument.getElementById("requestMethod").selectedItem.getAttribute('label');
  var requestBody = tabDocument.getElementById("tbRequestBody").value;
  var reqHeaderChilds = tabDocument.getElementById('reqHeaderChilds');
  var requestHeaderList = {};

  for (var i=reqHeaderChilds.childNodes.length-1 ; i>=0 ; i--){
    var headerKey = reqHeaderChilds.childNodes[i].childNodes[0].childNodes[0].getAttribute('label')
    var headerValue = reqHeaderChilds.childNodes[i].childNodes[0].childNodes[1].getAttribute('label')

    requestHeaderList[headerKey] = headerValue;
  }

  var tabController = new TabController();
  tabController.requestUrl = requestUrl;
  tabController.requestMethod = requestMethod;
  tabController.requestBody = requestBody;
  tabController.headerList = requestHeaderList;
  store.setTabValue(aEvent.originalTarget, "tab-controller", tabController.toString());
}
document.addEventListener("SSTabClosing", storeRestClientTab, false);

function ShutdownObserver() {
}
ShutdownObserver.prototype = {
  observe: function(subject, topic, data) {

    var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                           .getInterface(Components.interfaces.nsIWebNavigation)
                           .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                           .rootTreeItem
                           .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                           .getInterface(Components.interfaces.nsIDOMWindow);
    var container = mainWindow.getBrowser().tabContainer;
    for (var i=0; i<container.childNodes.length;i++) {
      var tabWindow = mainWindow.getBrowser().getBrowserForTab(
        container.childNodes[i]).
        contentWindow.wrappedJSObject;

      if (typeof tabWindow.restclient != "undefined") {
        tabWindow.restclient.storeRestClientTab(container.childNodes[i]);
      }
    }
  }
}
var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
observerService.addObserver(new ShutdownObserver(), "quit-application-requested", false);

function loadRestClient() {
    var theTab          = gBrowser.addTab('chrome://restclient/content/restclient.xul');
    theTab.label        = "REST Client";
    gBrowser.selectedTab = theTab;

    var func = function () { gBrowser.setIcon(theTab, "chrome://restclient/skin/logo16.png"); };
    setTimeout(func, 500);
}

<commit_msg>refactored tab storing<commit_after>function restoreRestClientTab(aEvent) {
  var store = Components.classes["@mozilla.org/browser/sessionstore;1"].
               getService(Components.interfaces.nsISessionStore);

  var theTab = aEvent.originalTarget;
  var theTabBrowser = gBrowser.getBrowserForTab(theTab);

  var tbRequestBody = theTabBrowser.contentDocument.getElementById('tbRequestBody');
  var tbRequestUrl = theTabBrowser.contentDocument.getElementById('tbRequestUrl');
  var requestMethod = theTabBrowser.contentDocument.getElementById('requestMethod');
  var reqMethodChilds = theTabBrowser.contentDocument.getElementById('requestMethod').childNodes[0].childNodes;
  var reqHeaderChilds = theTabBrowser.contentDocument.getElementById('reqHeaderChilds');
  if (tbRequestBody != null) {
    var data = store.getTabValue(aEvent.originalTarget, "tab-controller");
    var tabController = new TabController();
    if (data != "undefined") {
        tabController.fromString(data);
    }

    tbRequestBody.wrappedJSObject.value = tabController.requestBody;
    tbRequestUrl.wrappedJSObject.value = tabController.requestUrl;
    var reqHeaderList = tabController.headerList;
    for (headerKey in reqHeaderList) {
      var headerValue = reqHeaderList[headerKey];

      var item = theTabBrowser.contentDocument.createElement('treeitem');
      var row  = theTabBrowser.contentDocument.createElement('treerow');
      var c1   =  theTabBrowser.contentDocument.createElement('treecell');
      var c2   =  theTabBrowser.contentDocument.createElement('treecell');
      c1.setAttribute('label', headerKey);
      c2.setAttribute('label', headerValue);
      row.appendChild(c1);
      row.appendChild(c2);
      item.appendChild(row);
      reqHeaderChilds.appendChild(item);
    }

    var child = null;
    for (childIndex in reqMethodChilds) {
      var child = reqMethodChilds[childIndex];
      if (typeof child == 'object' && child.getAttribute('label') == tabController.requestMethod) {
        break;
      }
    }
    if (child != null) {
      requestMethod.wrappedJSObject.selectedItem = child;
    }

    // bring back the icon
    var func = function () { gBrowser.setIcon(theTab, "chrome://restclient/skin/logo16.png"); };
    setTimeout(func, 500);
  }
}
document.addEventListener("SSTabRestored", restoreRestClientTab, false);

function storeRestClientTab(aEvent) {
  var theTab = aEvent.originalTarget;
  var theTabBrowser = gBrowser.getBrowserForTab(theTab);
  var tabWindow = gBrowser.getBrowserForTab(theTab).contentWindow.wrappedJSObject;

  if (typeof tabWindow.restclient != "undefined") {
    tabWindow.restclient.storeRestClientTab(theTab);
  }
}
document.addEventListener("SSTabClosing", storeRestClientTab, false);

function ShutdownObserver() {}
ShutdownObserver.prototype = {
  observe: function(subject, topic, data) {

    var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                           .getInterface(Components.interfaces.nsIWebNavigation)
                           .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                           .rootTreeItem
                           .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                           .getInterface(Components.interfaces.nsIDOMWindow);
    var container = mainWindow.getBrowser().tabContainer;
    for (var i=0; i<container.childNodes.length;i++) {
      var tabWindow = mainWindow.getBrowser().getBrowserForTab(
        container.childNodes[i]).
        contentWindow.wrappedJSObject;

      if (typeof tabWindow.restclient != "undefined") {
        tabWindow.restclient.storeRestClientTab(container.childNodes[i]);
      }
    }
  }
}
var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
observerService.addObserver(new ShutdownObserver(), "quit-application-requested", false);

function loadRestClient() {
    var theTab          = gBrowser.addTab('chrome://restclient/content/restclient.xul');
    theTab.label        = "REST Client";
    gBrowser.selectedTab = theTab;

    var func = function () { gBrowser.setIcon(theTab, "chrome://restclient/skin/logo16.png"); };
    setTimeout(func, 500);
}

<|endoftext|>


<commit_before>(function() {
	"use strict";
	var	helpers = require("./../helpers"),
		appNotifications = require("./app-notifications"),
		helpers = require("./../helpers"),
		log = require("./../log"),
		_ = require("underscore");

    function init() {
		var iTunesInstallationInfo = require("./ios/ios-core/iTunes-installation-info"),
			iTunesErrorMessage = iTunesInstallationInfo.getErrorMessage();

        if(iTunesErrorMessage !== null) {
			helpers.abort(iTunesErrorMessage);
        }
    }

	function install(packageFile,packageName, provisionedDevices) {
		init();

		var iOSDevice = require("./ios/ios-core/ios-device");

		iOSDevice.waitForDevice();
		var deviceId = iOSDevice.getDeviceId();

		log.trace("device id %s", deviceId);

		var isFoundDeviceIdInProvision = _.find(provisionedDevices, function(device){
			return device === deviceId;
		});

		if(!isFoundDeviceIdInProvision || isFoundDeviceIdInProvision === undefined) {
			helpers.abort("The device is not included in the provisioning profile.");
		}

		var InstallationProxyClient = require("./ios/ios-mobile-device/installation-proxy/installation-proxy-client"),
		    installationProxyClient = new InstallationProxyClient();
		installationProxyClient.deployApplication(packageFile);
	}

	function sync(localToDevicePaths, appIdentifier) {
		init();

		var iOSDevice = require("./ios/ios-core/ios-device");
		iOSDevice.waitForDevice();

		var HouseArrestClient = require("./ios/ios-mobile-device/house-arrest/house-arrest-client"),
			houseArrestClient = new HouseArrestClient(),
			afcClientForAppDocuments = houseArrestClient.GetAfcClientForAppDocuments(appIdentifier);

        try {
            localToDevicePaths.forEach(function(data){
                afcClientForAppDocuments.transferFile(data.localPath, data.devicePath, data.relativeToProjectBasePath);
            });
        } catch(ex) {
            throw ex;
        }

		var NotificationProxyClient = require("./ios/ios-mobile-device/notification-proxy/notification-proxy-client"),
			notificationProxyClient = new NotificationProxyClient();
		notificationProxyClient.postNotification(appNotifications.ios.APP_REFRESH_WEBVIEW);
	}

	function requestDeviceById() {
		var iOSDevice = require("./ios/ios-core/ios-device");
		return iOSDevice.getDeviceId();
	}

	// function requestAllDevices() {
	// 	var iOSDevicesService = require("./ios/ios-core/ios-devices-service");
	// 	return iOSDevicesService.listDevices();
	// }

	function openLogStream() {
		init();

        var iOSDevice = require("./ios/ios-core/ios-device");
        iOSDevice.waitForDevice();

        var iOSSyslog = require("./ios/ios-core/ios-syslog");
        iOSSyslog.read();
	}

	function getDeviceProjectPath() {
		return "/Documents/";
	}

	exports.install = install;
	exports.sync = sync;
	exports.requestDeviceById = requestDeviceById;
	exports.openLogStream = openLogStream;
	exports.getDeviceProjectPath = getDeviceProjectPath;

})();<commit_msg>Fix list-devices command<commit_after>(function() {
	"use strict";
	var	helpers = require("./../helpers"),
		appNotifications = require("./app-notifications"),
		log = require("./../log"),
		Q = require("q"),
		_ = require("underscore");

    function init() {
		var iTunesInstallationInfo = require("./ios/ios-core/iTunes-installation-info"),
			iTunesErrorMessage = iTunesInstallationInfo.getErrorMessage();

        if(iTunesErrorMessage !== null) {
			helpers.abort(iTunesErrorMessage);
        }
    }

	function install(packageFile,packageName, provisionedDevices) {
		init();

		var iOSDevice = require("./ios/ios-core/ios-device");

		iOSDevice.waitForDevice();
		var deviceId = iOSDevice.getDeviceId();

		log.trace("device id %s", deviceId);

		var isFoundDeviceIdInProvision = _.find(provisionedDevices, function(device){
			return device === deviceId;
		});

		if(!isFoundDeviceIdInProvision || isFoundDeviceIdInProvision === undefined) {
			helpers.abort("The device is not included in the provisioning profile.");
		}

		var InstallationProxyClient = require("./ios/ios-mobile-device/installation-proxy/installation-proxy-client"),
		    installationProxyClient = new InstallationProxyClient();
		installationProxyClient.deployApplication(packageFile);
	}

	function sync(localToDevicePaths, appIdentifier) {
		init();

		var iOSDevice = require("./ios/ios-core/ios-device");
		iOSDevice.waitForDevice();

		var HouseArrestClient = require("./ios/ios-mobile-device/house-arrest/house-arrest-client"),
			houseArrestClient = new HouseArrestClient(),
			afcClientForAppDocuments = houseArrestClient.GetAfcClientForAppDocuments(appIdentifier);

        try {
            localToDevicePaths.forEach(function(data){
                afcClientForAppDocuments.transferFile(data.localPath, data.devicePath, data.relativeToProjectBasePath);
            });
        } catch(ex) {
            throw ex;
        }

		var NotificationProxyClient = require("./ios/ios-mobile-device/notification-proxy/notification-proxy-client"),
			notificationProxyClient = new NotificationProxyClient();
		notificationProxyClient.postNotification(appNotifications.ios.APP_REFRESH_WEBVIEW);
	}

	function requestDeviceById() {
		var iOSDevice = require("./ios/ios-core/ios-device");
		return iOSDevice.getDeviceId();
	}

	function requestAllDevices() {
		return Q.fcall(function(){
			return [];
		});
	}

	function openLogStream() {
		init();

        var iOSDevice = require("./ios/ios-core/ios-device");
        iOSDevice.waitForDevice();

        var iOSSyslog = require("./ios/ios-core/ios-syslog");
        iOSSyslog.read();
	}

	function getDeviceProjectPath() {
		return "/Documents/";
	}

	exports.install = install;
	exports.sync = sync;
	exports.requestDeviceById = requestDeviceById;
	exports.openLogStream = openLogStream;
	exports.getDeviceProjectPath = getDeviceProjectPath;
	exports.requestAllDevices = requestAllDevices;

})();<|endoftext|>


<commit_before>/*global require, process */
(function () {
    'use strict';

    var test = require('../scripts/whichTape.js'),
        AstroDate = require('../scripts/whichAstroDate'),
        Fire = require('../scripts/fire'),
        delay = 100,
        fireLoop = new Fire(),
        args = ['periphery methods'];

    if (!process.env.ASTRODATE_TAPE) {
        args.push({compact: true, name: 'All tests'});
    }

    args.push(function (t) {
        var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
            monthDays = ['31', '28', '31', '30', '31', '30', '31', '31', '30', '31', '30', '31'],
            monthDaysLeap = ['31', '29', '31', '30', '31', '30', '31', '31', '30', '31', '30', '31'],
            dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
            leapYears = [2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040, 2044, 2048, 2052, 2056, 2060, 2064, 2068, 2072, 2076, 2080, 2084, 2088, 2092],
            leapYearLength = leapYears.length,
            normalYears = [],
            normalYearsLength,
            astrodate,
            index;

        for (index = leapYears[0]; index < leapYears[leapYearLength - 1]; index += 1) {
            if (-1 === leapYears.indexOf(index)) {
                normalYears.push(index);
            }
        }

        normalYearsLength = normalYears.length;
        function mNd(index) {
            astrodate = new AstroDate([2013, index + 1]);
            t.equal(astrodate.format('MMMM'), monthNames[index], 'Month name match');
            t.equal(astrodate.daysInMonth(), monthDays[index], 'Days in month: normal year');

            astrodate = new AstroDate([2012, index + 1]);
            t.equal(astrodate.format('MMMM'), monthNames[index], 'Month name match');
            t.equal(astrodate.daysInMonth(), monthDaysLeap[index], 'Days in month: leap year');
        }

        fireLoop.run(12, mNd, delay);

        function lYears(index) {
            t.ok(new AstroDate([leapYears[index]]).isLeapYear(), 'Leap year');
        }

        fireLoop.run(leapYearLength, lYears, delay);

        function nYears(index) {
            t.ok(!new AstroDate([normalYears[index]]).isLeapYear(), 'Normal year');
        }

        fireLoop.run(normalYearsLength, nYears, delay);

        function dNames(index) {
            astrodate = new AstroDate([2013, 9, index + 1]);
            t.equal(astrodate.format('EEEE'), dayNames[index], 'Day names');
            t.equal(astrodate.format('D'), (244 + index).toString(), 'Day of year');
        }

        fireLoop.run(7, dNames, delay);
        t.end();
    });

    test.apply(null, args);
}());
<commit_msg>Changed periphery tests to match the other wrt to repeats<commit_after>/*global require, process */
(function () {
    'use strict';

    var test = require('../scripts/whichTape.js'),
        AstroDate = require('../scripts/whichAstroDate'),
        Fire = require('../scripts/fire'),
        delay = 100,
        fireSingle = new Fire(),
        args = ['periphery methods'];

    if (!process.env.ASTRODATE_TAPE) {
        args.push({
            compact: true,
            name: 'All tests'
        });
    }

    args.push(function (t) {
        var repeat = 50;

        function single() {
            var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
                monthDays = ['31', '28', '31', '30', '31', '30', '31', '31', '30', '31', '30', '31'],
                monthDaysLeap = ['31', '29', '31', '30', '31', '30', '31', '31', '30', '31', '30', '31'],
                dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
                leapYears = [2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040, 2044, 2048, 2052, 2056, 2060, 2064, 2068, 2072, 2076, 2080, 2084, 2088, 2092],
                leapYearLength = leapYears.length,
                fireLoop = new Fire(),
                normalYears = [],
                normalYearsLength,
                astrodate,
                index;

            for (index = leapYears[0]; index < leapYears[leapYearLength - 1]; index += 1) {
                if (-1 === leapYears.indexOf(index)) {
                    normalYears.push(index);
                }
            }

            normalYearsLength = normalYears.length;

            function mNd(index) {
                astrodate = new AstroDate([2013, index + 1]);
                t.equal(astrodate.format('MMMM'), monthNames[index], 'Month name match');
                t.equal(astrodate.daysInMonth(), monthDays[index], 'Days in month: normal year');

                astrodate = new AstroDate([2012, index + 1]);
                t.equal(astrodate.format('MMMM'), monthNames[index], 'Month name match');
                t.equal(astrodate.daysInMonth(), monthDaysLeap[index], 'Days in month: leap year');
            }

            fireLoop.run(12, mNd, delay);

            function lYears(index) {
                t.ok(new AstroDate([leapYears[index]]).isLeapYear(), 'Leap year');
            }

            fireLoop.run(leapYearLength, lYears, delay);

            function nYears(index) {
                t.ok(!new AstroDate([normalYears[index]]).isLeapYear(), 'Normal year');
            }

            fireLoop.run(normalYearsLength, nYears, delay);

            function dNames(index) {
                astrodate = new AstroDate([2013, 9, index + 1]);
                t.equal(astrodate.format('EEEE'), dayNames[index], 'Day names');
                t.equal(astrodate.format('D'), (244 + index).toString(), 'Day of year');
            }

            fireLoop.run(7, dNames, delay);
        }

        fireSingle.run(repeat, single, delay);
        t.end();
    });

    test.apply(null, args);
}());
<|endoftext|>


<commit_before>var debug = require('debug')('liftie:coerce');

module.exports = coerceStatus;

var map = {
  'o': 'open',
  'y': 'open',
  'yes': 'open',
  'ok': 'open',
  'open': 'open',
  'opened': 'open',
  'n': 'closed',
  'x': 'closed',
  'no': 'closed',
  'closed': 'closed',
  'closedfortheday': 'closed',
  'geschlossen': 'closed',
  'blank': 'closed',
  'h': 'hold',
  'hold': 'hold',
  'windhold': 'hold',
  'maintenancehold': 'hold',
  'onhold': 'hold',
  'standby': 'hold',
  'scheduled': 'scheduled',
  'onschedule': 'scheduled',
  'expected': 'scheduled',
  'delay': 'scheduled',
  'delayed': 'scheduled'
};

/**
 * More flexible slice. If from, or to are strings we determine their position in the string
 * and slice appropriately. In all other cases it works like normal slice
 */
function slice(str, from, to) {
  if (typeof from === 'string') {
    from = str.lastIndexOf(from) + from.length;
  }
  if (typeof to === 'string') {
    to = str.indexOf(to);
  }
  return str.slice(from, to);
}

/*
 * slice, trim, lowercase and coerce to standard liftie statuses
 * If no usuable status is found, return 'scheduled'
 */
function coerceStatus (status, start, stop) {
  var s;
  if (arguments.length > 1) {
    status = slice(status, start, stop);
  }
	status = status.replace(/[\s_\-]+/g, '').toLowerCase();
	s = map[status];
  if (!s) {
    debug('Uknown status %s. Treated as <scheduled>', status);
    s = 'scheduled';
  }
  return s;
}<commit_msg>safety -> hold for Kirkwood<commit_after>var debug = require('debug')('liftie:coerce');

module.exports = coerceStatus;

var map = {
  'o': 'open',
  'y': 'open',
  'yes': 'open',
  'ok': 'open',
  'open': 'open',
  'opened': 'open',
  'n': 'closed',
  'x': 'closed',
  'no': 'closed',
  'closed': 'closed',
  'closedfortheday': 'closed',
  'geschlossen': 'closed',
  'blank': 'closed',
  'h': 'hold',
  'hold': 'hold',
  'windhold': 'hold',
  'maintenancehold': 'hold',
  'safety': 'hold',
  'onhold': 'hold',
  'standby': 'hold',
  'scheduled': 'scheduled',
  'onschedule': 'scheduled',
  'expected': 'scheduled',
  'delay': 'scheduled',
  'delayed': 'scheduled'
};

/**
 * More flexible slice. If from, or to are strings we determine their position in the string
 * and slice appropriately. In all other cases it works like normal slice
 */
function slice(str, from, to) {
  if (typeof from === 'string') {
    from = str.lastIndexOf(from) + from.length;
  }
  if (typeof to === 'string') {
    to = str.indexOf(to);
  }
  return str.slice(from, to);
}

/*
 * slice, trim, lowercase and coerce to standard liftie statuses
 * If no usuable status is found, return 'scheduled'
 */
function coerceStatus (status, start, stop) {
  var s;
  if (arguments.length > 1) {
    status = slice(status, start, stop);
  }
	status = status.replace(/[\s_\-]+/g, '').toLowerCase();
	s = map[status];
  if (!s) {
    debug('Uknown status %s. Treated as <scheduled>', status);
    s = 'scheduled';
  }
  return s;
}<|endoftext|>


<commit_before>"use strict";

const request = require('request');
const config = require('../../../config');
const cfg = require('./config');
const db = require('../../database');
const server = require('../../server');
const io = server.io;

module.exports = (type, data) => {
    var grant_type,
        name,
        token;

    if (type == 'new') {
        grant_type = 'authorization_code';
        name = 'code';
        token = data;
    } else if (type == 'refresh') {
        grant_type = 'refresh_token';
        name = 'refresh_token';
        token = db.settings.get('api.streamlabs.auth.refresh_token').value();
    }

    var id = cfg.client_id,
        secret = cfg.client_secret,
        uri = `${config.server.url}/streamlabs/auth`,
        options = {
            url: 'https://streamlabs.com/api/v1.0/token',
            form: {
                grant_type: grant_type,
                client_id: id,
                client_secret: secret,
                redirect_uri: uri,
                [name]: token
            }
        };

    if (type == 'new') {

        request.post(options, (err, response, body) => {
            if (err) throw err;

            if (!err && response.statusCode == 200) {

                let json = JSON.parse(body),
                    date = new Date();

                date.setHours(date.getHours() + 1);

                db.settings.get('api.streamlabs')
                    .assign({
                        active: true,
                        auth: {
                            token: json.access_token,
                            refresh_token: json.refresh_token,
                            expiration_date: date
                        }
                    })
                    .value();
            }

            setTimeout(() => {
                io.emit('general', {
                    redirect: '/settings'
                });
            }, 3000);
        });
    } else if (type == 'refresh') {
        request.post(options, (err, response, body) => {
            if (err) throw err;

            if (!err && response.statusCode == 200) {

                let json = JSON.parse(body),
                    date = new Date();

                date.setHours(date.getHours() + 1);

                db.settings.get('api.streamlabs')
                    .assign({
                        auth: {
                            token: json.access_token,
                            refresh_token: json.refresh_token,
                            expiration_date: date
                        }
                    })
                    .value();
                require('./')();
            }
        });
    }

};
<commit_msg>small code optimization<commit_after>"use strict";

const request = require('request');
const config = require('../../../config');
const cfg = require('./config');
const db = require('../../database');
const server = require('../../server');
const io = server.io;

module.exports = (type, data) => {
    var grant_type,
        name,
        token;

    if (type == 'new') {
        grant_type = 'authorization_code';
        name = 'code';
        token = data;
    } else if (type == 'refresh') {
        grant_type = 'refresh_token';
        name = 'refresh_token';
        token = db.settings.get('api.streamlabs.auth.refresh_token').value();
    }

    var id = cfg.client_id,
        secret = cfg.client_secret,
        uri = `${config.server.url}/streamlabs/auth`,
        options = {
            url: 'https://streamlabs.com/api/v1.0/token',
            form: {
                grant_type: grant_type,
                client_id: id,
                client_secret: secret,
                redirect_uri: uri,
                [name]: token
            }
        };

    if (type == 'new') {

        request.post(options, (err, response, body) => {
            if (err) {
                console.log(err);
            } else if (response.statusCode == 200) {

                let json = JSON.parse(body),
                    date = new Date();

                date.setHours(date.getHours() + 1);

                db.settings.get('api.streamlabs')
                    .assign({
                        active: true,
                        auth: {
                            token: json.access_token,
                            refresh_token: json.refresh_token,
                            expiration_date: date
                        }
                    }).value();
            }

            setTimeout(() => {
                io.emit('general', {
                    redirect: '/settings'
                });
            }, 3000);
        });

    } else if (type == 'refresh') {
        request.post(options, (err, response, body) => {
            if (err) {
                console.log(err);
            } else if (response.statusCode == 200) {

                let json = JSON.parse(body),
                    date = new Date();

                date.setHours(date.getHours() + 1);

                db.settings.get('api.streamlabs')
                    .assign({
                        auth: {
                            token: json.access_token,
                            refresh_token: json.refresh_token,
                            expiration_date: date
                        }
                    }).value();

                require('./')();
            }
        });
    }
};
<|endoftext|>


<commit_before>var path = require('path');

module.exports = function(instance, def) {

	var __class = {
		_name: 'unknow',
		_column: {

		},
		_index: null,

		_collection: null,
		_db: null,

		init: function() {
			// support __name in each field
			var columns = _.encodeObject(this._column, '__type');
			try {
				_.each(columns, function(column, key) {
					(function(column, key) {
						var callback = arguments.callee;
						if(_.isArray(column)) {
							if(_.isObject(column[0]) && !_.isArray(column[0])) {
								_.each(column[0], function(v, k) {
									callback(column[0][k], key + '.$.' + k);
								});
							} else {
								callback(column[0], key);
							};
						} else {
							if(_.isObject(column)) {
								if(column.__type !== undefined) {
									column.__name = key;
								};
							};
						};
					})(columns[key], key);
				});
			} catch(ex) {
			};
			this._column = _.decodeObject(columns);
			// re-init column, support quick define: {}: object datatype, []: array datatype
			try {
				var tmp = {};
				var callback = function(res, value, key) {
					var args = arguments;
					var tmp = value;
					if(_.isArray(value)) {
						var a = {
							res: value[0]
						};
						args.callee(a, value[0], key + '.$');
						tmp = instance.base.types.array(a['res']);
						tmp.__name = key + '.$';
					} else if(_.isObject(value) && value.__type === undefined) {
						_.each(tmp, function(v, k) {
							var a = {
								res: v
							};
							args.callee(a, v, key + '.' + k);
							tmp[k] = a.res;
						});
						tmp = instance.base.types.object(tmp);
						tmp.__name = key;
					}
					res.res = tmp;
				};
				_.each(this._column, function(value, key) {
					var res = {
						res: value
					};
					callback(res, value, key);
					tmp[key] = res.res;
				});
				this._column = tmp;
			} catch(ex) {
				throw ex;
			};
			// call super
			this._super.apply(this, arguments);
		},

		getCollection: function(callback) {
			var log = this;
			if(this._collection) {
				callback(this._collection);
			} else {
				if(this.getDB()) {
					this.getDB().collection(this._name, function(err, collection) {
						if(!err) {
							this._collection = collection;
							callback(this._collection);
						} else {
							throw {
								func: 'getCollection',
								message: 'collection is null'
							};
						};
					});
				};
			};
		},

		getDB: function() {
			this._db === null ? this._db = def.db : null;
			if(this._db === null) {
				throw {
					func: 'getDB',
					message: 'db is null'
				};
			};
			return this._db;
		},

		create_validate: function(args) {
			// check default & require value
			_.each(this._column, function(column, name) {
				args[name] = column.validate(args[name]);
			});
			// remove all undefined property
			_.each(args, function(value, key) {
				if(value === undefined) {
					delete args[key];
				};
			});
			// return
			return args;
		},

		update_validate: function(selector, document, options) {
			var log = this;
			var self = this;
			var columns = this._column;
			/*
			* fields query
			*/
			// $inc: don't need to check datatype

			// $rename: doesn't support
			if(document.$rename) {
				throw {
					msg: "ekitjs doesn't support $rename"
				};
			};

			// $setOnInsert: use on upsert, check when create

			// $set: don't know how to check datatype yet. It's too complicated. I trust you.

			// $unset: doesn't support
			if(document.$unset) {
				throw {
					msg: "ekitjs doesn't support $unset"
				};
			};

			/*
			* array query
			*/

			// $addToSet: I trust you.

			// $pop: I trust you.

			// $pullAll: I trust you.

			// $pull: I trust you.

			// $pushAll: I trust you.

			// $push: I trust you.

			return true;
		},

		create: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result) {
				callback.call(self, err, result);
			});
			// validate args
			try {
				args[0] = this.create_validate(args[0]);
			} catch(ex) {
				log.log({
					func: 'create',
					args: JSON.stringify(arguments),
					ex: ex
				});
				callback.call(self, ex, null);
				return;
			};
			// execute
			this.getCollection(function(collection) {
				collection.insert.apply(collection, args);
			});
			return;
		},

		read: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			// remove disable _id field
			var fields = {};
			_.each(args, function(value, key, l, opt) {
				if(opt.index === 0) {
					return;
				};
				if(value.fields !== undefined) {
					if(value.fields._id !== undefined) {
						delete args[key].fields._id;
					};
					fields = args[key].fields;
				};
			});
			// find
			this.getCollection(function(collection) {
				collection.find.apply(collection, args).toArray(function(err, data) {
					if(err) {
						return callback.call(self, err, []);
					} else {
						/*
						* init function field (store === false)
						*/
						// init loop injection
						if(self.___loop_injection === true) {
							return callback.call(self, null, data);
						};
						self.___loop_injection = true;

						// callback & reset loop_injection
						var success = function() {
							try {
								self.___loop_injection = false;
								delete self.___loop_injection;
							} catch(ex) {
							};
							callback.call(self, null, data);
						};

						// get ids
						var ids = [];
						var keyData = {};
						_.each(data, function(value) {
							ids.push(value._id);
							keyData[value._id] = value;
						});

						// get hide & show fields
						var isQueryHide = null;
						var show_fields = [];
						var hidden_fields = [];
						_.each(fields, function(value, key) {
							if(value === 0) {
								hidden_fields.push(key);
								isQueryHide = true;
							} else if(value === 1) {
								show_fields.push(key);
								isQueryHide = false;
							}
							;
						});

						// filter function fields in column with hidden fields
						var funcs = {};
						_.each(self._column, function(column, name) {
							switch(column.__type) {
							case 'func':
								funcs[name] = column;
								break;
							case 'object':
								funcs[name] = column.getFuncField();
								break;
							};
						});
						funcs = _.encodeObject(funcs, '__type');
						_.each(funcs, function(value, key) {
							// just keep function with store === false
							if(value.get('store') !== false) {
								delete funcs[key];
								return;
							};
							if(isQueryHide === true) {
								// remove function field in hidden_fields
								_.each(hidden_fields, function(field) {
									if(key.indexOf(field) === 0) {
										delete funcs[key];
									}
								});
							} else if(isQueryHide === false) {
								// remove function field not in show_fields
								var isIn = false;
								_.each(show_fields, function(field) {
									if(key.indexOf(field) === 0) {
										isIn = true;
									};
								});
								if(!isIn) {
									delete funcs[key];
								};
							}
							;
						});

						// sort funcs by sequence
						_.sortObject(funcs, function(a, b) {
							return a.get('sequence') - b.get('sequence');
						});

						// rebuild function fields sequence
						var sequence = [];
						_.each(funcs, function(func, key, l, opt) {
							sequence.push((function(func, key, opt) {
								return function(fcb) {
									func.get('get').call(self, ids, keyData, function(funcData) {
										_.each(funcData, function(fData, _id) {
											var tmp = {};
											if(func.get('multi') === true) {
												tmp[key] = fData[key];
											} else {
												tmp[key] = fData;
											};
											_.setObject(keyData[_id], tmp);
										});
										data = _.values(keyData);
										// callback & reset loop_injection
										if(opt.isLast === true) {
											success();
										};
										fcb();
									});
								};
							})(func, key, opt));
						});

						if(sequence.length === 0) {
							success();
						} else {
							(function(sequence) {
								var args = arguments;
								if(sequence.length === 0) {
									return;
								};
								var func = sequence.shift();
								func(function() {
									args.callee(sequence);
								});
							})(sequence);
						};
					};
				});
			});
		},

		update: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result, extra) {
				if(err) {
					callback.call(self, err, 0, extra);
				} else {
					callback.call(self, null, result, extra);
				};
			});
			// init full args
			if(args.length === 3) {
				args[3] = args[2];
				args[2] = {};
			};
			// normal update
			var func_update = function() {
				// validate args
				try {
					// remove $setOnInsert
					delete args[1].$setOnInsert;
					// start validate
					this.update_validate(args[0], args[1], args[2]);
				} catch(ex) {
					log.log({
						func: 'update',
						args: JSON.stringify(arguments),
						ex: ex
					});
					callback.call(self, ex, 0);
					return;
				};
				// start update
				this.getCollection(function(collection) {
					collection.update.apply(collection, args);
				});
			};

			// check upsert
			var upsert = args[2].upsert;
			delete args[2].upsert;
			if(upsert === true) {
				this.read(args[0], {
					_id: 1
				}, function(err, data) {
					if(data.length > 0) {
						func_update.call(self);
					} else {
						// create using $setOnInsert
						if(args[1].$setOnInsert !== undefined) {
							self.create(args[1].$setOnInsert, function(e, r) {
								if(e) {
									args[3](e, 0);
								} else {
									args[3](null, 1, r);
								};
							});
						} else {
							args[3]({
								msg: 'can not find $setOnInsert'
							}, 0);
						};
					};
				});
			} else {
				func_update.call(self);
			};
			return;
		},

		'delete': function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result) {
				callback.call(self, err, result);
			});
			// delete
			this.getCollection(function(collection) {
				collection.remove.apply(collection, args);
			});
		}
	};

	instance.base.model = instance.base.base.extend(__class);

	// config model static type
	instance.base.model.__type = 'model';
	instance.base.model.__extend = instance.base.model.extend;
	instance.base.model.extend = function() {
		var res = instance.base.model.__extend.apply(instance.base.model, arguments);
		res.__type = instance.base.model.__type;
		return res;
	};
};
<commit_msg>add force parameter to pass all validator and go back to original Mongo Native Driver<commit_after>var path = require('path');

module.exports = function(instance, def) {

	var __class = {
		_name: 'unknow',
		_column: {

		},
		_index: null,

		_collection: null,
		_db: null,

		init: function() {
			// support __name in each field
			var columns = _.encodeObject(this._column, '__type');
			try {
				_.each(columns, function(column, key) {
					(function(column, key) {
						var callback = arguments.callee;
						if(_.isArray(column)) {
							if(_.isObject(column[0]) && !_.isArray(column[0])) {
								_.each(column[0], function(v, k) {
									callback(column[0][k], key + '.$.' + k);
								});
							} else {
								callback(column[0], key);
							};
						} else {
							if(_.isObject(column)) {
								if(column.__type !== undefined) {
									column.__name = key;
								};
							};
						};
					})(columns[key], key);
				});
			} catch(ex) {
			};
			this._column = _.decodeObject(columns);
			// re-init column, support quick define: {}: object datatype, []: array datatype
			try {
				var tmp = {};
				var callback = function(res, value, key) {
					var args = arguments;
					var tmp = value;
					if(_.isArray(value)) {
						var a = {
							res: value[0]
						};
						args.callee(a, value[0], key + '.$');
						tmp = instance.base.types.array(a['res']);
						tmp.__name = key + '.$';
					} else if(_.isObject(value) && value.__type === undefined) {
						_.each(tmp, function(v, k) {
							var a = {
								res: v
							};
							args.callee(a, v, key + '.' + k);
							tmp[k] = a.res;
						});
						tmp = instance.base.types.object(tmp);
						tmp.__name = key;
					}
					res.res = tmp;
				};
				_.each(this._column, function(value, key) {
					var res = {
						res: value
					};
					callback(res, value, key);
					tmp[key] = res.res;
				});
				this._column = tmp;
			} catch(ex) {
				throw ex;
			};
			// call super
			this._super.apply(this, arguments);
		},

		getCollection: function(callback) {
			var log = this;
			if(this._collection) {
				callback(this._collection);
			} else {
				if(this.getDB()) {
					this.getDB().collection(this._name, function(err, collection) {
						if(!err) {
							this._collection = collection;
							callback(this._collection);
						} else {
							throw {
								func: 'getCollection',
								message: 'collection is null'
							};
						};
					});
				};
			};
		},

		getDB: function() {
			this._db === null ? this._db = def.db : null;
			if(this._db === null) {
				throw {
					func: 'getDB',
					message: 'db is null'
				};
			};
			return this._db;
		},

		create_validate: function(args) {
			// check default & require value
			_.each(this._column, function(column, name) {
				args[name] = column.validate(args[name]);
			});
			// remove all undefined property
			_.each(args, function(value, key) {
				if(value === undefined) {
					delete args[key];
				};
			});
			// return
			return args;
		},

		update_validate: function(selector, document, options) {
			var log = this;
			var self = this;
			var columns = this._column;
			/*
			* fields query
			*/
			// $inc: don't need to check datatype

			// $rename: doesn't support
			if(document.$rename) {
				throw {
					msg: "ekitjs doesn't support $rename"
				};
			};

			// $setOnInsert: use on upsert, check when create

			// $set: don't know how to check datatype yet. It's too complicated. I trust you.

			// $unset: doesn't support
			if(document.$unset) {
				throw {
					msg: "ekitjs doesn't support $unset"
				};
			};

			/*
			* array query
			*/

			// $addToSet: I trust you.

			// $pop: I trust you.

			// $pullAll: I trust you.

			// $pull: I trust you.

			// $pushAll: I trust you.

			// $push: I trust you.

			return true;
		},

		create: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result) {
				callback.call(self, err, result);
			});
			// check force = true (pass the validator)
			if(args[1].force !== true){
				// validate args
				try {
					args[0] = this.create_validate(args[0]);
				} catch(ex) {
					log.log({
						func: 'create',
						args: JSON.stringify(arguments),
						ex: ex
					});
					callback.call(self, ex, null);
					return;
				};
			};
			delete args[1].force;
			// execute
			this.getCollection(function(collection) {
				collection.insert.apply(collection, args);
			});
			return;
		},

		read: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			// remove disable _id field
			var fields = {};
			_.each(args, function(value, key, l, opt) {
				if(opt.index === 0) {
					return;
				};
				if(value.fields !== undefined) {
					if(value.fields._id !== undefined) {
						delete args[key].fields._id;
					};
					fields = args[key].fields;
				};
			});
			// force
			var force = false;
			_.each(args, function(arg){
				if(_.isObject(arg) && !_.isArray(arg)){
					force = arg.force === true ? true : false;
				};
			});
			// find
			this.getCollection(function(collection) {
				collection.find.apply(collection, args).toArray(function(err, data) {
					if(err) {
						return callback.call(self, err, []);
					} else {
						// check force = true (pass all function field)
						if(force === true){
							return callback.call(self, null, data);
						};
						/*
						* init function field (store === false)
						*/
						// init loop injection
						if(self.___loop_injection === true) {
							return callback.call(self, null, data);
						};
						self.___loop_injection = true;

						// callback & reset loop_injection
						var success = function() {
							try {
								self.___loop_injection = false;
								delete self.___loop_injection;
							} catch(ex) {
							};
							callback.call(self, null, data);
						};

						// get ids
						var ids = [];
						var keyData = {};
						_.each(data, function(value) {
							ids.push(value._id);
							keyData[value._id] = value;
						});

						// get hide & show fields
						var isQueryHide = null;
						var show_fields = [];
						var hidden_fields = [];
						_.each(fields, function(value, key) {
							if(value === 0) {
								hidden_fields.push(key);
								isQueryHide = true;
							} else if(value === 1) {
								show_fields.push(key);
								isQueryHide = false;
							}
							;
						});

						// filter function fields in column with hidden fields
						var funcs = {};
						_.each(self._column, function(column, name) {
							switch(column.__type) {
							case 'func':
								funcs[name] = column;
								break;
							case 'object':
								funcs[name] = column.getFuncField();
								break;
							};
						});
						funcs = _.encodeObject(funcs, '__type');
						_.each(funcs, function(value, key) {
							// just keep function with store === false
							if(value.get('store') !== false) {
								delete funcs[key];
								return;
							};
							if(isQueryHide === true) {
								// remove function field in hidden_fields
								_.each(hidden_fields, function(field) {
									if(key.indexOf(field) === 0) {
										delete funcs[key];
									}
								});
							} else if(isQueryHide === false) {
								// remove function field not in show_fields
								var isIn = false;
								_.each(show_fields, function(field) {
									if(key.indexOf(field) === 0) {
										isIn = true;
									};
								});
								if(!isIn) {
									delete funcs[key];
								};
							}
							;
						});

						// sort funcs by sequence
						_.sortObject(funcs, function(a, b) {
							return a.get('sequence') - b.get('sequence');
						});

						// rebuild function fields sequence
						var sequence = [];
						_.each(funcs, function(func, key, l, opt) {
							sequence.push((function(func, key, opt) {
								return function(fcb) {
									func.get('get').call(self, ids, keyData, function(funcData) {
										_.each(funcData, function(fData, _id) {
											var tmp = {};
											if(func.get('multi') === true) {
												tmp[key] = fData[key];
											} else {
												tmp[key] = fData;
											};
											_.setObject(keyData[_id], tmp);
										});
										data = _.values(keyData);
										// callback & reset loop_injection
										if(opt.isLast === true) {
											success();
										};
										fcb();
									});
								};
							})(func, key, opt));
						});

						if(sequence.length === 0) {
							success();
						} else {
							(function(sequence) {
								var args = arguments;
								if(sequence.length === 0) {
									return;
								};
								var func = sequence.shift();
								func(function() {
									args.callee(sequence);
								});
							})(sequence);
						};
					};
				});
			});
		},

		update: function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result, extra) {
				if(err) {
					callback.call(self, err, 0, extra);
				} else {
					callback.call(self, null, result, extra);
				};
			});
			// init full args (4 items)
			if(args.length === 3) {
				args[3] = args[2];
				args[2] = {};
			};
			// check force = true (pass all volidator)
			if(args[2].force === true){
				// start update
				this.getCollection(function(collection) {
					collection.update.apply(collection, args);
				});
				return;
			};
			// normal update
			var func_update = function() {
				// validate args
				try {
					// remove $setOnInsert
					delete args[1].$setOnInsert;
					// start validate
					this.update_validate(args[0], args[1], args[2]);
				} catch(ex) {
					log.log({
						func: 'update',
						args: JSON.stringify(arguments),
						ex: ex
					});
					callback.call(self, ex, 0);
					return;
				};
				// start update
				this.getCollection(function(collection) {
					collection.update.apply(collection, args);
				});
			};

			// check upsert
			var upsert = args[2].upsert;
			delete args[2].upsert;
			if(upsert === true) {
				this.read(args[0], {
					_id: 1
				}, function(err, data) {
					if(data.length > 0) {
						func_update.call(self);
					} else {
						// create using $setOnInsert
						if(args[1].$setOnInsert !== undefined) {
							self.create(args[1].$setOnInsert, function(e, r) {
								if(e) {
									args[3](e, 0);
								} else {
									args[3](null, 1, r);
								};
							});
						} else {
							args[3]({
								msg: 'can not find $setOnInsert'
							}, 0);
						};
					};
				});
			} else {
				func_update.call(self);
			};
			return;
		},

		'delete': function() {
			var log = this;
			var self = this;
			var args = _.initCallback(arguments);
			// get out callback
			var callback = args.pop();
			args.push(function(err, result) {
				callback.call(self, err, result);
			});
			// delete
			this.getCollection(function(collection) {
				collection.remove.apply(collection, args);
			});
		}
	};

	instance.base.model = instance.base.base.extend(__class);

	// config model static type
	instance.base.model.__type = 'model';
	instance.base.model.__extend = instance.base.model.extend;
	instance.base.model.extend = function() {
		var res = instance.base.model.__extend.apply(instance.base.model, arguments);
		res.__type = instance.base.model.__type;
		return res;
	};
};
<|endoftext|>


<commit_before>module.exports = {
  apps : [
    {
      name      : 'Move TV',
      script    : 'app.js',
      args      : ['-p', '-s', '/mnt/library/Downloads/complete/TV', '-d', '/mnt/library/TV']
    },

    {
      name      : 'Move Movies',
      script    : 'app.js',
      args      : ['-s', '/mnt/library/Downloads/complete/Movies', '-d', '/mnt/library/Movies']
    }
  ],

  deploy : {
    production : {
      user : 'pi',
      host : '10.1.1.15',
      ref  : 'origin/deploy',
      repo : 'git@github.com:ducas/movie-maid.git',
      path : '/home/pi/movie-maid-deploy',
      'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production'
    }
  }
};
<commit_msg>change ref to master<commit_after>module.exports = {
  apps : [
    {
      name      : 'Move TV',
      script    : 'app.js',
      args      : ['-p', '-s', '/mnt/library/Downloads/complete/TV', '-d', '/mnt/library/TV']
    },

    {
      name      : 'Move Movies',
      script    : 'app.js',
      args      : ['-s', '/mnt/library/Downloads/complete/Movies', '-d', '/mnt/library/Movies']
    }
  ],

  deploy : {
    production : {
      user : 'pi',
      host : '10.1.1.15',
      ref  : 'origin/master',
      repo : 'git@github.com:ducas/movie-maid.git',
      path : '/home/pi/movie-maid-deploy',
      'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production'
    }
  }
};
<|endoftext|>


<commit_before>import _ from 'lodash'
import { createSelector } from 'reselect';
import { jsonWriter } from 'io'
// import { getTrackFromCache } from './trackCacheMiddleware'

const createSelectorFromProps = (reducer, props) =>
  createSelector(
    props.map(prop =>
      state => state[reducer][prop]
    ),
    (...args) => {
      let out = {}
      props.forEach((prop, i) =>
        out[prop] = args[i]
      )
      return out
    }
  )

const inPlaybackModeSelector = ({playback: {state}}) => state !== 'stop' && state !== 'pause'

// track gets mutated but startPosition and lineStore are immutable
// const trackSelector = createSelectorFromProps('trackData', ['track', 'startPosition', 'lineStore'])
// const trackSelector = createSelector(
//   [
//     state => state.trackData.startPosition,
//     state => state.trackData.lineStore
//   ],
//   // the track from cache is derived purely from startPosition and lineStore
//   (startPosition, lineStore) => ({
//     track: getTrackFromCache(null, lineStore, startPosition),
//     startPosition,
//     lineStore
//   })
// )
const viewportSelector = createSelectorFromProps('viewport', ['w', 'h', 'x', 'y', 'z'])
const widthHeightSelector = createSelectorFromProps('viewport', ['w', 'h'])
const fileLoaderSelector = createSelectorFromProps('fileLoader', ['open', 'loadingFile', 'error', 'fileName', 'tracks'])
const toolbarSelector = createSelectorFromProps('toolbars', ['toolbarsOpen', 'timeControlOpen', 'sidebarOpen', 'sidebarSelected', 'colorSelected'])
const openToolbarSelector = createSelectorFromProps('toolbars', ['toolbarsOpen', 'timeControlOpen', 'sidebarOpen'])

// TODO: instead of making the radius smaller, change the viewport itself
const TOOLBAR_HEIGHT = 56
const PAPERBAR_HEIGHT = 48
const OFFSET = 15
export const playbackCamSelector = createSelector(
  [
    openToolbarSelector,
    viewportSelector,
    state => state.playback.index,
    // trackSelector
  ],
  ({toolbarsOpen, timeControlOpen, sidebarOpen}, {w, h, z}, index, /*{track}*/) => {
    h = h - 2 * (toolbarsOpen ? TOOLBAR_HEIGHT * (timeControlOpen ? 2 : 1) : PAPERBAR_HEIGHT)
    let maxRadius = Math.max(z * (Math.min(w, h) / 2) - OFFSET, 0)
    let {x, y} = track.getRiderCam(index, maxRadius)
    return {x, y, z}
  }
)

const camSelector = createSelector(
  [
    viewportSelector,
    state => null //inPlaybackModeSelector(state) ? playbackCamSelector(state) : null
  ],
  ({x, y, z}, playbackCam) => {
    if (playbackCam) {
      return playbackCam
    }
    return {x, y, z}
  }
)

// const lineSelector = createSelector(
//   [
//     state => state.viewport.w,
//     state => state.viewport.h,
//     camSelector,
//     trackSelector
//   ],
//   (w, h, {x, y, z}, {track}) => {
//     let [x1, y1, width, height] = [
//       x - w / 2 * z,
//       y - h / 2 * z,
//       w * z,
//       h * z
//     ]
//     return track.getLinesInBox(x1, y1, x1 + width, y1 + height)
//   }
// )

const indexSelector = createSelector(
  [
    state => state.playback.index,
    state => state.playback.flag,
  ],
  (index, flagIndex) => ({
    index,
    flagIndex,
    startIndex: Math.min(index, flagIndex),
    endIndex: Math.max(index, flagIndex)
  })
)
// const riderSelector = createSelector(
//   [
//     indexSelector,
//     trackSelector
//   ],
//   ({index, flagIndex, startIndex, endIndex}, {track}) => ({
//     startPosition: track.getStartPosition(),
//     rider: track.getRiderStateAtFrame(index),
//     flagRider: track.getRiderStateAtFrame(flagIndex),
//     states: _.range(startIndex, endIndex + 1)
//       .map(i => track.getRiderStateAtFrame(i))
//   })
// )

const colorPickerOpenSelector = state => {
  switch (state.selectedTool) {
    case 'pencil':
    case 'line':
    case 'curve':
    case 'brush':
    case 'multiLine':
      return true
    default:
      return false
  }
}

const selectedSelector = createSelector(
  [
    state => state.toggled,
    state => state.selectedTool,
    state => state.playback.state
  ],
  (toggled, selectedTool, playbackState) => ({
    ...toggled,
    [selectedTool]: true,
    [playbackState]: playbackState !== 'stop'
  })
)

const editorSelector = createSelector(
  [
    toolbarSelector,
    inPlaybackModeSelector,
    selectedSelector,
    colorPickerOpenSelector
  ],
  (toolbars, inPlaybackMode, selected, colorPickerOpen) => ({
    ...toolbars,
    inPlaybackMode,
    selected,
    colorPickerOpen: colorPickerOpen && !inPlaybackMode
  })
)

const timelineSelector = createSelector(
  [
    state => state.playback.index,
    state => state.playback.flag,
    state => state.playback.maxIndex
  ],
  (index, flagIndex, maxIndex) => ({index, flagIndex, maxIndex})
)
// TODO: make sidebar selector
// TODO: move rendering button logic to here
const viewOptionsSelector = createSelector(
  [
    inPlaybackModeSelector
  ],
  (inPlaybackMode) => ({
    color: !inPlaybackMode,
    floor: true,
    accArrow: true
  })
)

// const lineSelectionSelector = createSelector(
//   [
//     trackSelector,
//     state => state.lineSelection.lineID,
//     state => state.selectedTool
//   ],
//   ({track}, lineID, tool) => {
//     if (tool !== 'select') {
//       return []
//     }
//     if (lineID != null) {
//       let line = track.getLineByID(lineID)
//       if (line != null) {
//         return [line]
//       }
//     }
//     return []
//   }
// )

const displaySelector = createSelector(
  [
    timelineSelector,
    indexSelector,
    // riderSelector,
    camSelector,
    // lineSelector,
    widthHeightSelector,
    viewOptionsSelector,
    state => state.toggled.onionSkin || false,
    // lineSelectionSelector,
  ],
  ({index, flagIndex, maxIndex}, {startIndex, endIndex}, /*{startPosition, states, rider, flagRider}, */cam, /*lines,*/ {w, h}, viewOptions, onionSkin/*, lineSelection*/) => ({
    frame: index,
    flagIndex,
    maxIndex,
    // startPosition,
    // rider,
    // flagRider,
    // riders: states,
    startIndex,
    endIndex,
    cam,
    // lines,
    width: w,
    height: h,
    viewOptions,
    onionSkin,
    // lineSelection
  })
)

// const trackSaverSelector = createSelector(
//   [
//     // trackSelector,
//     state => state.trackSaver.open,
//     state => state.trackData.startPosition,
//     state => state.trackData.label,
//     state => state.trackData.version
//   ],
//   (/*{track},*/ open, startPosition, label, version) => {
//     let trackData = open ? {
//       label,
//       version,
//       startPosition,
//       // lines: _.sortBy(track.getData(), 'id')
//     } : null
//     let trackSaver = {
//       open,
//       trackData,
//       label,
//       fileName: (label || 'untitled') + '.json'
//     }
//     if (trackData) {
//       let trackDataJSON = jsonWriter(trackData) // TODO: this is slow af
//       let blob = new Blob([trackDataJSON], {type: 'application/json'})
//       trackSaver.trackDataURI = URL.createObjectURL(blob)
//       trackSaver.trackDataJSON = (blob.size > 500000) ? // 500 kb
//         (label + ': too long to preview raw data :O') : trackDataJSON
//     }
//     return trackSaver
//   }
// )

const drawingSurfaceSelector = createSelector(
  [
    state => state.drawStreamActive
  ],
  (drawStreamActive) => ({drawStreamActive})
)

export default createSelector(
  [
    editorSelector,
    fileLoaderSelector,
    timelineSelector,
    displaySelector,
    widthHeightSelector,
    // trackSaverSelector,
    drawingSurfaceSelector
  ],
  (editor, fileLoader, timeline, display, widthHeight, /*trackSaver,*/ drawingSurface) => ({
    editor,
    fileLoader,
    timeline,
    display,
    widthHeight,
    // trackSaver,
    drawingSurface
  })
)
<commit_msg>make entitySelector<commit_after>import _ from 'lodash'
import { createSelector } from 'reselect';
import { jsonWriter } from 'io'
// import { getTrackFromCache } from './trackCacheMiddleware'

const createSelectorFromProps = (reducer, props) =>
  createSelector(
    props.map(prop =>
      state => state[reducer][prop]
    ),
    (...args) => {
      let out = {}
      props.forEach((prop, i) =>
        out[prop] = args[i]
      )
      return out
    }
  )

const inPlaybackModeSelector = ({playback: {state}}) => state !== 'stop' && state !== 'pause'

// track gets mutated but startPosition and lineStore are immutable
// const trackSelector = createSelectorFromProps('trackData', ['track', 'startPosition', 'lineStore'])
// const trackSelector = createSelector(
//   [
//     state => state.trackData.startPosition,
//     state => state.trackData.lineStore
//   ],
//   // the track from cache is derived purely from startPosition and lineStore
//   (startPosition, lineStore) => ({
//     track: getTrackFromCache(null, lineStore, startPosition),
//     startPosition,
//     lineStore
//   })
// )
const viewportSelector = createSelectorFromProps('viewport', ['w', 'h', 'x', 'y', 'z'])
const widthHeightSelector = createSelectorFromProps('viewport', ['w', 'h'])
const fileLoaderSelector = createSelectorFromProps('fileLoader', ['open', 'loadingFile', 'error', 'fileName', 'tracks'])
const toolbarSelector = createSelectorFromProps('toolbars', ['toolbarsOpen', 'timeControlOpen', 'sidebarOpen', 'sidebarSelected', 'colorSelected'])
const openToolbarSelector = createSelectorFromProps('toolbars', ['toolbarsOpen', 'timeControlOpen', 'sidebarOpen'])

// TODO: instead of making the radius smaller, change the viewport itself
const TOOLBAR_HEIGHT = 56
const PAPERBAR_HEIGHT = 48
const OFFSET = 15
export const playbackCamSelector = createSelector(
  [
    openToolbarSelector,
    viewportSelector,
    state => state.playback.index,
    // trackSelector
  ],
  ({toolbarsOpen, timeControlOpen, sidebarOpen}, {w, h, z}, index, /*{track}*/) => {
    h = h - 2 * (toolbarsOpen ? TOOLBAR_HEIGHT * (timeControlOpen ? 2 : 1) : PAPERBAR_HEIGHT)
    let maxRadius = Math.max(z * (Math.min(w, h) / 2) - OFFSET, 0)
    let {x, y} = track.getRiderCam(index, maxRadius)
    return {x, y, z}
  }
)

const camSelector = createSelector(
  [
    viewportSelector,
    state => null //inPlaybackModeSelector(state) ? playbackCamSelector(state) : null
  ],
  ({x, y, z}, playbackCam) => {
    if (playbackCam) {
      return playbackCam
    }
    return {x, y, z}
  }
)

// const lineSelector = createSelector(
//   [
//     state => state.viewport.w,
//     state => state.viewport.h,
//     camSelector,
//     trackSelector
//   ],
//   (w, h, {x, y, z}, {track}) => {
//     let [x1, y1, width, height] = [
//       x - w / 2 * z,
//       y - h / 2 * z,
//       w * z,
//       h * z
//     ]
//     return track.getLinesInBox(x1, y1, x1 + width, y1 + height)
//   }
// )

const indexSelector = createSelector(
  [
    state => state.playback.index,
    state => state.playback.flag,
  ],
  (index, flagIndex) => ({
    index,
    flagIndex,
    startIndex: Math.min(index, flagIndex),
    endIndex: Math.max(index, flagIndex)
  })
)
// const riderSelector = createSelector(
//   [
//     indexSelector,
//     trackSelector
//   ],
//   ({index, flagIndex, startIndex, endIndex}, {track}) => ({
//     startPosition: track.getStartPosition(),
//     rider: track.getRiderStateAtFrame(index),
//     flagRider: track.getRiderStateAtFrame(flagIndex),
//     states: _.range(startIndex, endIndex + 1)
//       .map(i => track.getRiderStateAtFrame(i))
//   })
// )

const colorPickerOpenSelector = state => {
  switch (state.selectedTool) {
    case 'pencil':
    case 'line':
    case 'curve':
    case 'brush':
    case 'multiLine':
      return true
    default:
      return false
  }
}

const selectedSelector = createSelector(
  [
    state => state.toggled,
    state => state.selectedTool,
    state => state.playback.state
  ],
  (toggled, selectedTool, playbackState) => ({
    ...toggled,
    [selectedTool]: true,
    [playbackState]: playbackState !== 'stop'
  })
)

const editorSelector = createSelector(
  [
    toolbarSelector,
    inPlaybackModeSelector,
    selectedSelector,
    colorPickerOpenSelector
  ],
  (toolbars, inPlaybackMode, selected, colorPickerOpen) => ({
    ...toolbars,
    inPlaybackMode,
    selected,
    colorPickerOpen: colorPickerOpen && !inPlaybackMode
  })
)

const timelineSelector = createSelector(
  [
    state => state.playback.index,
    state => state.playback.flag,
    state => state.playback.maxIndex
  ],
  (index, flagIndex, maxIndex) => ({index, flagIndex, maxIndex})
)
// TODO: make sidebar selector
// TODO: move rendering button logic to here
const viewOptionsSelector = createSelector(
  [
    inPlaybackModeSelector
  ],
  (inPlaybackMode) => ({
    color: !inPlaybackMode,
    floor: true,
    accArrow: true
  })
)

// const lineSelectionSelector = createSelector(
//   [
//     trackSelector,
//     state => state.lineSelection.lineID,
//     state => state.selectedTool
//   ],
//   ({track}, lineID, tool) => {
//     if (tool !== 'select') {
//       return []
//     }
//     if (lineID != null) {
//       let line = track.getLineByID(lineID)
//       if (line != null) {
//         return [line]
//       }
//     }
//     return []
//   }
// )

const entitySelector = createSelector(
  [
    indexSelector,
    state => state.simStates
  ],
  ({index}, simStates) => ({
    balls: simStates[index].balls,
    wires: simStates[index].wires
  })
)

const displaySelector = createSelector(
  [
    timelineSelector,
    indexSelector,
    // riderSelector,
    camSelector,
    // lineSelector,
    widthHeightSelector,
    viewOptionsSelector,
    state => state.toggled.onionSkin || false,
    // lineSelectionSelector,
    entitySelector
  ],
  ({index, flagIndex, maxIndex}, {startIndex, endIndex}, /*{startPosition, states, rider, flagRider}, */cam, /*lines,*/ {w, h}, viewOptions, onionSkin, entities) => ({
    frame: index,
    flagIndex,
    maxIndex,
    // startPosition,
    // rider,
    // flagRider,
    // riders: states,
    startIndex,
    endIndex,
    cam,
    // lines,
    width: w,
    height: h,
    viewOptions,
    onionSkin,
    entities
    // lineSelection
  })
)

// const trackSaverSelector = createSelector(
//   [
//     // trackSelector,
//     state => state.trackSaver.open,
//     state => state.trackData.startPosition,
//     state => state.trackData.label,
//     state => state.trackData.version
//   ],
//   (/*{track},*/ open, startPosition, label, version) => {
//     let trackData = open ? {
//       label,
//       version,
//       startPosition,
//       // lines: _.sortBy(track.getData(), 'id')
//     } : null
//     let trackSaver = {
//       open,
//       trackData,
//       label,
//       fileName: (label || 'untitled') + '.json'
//     }
//     if (trackData) {
//       let trackDataJSON = jsonWriter(trackData) // TODO: this is slow af
//       let blob = new Blob([trackDataJSON], {type: 'application/json'})
//       trackSaver.trackDataURI = URL.createObjectURL(blob)
//       trackSaver.trackDataJSON = (blob.size > 500000) ? // 500 kb
//         (label + ': too long to preview raw data :O') : trackDataJSON
//     }
//     return trackSaver
//   }
// )

const drawingSurfaceSelector = createSelector(
  [
    state => state.drawStreamActive
  ],
  (drawStreamActive) => ({drawStreamActive})
)

export default createSelector(
  [
    editorSelector,
    fileLoaderSelector,
    timelineSelector,
    displaySelector,
    widthHeightSelector,
    // trackSaverSelector,
    drawingSurfaceSelector
  ],
  (editor, fileLoader, timeline, display, widthHeight, /*trackSaver,*/ drawingSurface) => ({
    editor,
    fileLoader,
    timeline,
    display,
    widthHeight,
    // trackSaver,
    drawingSurface
  })
)
<|endoftext|>


<commit_before><commit_msg>Removed debug log<commit_after><|endoftext|>


<commit_before>import {PropTypes} from 'react'
import {StyleSheet, css} from 'aphrodite'
import {stringify as queryify} from 'query-string'

export default Chat

function Chat({youTubeId}) {
  const {styles} = Chat
  const params = {
    v: youTubeId,
    embed_domain: process.env.CI ? 'javascriptair.com' : 'localhost', // eslint-disable-line camelcase
  }
  const base = 'https://www.youtube.com/live_chat'
  const src = `${base}?${queryify(params)}`
  return (

  )
}

Chat.propTypes = {
  youTubeId: PropTypes.string.isRequired,
}

Chat.styles = StyleSheet.create({
  chatFrame: {
    maxWidth: '100%',
    marginTop: 10,
  },
})
<commit_msg>add code of conduct link below the chat<commit_after>import {PropTypes} from 'react'
import {StyleSheet, css} from 'aphrodite'
import {stringify as queryify} from 'query-string'

export default Chat

function Chat({youTubeId}) {
  const {styles} = Chat
  const params = {
    v: youTubeId,
    embed_domain: process.env.CI ? 'javascriptair.com' : 'localhost', // eslint-disable-line camelcase
  }
  const base = 'https://www.youtube.com/live_chat'
  const src = `${base}?${queryify(params)}`
  return (

        Please adhear to our Code of Conduct.

  )
}

Chat.propTypes = {
  youTubeId: PropTypes.string.isRequired,
}

Chat.styles = StyleSheet.create({
  chatFrame: {
    maxWidth: '100%',
    marginTop: 10,
  },
  coc: {
    marginTop: 30,
  },
})
<|endoftext|>


<commit_before>'use strict';

const REDIS_DB = 8;

const RATE_LIMIT_ENDPOINTS_GROUPS = {
    ENDPOINT_1: 'anonymous',
    ENDPOINT_2: 'static',
    ENDPOINT_3: 'staticNamed',
    ENDPOINT_5: 'dataview',
    ENDPOINT_6: 'dataviewSearch',
    ENDPOINT_7: 'analysis',
    ENDPOINT_8: 'tile',
    ENDPOINT_9: 'attributes',
    ENDPOINT_10: 'namedList',
    ENDPOINT_11: 'namedCreate',
    ENDPOINT_12: 'namedGet',
    ENDPOINT_13: 'named',
    ENDPOINT_14: 'namedUpdate',
    ENDPOINT_15: 'namedDelete',
    ENDPOINT_17: 'namedTiles'
};

function rateLimitMiddleware(metadataBackend, endpointGroup = null) {
    return function rateLimit(req, res, next) {
        if (!global.environment.enabledFeatures.rateLimitsEnabled) {
            return next();
        }

        const user = 'cdb';
        // const user = res.locals.user;
        if (!endpointGroup) {
            endpointGroup = getEndpointGroup();
        }

        if (!endpointGroup || !isRateLimitEnabledByEndpoint(endpointGroup)) {
            return next();
        }

        const redisParams = [
            getLuaScript(),
            2,
            getStoreKey(user, endpointGroup),   // KEY[1]
            getStatusKey(user, endpointGroup)   // KEY[2]
        ];

        metadataBackend.redisCmd(REDIS_DB, 'EVAL', redisParams, function(err, data) {
            if (err) {
                return next(err);
            }

            if (!data || !Array.isArray(data)) {
                return next();
            }

            const isBloqued = data[0];
            const limit = data[1];
            const remaining = data[2];
            const retry = data[3];
            const reset = data[4];

            res.set({
                'X-Rate-Limit-Limit': limit,
                'X-Rate-Limit-Remaining': remaining,
                'X-Rate-Limit-Retry-After': retry,
                'X-Rate-Limit-Reset': reset
            });

            if (isBloqued) {
                const err = new Error('You are over the limits.');
                err.http_status = 429;
                return next(err);
            }

            return next();
        });
    };
}

/**
 * Returns the endpoint key in Redis
 */
function getEndpointGroup() {
    // TODO: get endpoint from route path
    return null;
}

/**
 * Returns Redis key where the limits are saved by user and endpoint
 * The value is a Redis hash:
 *    maxBurst (b): Integer (as string)
 *    countPerPeriod (c): Integer (as string)
 *    period (p): Integer (as string)
 * @param {string} user
 * @param {string} endpointGroup
 */
function getStoreKey(user, endpointGroup) {
    return `rate-limit:store:${user}:${endpointGroup}`;
}

/**
 * Returns Redis key where the current state of the limit by user and endpoint
 * This key is managed by redis-cell (CL.THROTTLE command)
 * @param {string} user
 * @param {string} endpointGroup
 */
function getStatusKey(user, endpointGroup) {
    return `rate-limit:status:${user}:${endpointGroup}`;
}

function getLuaScript() {
    return `
        local limmits = {}
        local limmitsArray = redis.call("HGETALL", KEYS[1])
        if table.getn(limmitsArray) == 6 then
            limmits[limmitsArray[1]] = limmitsArray[2]
            limmits[limmitsArray[3]] = limmitsArray[4]
            limmits[limmitsArray[5]] = limmitsArray[6]

            return redis.call("CL.THROTTLE", KEYS[2], limmits['b'], limmits['c'], limmits['p'])
        else
            return nil
        end
    `;
}

function isRateLimitEnabledByEndpoint(endpointGroup) {
    return global.environment.enabledFeatures.rateLimitsByEndpoint[endpointGroup] === true;
}

module.exports.rateLimitMiddleware = rateLimitMiddleware;
module.exports.RATE_LIMIT_ENDPOINTS_GROUPS = RATE_LIMIT_ENDPOINTS_GROUPS;
module.exports.getStoreKey = getStoreKey;
<commit_msg>updating redis keys<commit_after>'use strict';

const REDIS_DB = 8;

const RATE_LIMIT_ENDPOINTS_GROUPS = {
    ENDPOINT_1: 'anonymous',
    ENDPOINT_2: 'static',
    ENDPOINT_3: 'staticNamed',
    ENDPOINT_5: 'dataview',
    ENDPOINT_6: 'dataviewSearch',
    ENDPOINT_7: 'analysis',
    ENDPOINT_8: 'tile',
    ENDPOINT_9: 'attributes',
    ENDPOINT_10: 'namedList',
    ENDPOINT_11: 'namedCreate',
    ENDPOINT_12: 'namedGet',
    ENDPOINT_13: 'named',
    ENDPOINT_14: 'namedUpdate',
    ENDPOINT_15: 'namedDelete',
    ENDPOINT_17: 'namedTiles'
};

function rateLimitMiddleware(metadataBackend, endpointGroup = null) {
    return function rateLimit(req, res, next) {
        if (!global.environment.enabledFeatures.rateLimitsEnabled) {
            return next();
        }

        const user = 'cdb';
        // const user = res.locals.user;
        if (!endpointGroup) {
            endpointGroup = getEndpointGroup();
        }

        if (!endpointGroup || !isRateLimitEnabledByEndpoint(endpointGroup)) {
            return next();
        }

        const redisParams = [
            getLuaScript(),
            2,
            getStoreKey(user, endpointGroup),   // KEY[1]
            getStatusKey(user, endpointGroup)   // KEY[2]
        ];

        metadataBackend.redisCmd(REDIS_DB, 'EVAL', redisParams, function(err, data) {
            if (err) {
                return next(err);
            }

            if (!data || !Array.isArray(data)) {
                return next();
            }

            const isBloqued = data[0];
            const limit = data[1];
            const remaining = data[2];
            const retry = data[3];
            const reset = data[4];

            res.set({
                'X-Rate-Limit-Limit': limit,
                'X-Rate-Limit-Remaining': remaining,
                'X-Rate-Limit-Retry-After': retry,
                'X-Rate-Limit-Reset': reset
            });

            if (isBloqued) {
                const err = new Error('You are over the limits.');
                err.http_status = 429;
                return next(err);
            }

            return next();
        });
    };
}

/**
 * Returns the endpoint key in Redis
 */
function getEndpointGroup() {
    // TODO: get endpoint from route path
    return null;
}

/**
 * Returns Redis key where the limits are saved by user and endpoint
 * The value is a Redis hash:
 *    maxBurst (b): Integer (as string)
 *    countPerPeriod (c): Integer (as string)
 *    period (p): Integer (as string)
 * @param {string} user
 * @param {string} endpointGroup
 */
function getStoreKey(user, endpointGroup) {
    return `limits:rate:store:${user}:${endpointGroup}`;
}

/**
 * Returns Redis key where the current state of the limit by user and endpoint
 * This key is managed by redis-cell (CL.THROTTLE command)
 * @param {string} user
 * @param {string} endpointGroup
 */
function getStatusKey(user, endpointGroup) {
    return `limits:rate:status:${user}:${endpointGroup}`;
}

function getLuaScript() {
    return `
        local limmits = {}
        local limmitsArray = redis.call("HGETALL", KEYS[1])
        if table.getn(limmitsArray) == 6 then
            limmits[limmitsArray[1]] = limmitsArray[2]
            limmits[limmitsArray[3]] = limmitsArray[4]
            limmits[limmitsArray[5]] = limmitsArray[6]

            return redis.call("CL.THROTTLE", KEYS[2], limmits['b'], limmits['c'], limmits['p'])
        else
            return nil
        end
    `;
}

function isRateLimitEnabledByEndpoint(endpointGroup) {
    return global.environment.enabledFeatures.rateLimitsByEndpoint[endpointGroup] === true;
}

module.exports.rateLimitMiddleware = rateLimitMiddleware;
module.exports.RATE_LIMIT_ENDPOINTS_GROUPS = RATE_LIMIT_ENDPOINTS_GROUPS;
module.exports.getStoreKey = getStoreKey;
<|endoftext|>