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

/**
 * Class Property
 * uses Prototpye 1.5.0
 * uses Inheritance
 */
ORYX.Core.StencilSet.Property = Clazz.extend({

    /**
     * Constructor
     */
    construct: function(jsonProp, namespace, stencil){
        arguments.callee.$.construct.apply(this, arguments);

        this._jsonProp = jsonProp || ORYX.Log.error("Parameter jsonProp is not defined.");
        this._namespace = namespace || ORYX.Log.error("Parameter namespace is not defined.");
        this._stencil = stencil || ORYX.Log.error("Parameter stencil is not defined.");

        this._items = new Hash();
        this._complexItems = new Hash();

        jsonProp.id = jsonProp.id || ORYX.Log.error("ORYX.Core.StencilSet.Property(construct): Id is not defined.");
		jsonProp.id = jsonProp.id.toLowerCase();

        if (!jsonProp.type) {
            ORYX.Log.info("Type is not defined for stencil '%0', id '%1'. Falling back to 'String'.", stencil, jsonProp.id);
            jsonProp.type = "string";
        }
        else {
            jsonProp.type = jsonProp.type.toLowerCase();
        }

        jsonProp.prefix = jsonProp.prefix || "oryx";
        jsonProp.title = jsonProp.title || "";
        jsonProp.value = jsonProp.value || "";
        jsonProp.description = jsonProp.description || "";
        jsonProp.readonly = jsonProp.readonly || false;
        jsonProp.optional = jsonProp.optional || true;

        //init refToView
        if (this._jsonProp.refToView) {
            if (!(this._jsonProp.refToView instanceof Array)) {
                this._jsonProp.refToView = [this._jsonProp.refToView];
            }
        }
        else {
            this._jsonProp.refToView = [];
        }

        if (jsonProp.min === undefined || jsonProp.min === null) {
            jsonProp.min = Number.MIN_VALUE;
        }

        if (jsonProp.max === undefined || jsonProp.max === null) {
            jsonProp.max = Number.MAX_VALUE;
        }

        if (!jsonProp.fillOpacity) {
            jsonProp.fillOpacity = false;
        }

        if (!jsonProp.strokeOpacity) {
            jsonProp.strokeOpacity = false;
        }

        if (jsonProp.length === undefined || jsonProp.length === null) {
            jsonProp.length = Number.MAX_VALUE;
        }

        if (!jsonProp.wrapLines) {
            jsonProp.wrapLines = false;
        }

        if (!jsonProp.dateFormat) {
            jsonProp.dataFormat = "m/d/y";
        }

        if (!jsonProp.fill) {
            jsonProp.fill = false;
        }

        if (!jsonProp.stroke) {
            jsonProp.stroke = false;
        }

        if (jsonProp.type === ORYX.CONFIG.TYPE_CHOICE) {
            if (jsonProp.items && jsonProp.items instanceof Array) {
                jsonProp.items.each((function(jsonItem){
                    this._items[jsonItem.value] = new ORYX.Core.StencilSet.PropertyItem(jsonItem, namespace, this);
                }).bind(this));
            }
            else {
                throw "ORYX.Core.StencilSet.Property(construct): No property items defined."
            }
            // extended by Kerstin (start)
        }
        else
            if (jsonProp.type === ORYX.CONFIG.TYPE_COMPLEX) {
                if (jsonProp.complexItems && jsonProp.complexItems instanceof Array) {
                    jsonProp.complexItems.each((function(jsonComplexItem){
                        this._complexItems[jsonComplexItem.id] = new ORYX.Core.StencilSet.ComplexPropertyItem(jsonComplexItem, namespace, this);
                    }).bind(this));
                }
                else {
                    throw "ORYX.Core.StencilSet.Property(construct): No complex property items defined."
                }
            }
        // extended by Kerstin (end)
    },

    /**
     * @param {ORYX.Core.StencilSet.Property} property
     * @return {Boolean} True, if property has the same namespace and id.
     */
    equals: function(property){
        return (this._namespace === property.namespace() &&
        this.id() === property.id()) ? true : false;
    },

    namespace: function(){
        return this._namespace;
    },

    stencil: function(){
        return this._stencil;
    },

    id: function(){
        return this._jsonProp.id;
    },

    prefix: function(){
        return this._jsonProp.prefix;
    },

    type: function(){
        return this._jsonProp.type;
    },

    title: function(){
        return ORYX.Core.StencilSet.getTranslation(this._jsonProp, "title");
    },

    value: function(){
        return this._jsonProp.value;
    },

    readonly: function(){
        return this._jsonProp.readonly;
    },

    optional: function(){
        return this._jsonProp.optional;
    },

    description: function(){
        return ORYX.Core.StencilSet.getTranslation(this._jsonProp, "description");
    },

    /**
     * An optional link to a SVG element so that the property affects the
     * graphical representation of the stencil.
     */
    refToView: function(){
        return this._jsonProp.refToView;
    },

    /**
     * If type is integer or float, min is the lower bounds of value.
     */
    min: function(){
        return this._jsonProp.min;
    },

    /**
     * If type ist integer or float, max is the upper bounds of value.
     */
    max: function(){
        return this._jsonProp.max;
    },

    /**
     * If type is float, this method returns if the fill-opacity property should
     *  be set.
     *  @return {Boolean}
     */
    fillOpacity: function(){
        return this._jsonProp.fillOpacity;
    },

    /**
     * If type is float, this method returns if the stroke-opacity property should
     *  be set.
     *  @return {Boolean}
     */
    strokeOpacity: function(){
        return this._jsonProp.strokeOpacity;
    },

    /**
     * If type is string or richtext, length is the maximum length of the text.
     * TODO how long can a string be.
     */
    length: function(){
        return this._jsonProp.length ? this._jsonProp.length : Number.MAX_VALUE;
    },

    wrapLines: function(){
        return this._jsonProp.wrapLines;
    },

    /**
     * If type is date, dateFormat specifies the format of the date. The format
     * specification of the ext library is used:
     *
     * Format  Output      Description
     *	------  ----------  --------------------------------------------------------------
     *	  d      10         Day of the month, 2 digits with leading zeros
     *	  D      Wed        A textual representation of a day, three letters
     *	  j      10         Day of the month without leading zeros
     *	  l      Wednesday  A full textual representation of the day of the week
     *	  S      th         English ordinal day of month suffix, 2 chars (use with j)
     *	  w      3          Numeric representation of the day of the week
     *	  z      9          The julian date, or day of the year (0-365)
     *	  W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
     *	  F      January    A full textual representation of the month
     *	  m      01         Numeric representation of a month, with leading zeros
     *	  M      Jan        Month name abbreviation, three letters
     *	  n      1          Numeric representation of a month, without leading zeros
     *	  t      31         Number of days in the given month
     *	  L      0          Whether its a leap year (1 if it is a leap year, else 0)
     *	  Y      2007       A full numeric representation of a year, 4 digits
     *	  y      07         A two digit representation of a year
     *	  a      pm         Lowercase Ante meridiem and Post meridiem
     *	  A      PM         Uppercase Ante meridiem and Post meridiem
     *	  g      3          12-hour format of an hour without leading zeros
     *	  G      15         24-hour format of an hour without leading zeros
     *	  h      03         12-hour format of an hour with leading zeros
     *	  H      15         24-hour format of an hour with leading zeros
     *	  i      05         Minutes with leading zeros
     *	  s      01         Seconds, with leading zeros
     *	  O      -0600      Difference to Greenwich time (GMT) in hours
     *	  T      CST        Timezone setting of the machine running the code
     *	  Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
     *
     * Example:
     *  F j, Y, g:i a  ->  January 10, 2007, 3:05 pm
     */
    dateFormat: function(){
        return this._jsonProp.dateFormat;
    },

    /**
     * If type is color, this method returns if the fill property should
     *  be set.
     *  @return {Boolean}
     */
    fill: function(){
        return this._jsonProp.fill;
    },

    /**
     * If type is color, this method returns if the stroke property should
     *  be set.
     *  @return {Boolean}
     */
    stroke: function(){
        return this._jsonProp.stroke;
    },

    /**
     * If type is choice, items is a hash map with all alternative values
     * (PropertyItem objects) with id as keys.
     */
    items: function(){
        return this._items.values();
    },

    item: function(value){
        return this._item[value];
    },

    toString: function(){
        return "Property " + this.title() + " (" + this.id() + ")";
    },

    // extended by Kerstin (start)
    complexItems: function(){
        return this._complexItems.values();
    },

    complexItem: function(value){
        return this._complexItems[value];
    }
    // extended by Kerstin (end)
});
<commit_msg>fixed property.item method.<commit_after>
/**
 * Copyright (c) 2006
 * Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 **/
/**
 * Init namespace
 */
if (!ORYX) {
    var ORYX = {};
}
if (!ORYX.Core) {
    ORYX.Core = {};
}
if (!ORYX.Core.StencilSet) {
    ORYX.Core.StencilSet = {};
}

/**
 * Class Property
 * uses Prototpye 1.5.0
 * uses Inheritance
 */
ORYX.Core.StencilSet.Property = Clazz.extend({

    /**
     * Constructor
     */
    construct: function(jsonProp, namespace, stencil){
        arguments.callee.$.construct.apply(this, arguments);

        this._jsonProp = jsonProp || ORYX.Log.error("Parameter jsonProp is not defined.");
        this._namespace = namespace || ORYX.Log.error("Parameter namespace is not defined.");
        this._stencil = stencil || ORYX.Log.error("Parameter stencil is not defined.");

        this._items = new Hash();
        this._complexItems = new Hash();

        jsonProp.id = jsonProp.id || ORYX.Log.error("ORYX.Core.StencilSet.Property(construct): Id is not defined.");
		jsonProp.id = jsonProp.id.toLowerCase();

        if (!jsonProp.type) {
            ORYX.Log.info("Type is not defined for stencil '%0', id '%1'. Falling back to 'String'.", stencil, jsonProp.id);
            jsonProp.type = "string";
        }
        else {
            jsonProp.type = jsonProp.type.toLowerCase();
        }

        jsonProp.prefix = jsonProp.prefix || "oryx";
        jsonProp.title = jsonProp.title || "";
        jsonProp.value = jsonProp.value || "";
        jsonProp.description = jsonProp.description || "";
        jsonProp.readonly = jsonProp.readonly || false;
        jsonProp.optional = jsonProp.optional || true;

        //init refToView
        if (this._jsonProp.refToView) {
            if (!(this._jsonProp.refToView instanceof Array)) {
                this._jsonProp.refToView = [this._jsonProp.refToView];
            }
        }
        else {
            this._jsonProp.refToView = [];
        }

        if (jsonProp.min === undefined || jsonProp.min === null) {
            jsonProp.min = Number.MIN_VALUE;
        }

        if (jsonProp.max === undefined || jsonProp.max === null) {
            jsonProp.max = Number.MAX_VALUE;
        }

        if (!jsonProp.fillOpacity) {
            jsonProp.fillOpacity = false;
        }

        if (!jsonProp.strokeOpacity) {
            jsonProp.strokeOpacity = false;
        }

        if (jsonProp.length === undefined || jsonProp.length === null) {
            jsonProp.length = Number.MAX_VALUE;
        }

        if (!jsonProp.wrapLines) {
            jsonProp.wrapLines = false;
        }

        if (!jsonProp.dateFormat) {
            jsonProp.dataFormat = "m/d/y";
        }

        if (!jsonProp.fill) {
            jsonProp.fill = false;
        }

        if (!jsonProp.stroke) {
            jsonProp.stroke = false;
        }

        if (jsonProp.type === ORYX.CONFIG.TYPE_CHOICE) {
            if (jsonProp.items && jsonProp.items instanceof Array) {
                jsonProp.items.each((function(jsonItem){
                    this._items[jsonItem.value] = new ORYX.Core.StencilSet.PropertyItem(jsonItem, namespace, this);
                }).bind(this));
            }
            else {
                throw "ORYX.Core.StencilSet.Property(construct): No property items defined."
            }
            // extended by Kerstin (start)
        }
        else
            if (jsonProp.type === ORYX.CONFIG.TYPE_COMPLEX) {
                if (jsonProp.complexItems && jsonProp.complexItems instanceof Array) {
                    jsonProp.complexItems.each((function(jsonComplexItem){
                        this._complexItems[jsonComplexItem.id] = new ORYX.Core.StencilSet.ComplexPropertyItem(jsonComplexItem, namespace, this);
                    }).bind(this));
                }
                else {
                    throw "ORYX.Core.StencilSet.Property(construct): No complex property items defined."
                }
            }
        // extended by Kerstin (end)
    },

    /**
     * @param {ORYX.Core.StencilSet.Property} property
     * @return {Boolean} True, if property has the same namespace and id.
     */
    equals: function(property){
        return (this._namespace === property.namespace() &&
        this.id() === property.id()) ? true : false;
    },

    namespace: function(){
        return this._namespace;
    },

    stencil: function(){
        return this._stencil;
    },

    id: function(){
        return this._jsonProp.id;
    },

    prefix: function(){
        return this._jsonProp.prefix;
    },

    type: function(){
        return this._jsonProp.type;
    },

    title: function(){
        return ORYX.Core.StencilSet.getTranslation(this._jsonProp, "title");
    },

    value: function(){
        return this._jsonProp.value;
    },

    readonly: function(){
        return this._jsonProp.readonly;
    },

    optional: function(){
        return this._jsonProp.optional;
    },

    description: function(){
        return ORYX.Core.StencilSet.getTranslation(this._jsonProp, "description");
    },

    /**
     * An optional link to a SVG element so that the property affects the
     * graphical representation of the stencil.
     */
    refToView: function(){
        return this._jsonProp.refToView;
    },

    /**
     * If type is integer or float, min is the lower bounds of value.
     */
    min: function(){
        return this._jsonProp.min;
    },

    /**
     * If type ist integer or float, max is the upper bounds of value.
     */
    max: function(){
        return this._jsonProp.max;
    },

    /**
     * If type is float, this method returns if the fill-opacity property should
     *  be set.
     *  @return {Boolean}
     */
    fillOpacity: function(){
        return this._jsonProp.fillOpacity;
    },

    /**
     * If type is float, this method returns if the stroke-opacity property should
     *  be set.
     *  @return {Boolean}
     */
    strokeOpacity: function(){
        return this._jsonProp.strokeOpacity;
    },

    /**
     * If type is string or richtext, length is the maximum length of the text.
     * TODO how long can a string be.
     */
    length: function(){
        return this._jsonProp.length ? this._jsonProp.length : Number.MAX_VALUE;
    },

    wrapLines: function(){
        return this._jsonProp.wrapLines;
    },

    /**
     * If type is date, dateFormat specifies the format of the date. The format
     * specification of the ext library is used:
     *
     * Format  Output      Description
     *	------  ----------  --------------------------------------------------------------
     *	  d      10         Day of the month, 2 digits with leading zeros
     *	  D      Wed        A textual representation of a day, three letters
     *	  j      10         Day of the month without leading zeros
     *	  l      Wednesday  A full textual representation of the day of the week
     *	  S      th         English ordinal day of month suffix, 2 chars (use with j)
     *	  w      3          Numeric representation of the day of the week
     *	  z      9          The julian date, or day of the year (0-365)
     *	  W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
     *	  F      January    A full textual representation of the month
     *	  m      01         Numeric representation of a month, with leading zeros
     *	  M      Jan        Month name abbreviation, three letters
     *	  n      1          Numeric representation of a month, without leading zeros
     *	  t      31         Number of days in the given month
     *	  L      0          Whether its a leap year (1 if it is a leap year, else 0)
     *	  Y      2007       A full numeric representation of a year, 4 digits
     *	  y      07         A two digit representation of a year
     *	  a      pm         Lowercase Ante meridiem and Post meridiem
     *	  A      PM         Uppercase Ante meridiem and Post meridiem
     *	  g      3          12-hour format of an hour without leading zeros
     *	  G      15         24-hour format of an hour without leading zeros
     *	  h      03         12-hour format of an hour with leading zeros
     *	  H      15         24-hour format of an hour with leading zeros
     *	  i      05         Minutes with leading zeros
     *	  s      01         Seconds, with leading zeros
     *	  O      -0600      Difference to Greenwich time (GMT) in hours
     *	  T      CST        Timezone setting of the machine running the code
     *	  Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
     *
     * Example:
     *  F j, Y, g:i a  ->  January 10, 2007, 3:05 pm
     */
    dateFormat: function(){
        return this._jsonProp.dateFormat;
    },

    /**
     * If type is color, this method returns if the fill property should
     *  be set.
     *  @return {Boolean}
     */
    fill: function(){
        return this._jsonProp.fill;
    },

    /**
     * If type is color, this method returns if the stroke property should
     *  be set.
     *  @return {Boolean}
     */
    stroke: function(){
        return this._jsonProp.stroke;
    },

    /**
     * If type is choice, items is a hash map with all alternative values
     * (PropertyItem objects) with id as keys.
     */
    items: function(){
        return this._items.values();
    },

    item: function(value){
        return this._items[value];
    },

    toString: function(){
        return "Property " + this.title() + " (" + this.id() + ")";
    },

    // extended by Kerstin (start)
    complexItems: function(){
        return this._complexItems.values();
    },

    complexItem: function(value){
        return this._complexItems[value];
    }
    // extended by Kerstin (end)
});
<|endoftext|>


<commit_before>// @flow

import React from 'react';
import LocationSubscriber from '@ncigdc/components/LocationSubscriber';
import Tabs from '@ncigdc/uikit/Tabs';
import Link from '@ncigdc/components/Links/Link';
import type { TRawQuery } from '@ncigdc/utils/uri/types';

type TTabbedLinksProps = {
  defaultIndex?: number,
  links: [Object],
  queryParam: string,
  tabToolbar?: React.Element<>,
  hideTabs?: boolean,
  style?: Object,
};
type TTabbedLinks = (props: TTabbedLinksProps) => React.Element<{}>;
const TabbedLinks: TTabbedLinks = (
  { links, queryParam, defaultIndex = 0, tabToolbar, hideTabs, style } = {},
) =>
  <LocationSubscriber>
    {(ctx: {| pathname: string, query: TRawQuery |}) => {
      const foundIndex = links.findIndex(
        x => x.id === (ctx.query || {})[queryParam],
      );
      const activeIndex = foundIndex > -1 ? foundIndex : defaultIndex;

      return (
        <Tabs
          className="test-tabbed-links"
          style={style}
          tabToolbar={tabToolbar}
          tabStyle={{ padding: 0 }}
          tabs={
            hideTabs
              ? []
              : links.map(x =>
                  <Link
                    style={{
                      padding: '1.2rem 1.8rem',
                      textDecoration: 'none',
                      display: 'inline-block',
                    }}
                    key={x.id}
                    query={{ [queryParam]: x.id }}
                    merge
                    className={x.id}
                  >
                    {x.text}
                  </Link>,
                )
          }
          activeIndex={activeIndex}
        >
          {links[activeIndex].component}
        </Tabs>
      );
    }}
  </LocationSubscriber>;

export default TabbedLinks;
<commit_msg>🐛 missing test- prefix in class causing style issue<commit_after>// @flow

import React from 'react';
import LocationSubscriber from '@ncigdc/components/LocationSubscriber';
import Tabs from '@ncigdc/uikit/Tabs';
import Link from '@ncigdc/components/Links/Link';
import type { TRawQuery } from '@ncigdc/utils/uri/types';

type TTabbedLinksProps = {
  defaultIndex?: number,
  links: [Object],
  queryParam: string,
  tabToolbar?: React.Element<>,
  hideTabs?: boolean,
  style?: Object,
};
type TTabbedLinks = (props: TTabbedLinksProps) => React.Element<{}>;
const TabbedLinks: TTabbedLinks = (
  { links, queryParam, defaultIndex = 0, tabToolbar, hideTabs, style } = {},
) =>
  <LocationSubscriber>
    {(ctx: {| pathname: string, query: TRawQuery |}) => {
      const foundIndex = links.findIndex(
        x => x.id === (ctx.query || {})[queryParam],
      );
      const activeIndex = foundIndex > -1 ? foundIndex : defaultIndex;

      return (
        <Tabs
          className="test-tabbed-links"
          style={style}
          tabToolbar={tabToolbar}
          tabStyle={{ padding: 0 }}
          tabs={
            hideTabs
              ? []
              : links.map(x =>
                  <Link
                    style={{
                      padding: '1.2rem 1.8rem',
                      textDecoration: 'none',
                      display: 'inline-block',
                    }}
                    key={x.id}
                    query={{ [queryParam]: x.id }}
                    merge
                    className={'test-' + x.id}
                  >
                    {x.text}
                  </Link>,
                )
          }
          activeIndex={activeIndex}
        >
          {links[activeIndex].component}
        </Tabs>
      );
    }}
  </LocationSubscriber>;

export default TabbedLinks;
<|endoftext|>


<commit_before>var GUI = (function(){
  var controller;
  var settings;

  var stateChangedEvent = (type, detail) => {
    if (controller){
      var force = type === "gui" && detail === "controller";

      if (type === "data" || force){
        controller.ui.btnDownload.disabled = controller.ui.btnReset.disabled = !STATE.hasSavedData();
      }

      if (type === "tracking" || force){
        controller.ui.btnUpload.disabled = controller.ui.btnSettings.disabled = STATE.isTracking();
        controller.ui.btnToggleTracking.innerHTML = STATE.isTracking() ? "Pause Tracking" : "Start Tracking";
      }

      if (type === "data" || force){
        var messageCount = 0;
        var channelCount = 0;

        if (STATE.hasSavedData()){
          messageCount = STATE.getSavefile().countMessages();
          channelCount = STATE.getSavefile().countChannels();
        }

        controller.ui.textStatus.innerHTML = [
          messageCount, " message", (messageCount === 1 ? "" : "s"),
          " from ",
          channelCount, " channel", (channelCount === 1 ? "" : "s")
        ].join("");
      }
    }

    if (settings){
      var force = type === "gui" && detail === "settings";

      if (force){
        settings.ui.cbAutoscroll.checked = SETTINGS.autoscroll;
        settings.ui.optsAfterFirstMsg[SETTINGS.afterFirstMsg].checked = true;
        settings.ui.optsAfterSavedMsg[SETTINGS.afterSavedMsg].checked = true;
      }

      if (type === "setting" || force){
        var autoscrollRev = !SETTINGS.autoscroll;

        // discord polyfills Object.values
        Object.values(settings.ui.optsAfterFirstMsg).forEach(ele => ele.disabled = autoscrollRev);
        Object.values(settings.ui.optsAfterSavedMsg).forEach(ele => ele.disabled = autoscrollRev);
      }
    }
  };

  var registeredEvent = false;

  var setupStateChanged = function(detail){
    if (!registeredEvent){
      STATE.onStateChanged(stateChangedEvent);
      SETTINGS.onSettingsChanged(stateChangedEvent);
      registeredEvent = true;
    }

    stateChangedEvent("gui", detail);
  };

  var root = {
    showController: function(){
      controller = {};

      // styles

      controller.styles = DOM.createStyle([
        ".app, .connecting { bottom: 48px !important; }",
        "#dht-ctrl { position: absolute; bottom: 0; width: 100%; height: 48px; background-color: #FFF; }",
        "#dht-ctrl button { height: 32px; margin: 8px 0 8px 8px; font-size: 16px; padding: 0 12px; background-color: #7289DA; color: #FFF; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.75); }",
        "#dht-ctrl button:disabled { background-color: #7A7A7A; cursor: default; }",
        "#dht-ctrl-close { margin: 8px 8px 8px 0 !important; float: right; }",
        "#dht-ctrl p { display: inline-block; margin: 14px 12px; }",
        "#dht-ctrl input { display: none; }"
      ]);

      // main

      var btn = (id, title) => "<button id='dht-ctrl-"+id+"'>"+title+"</button>";

      controller.ele = DOM.createElement("div", document.body);
      controller.ele.id = "dht-ctrl";

      controller.ele.innerHTML = [
        btn("upload", "Upload & Combine"),
        btn("settings", "Settings"),
        btn("track", ""),
        btn("download", "Download"),
        btn("reset", "Reset"),
        "

",
        "<input id='dht-ctrl-upload-input' type='file' multiple>",
        btn("close", "X")
      ].join("");

      // elements

      controller.ui = {
        btnUpload: DOM.id("dht-ctrl-upload"),
        btnSettings: DOM.id("dht-ctrl-settings"),
        btnToggleTracking: DOM.id("dht-ctrl-track"),
        btnDownload: DOM.id("dht-ctrl-download"),
        btnReset: DOM.id("dht-ctrl-reset"),
        btnClose: DOM.id("dht-ctrl-close"),
        textStatus: DOM.id("dht-ctrl-status"),
        inputUpload: DOM.id("dht-ctrl-upload-input")
      };

      // events

      DOM.listen(controller.ui.btnUpload, "click", () => {
        controller.ui.inputUpload.click();
      });

      DOM.listen(controller.ui.btnSettings, "click", () => {
        root.showSettings();
      });

      DOM.listen(controller.ui.btnToggleTracking, "click", () => {
        STATE.toggleTracking();
      });

      DOM.listen(controller.ui.btnDownload, "click", () => {
        STATE.downloadSavefile();
      });

      DOM.listen(controller.ui.btnReset, "click", () => {
        STATE.resetState();
      });

      DOM.listen(controller.ui.btnClose, "click", () => {
        root.hideController();
        window.DHT_ON_UNLOAD.forEach(f => f());
        window.DHT_LOADED = false;
      });

      DOM.listen(controller.ui.inputUpload, "change", () => {
        for(var file of controller.ui.inputUpload.files){
          var reader = new FileReader();

          reader.onload = function(){
            var obj = {};

            try{
              obj = JSON.parse(reader.result);
            }catch(e){
              alert("Could not parse '"+file.name+"', see console for details.");
              console.error(e);
              return;
            }

            if (SAVEFILE.isValid(obj)){
              STATE.uploadSavefile(file.name, new SAVEFILE(obj));
            }
            else{
              alert("File '"+file.name+"' has an invalid format.");
            }
          };

          reader.readAsText(file, "UTF-8");
        }

        controller.ui.inputUpload.value = null;
      });

      setupStateChanged("controller");
    },

    hideController: function(){
      if (controller){
        DOM.removeElement(controller.ele);
        DOM.removeElement(controller.styles);
        controller = null;
      }
    },

    showSettings: function(){
      settings = {};

      // styles

      settings.styles = DOM.createStyle([
        "#dht-cfg-overlay { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: #000; opacity: 0.5; display: block; z-index: 1000; }",
        "#dht-cfg { position: absolute; left: 50%; top: 50%; width: 800px; height: 308px; margin-left: -400px; margin-top: -154px; padding: 8px; background-color: #fff; z-index: 1001; }",
        "#dht-cfg-note { margin-top: 22px; }",
        "#dht-cfg sub { color: #666; font-size: 13px; }"
      ]);

      // overlay

      settings.overlay = DOM.createElement("div", document.body);
      settings.overlay.id = "dht-cfg-overlay";

      DOM.listen(settings.overlay, "click", () => {
        root.hideSettings();
      });

      // main

      settings.ele = DOM.createElement("div", document.body);
      settings.ele.id = "dht-cfg";

      var radio = (type, id, label) => "<input id='dht-cfg-"+type+"-"+id+"' name='dht-"+type+"' type='radio'> "+label+"
";

      settings.ele.innerHTML = [
        "<input id='dht-cfg-autoscroll' type='checkbox'> Autoscroll
",
        "
",
        "After reaching the first message in channel...
",
        radio("afm", "nothing", "Do Nothing"),
        radio("afm", "pause", "Pause Tracking"),
        radio("afm", "switch", "Switch to Next Channel"),
        "
",
        "After reaching a previously saved message...
",
        radio("asm", "nothing", "Do Nothing"),
        radio("asm", "pause", "Pause Tracking"),
        radio("asm", "switch", "Switch to Next Channel"),
        "
",
        "Discord History Tracker will not save newly received messages, it is specifically made to load and save the server/group/DM history.

",
        "It is recommended to disable link and image previews to avoid putting unnecessary strain on your browser.

",
        "BETA v.3, released 1 Jul 2017",
        "
"
      ].join("");

      // elements

      settings.ui = {
        cbAutoscroll: DOM.id("dht-cfg-autoscroll"),
        optsAfterFirstMsg: {},
        optsAfterSavedMsg: {}
      };

      settings.ui.optsAfterFirstMsg[CONSTANTS.AUTOSCROLL_ACTION_NOTHING] = DOM.id("dht-cfg-afm-nothing");
      settings.ui.optsAfterFirstMsg[CONSTANTS.AUTOSCROLL_ACTION_PAUSE] = DOM.id("dht-cfg-afm-pause");
      settings.ui.optsAfterFirstMsg[CONSTANTS.AUTOSCROLL_ACTION_SWITCH] = DOM.id("dht-cfg-afm-switch");

      settings.ui.optsAfterSavedMsg[CONSTANTS.AUTOSCROLL_ACTION_NOTHING] = DOM.id("dht-cfg-asm-nothing");
      settings.ui.optsAfterSavedMsg[CONSTANTS.AUTOSCROLL_ACTION_PAUSE] = DOM.id("dht-cfg-asm-pause");
      settings.ui.optsAfterSavedMsg[CONSTANTS.AUTOSCROLL_ACTION_SWITCH] = DOM.id("dht-cfg-asm-switch");

      // events

      settings.ui.cbAutoscroll.addEventListener("change", () => {
        SETTINGS.autoscroll = settings.ui.cbAutoscroll.checked;
      });

      Object.keys(settings.ui.optsAfterFirstMsg).forEach(key => {
        DOM.listen(settings.ui.optsAfterFirstMsg[key], "click", () => {
          SETTINGS.afterFirstMsg = key;
        });
      });

      Object.keys(settings.ui.optsAfterSavedMsg).forEach(key => {
        DOM.listen(settings.ui.optsAfterSavedMsg[key], "click", () => {
          SETTINGS.afterSavedMsg = key;
        });
      });

      setupStateChanged("settings");
    },

    hideSettings: function(){
      if (settings){
        DOM.removeElement(settings.overlay);
        DOM.removeElement(settings.ele);
        DOM.removeElement(settings.styles);
        settings = null;
      }
    }
  };

  return root;
})();
<commit_msg>Disable reset button while tracking<commit_after>var GUI = (function(){
  var controller;
  var settings;

  var updateButtonState = () => {
    if (STATE.isTracking()){
      controller.ui.btnUpload.disabled = true;
      controller.ui.btnSettings.disabled = true;
      controller.ui.btnReset.disabled = true;
    }
    else{
      controller.ui.btnUpload.disabled = false;
      controller.ui.btnSettings.disabled = false;
      controller.ui.btnDownload.disabled = controller.ui.btnReset.disabled = !STATE.hasSavedData();
    }
  };

  var stateChangedEvent = (type, detail) => {
    if (controller){
      var force = type === "gui" && detail === "controller";

      if (type === "data" || force){
        updateButtonState();
      }

      if (type === "tracking" || force){
        updateButtonState();
        controller.ui.btnToggleTracking.innerHTML = STATE.isTracking() ? "Pause Tracking" : "Start Tracking";
      }

      if (type === "data" || force){
        var messageCount = 0;
        var channelCount = 0;

        if (STATE.hasSavedData()){
          messageCount = STATE.getSavefile().countMessages();
          channelCount = STATE.getSavefile().countChannels();
        }

        controller.ui.textStatus.innerHTML = [
          messageCount, " message", (messageCount === 1 ? "" : "s"),
          " from ",
          channelCount, " channel", (channelCount === 1 ? "" : "s")
        ].join("");
      }
    }

    if (settings){
      var force = type === "gui" && detail === "settings";

      if (force){
        settings.ui.cbAutoscroll.checked = SETTINGS.autoscroll;
        settings.ui.optsAfterFirstMsg[SETTINGS.afterFirstMsg].checked = true;
        settings.ui.optsAfterSavedMsg[SETTINGS.afterSavedMsg].checked = true;
      }

      if (type === "setting" || force){
        var autoscrollRev = !SETTINGS.autoscroll;

        // discord polyfills Object.values
        Object.values(settings.ui.optsAfterFirstMsg).forEach(ele => ele.disabled = autoscrollRev);
        Object.values(settings.ui.optsAfterSavedMsg).forEach(ele => ele.disabled = autoscrollRev);
      }
    }
  };

  var registeredEvent = false;

  var setupStateChanged = function(detail){
    if (!registeredEvent){
      STATE.onStateChanged(stateChangedEvent);
      SETTINGS.onSettingsChanged(stateChangedEvent);
      registeredEvent = true;
    }

    stateChangedEvent("gui", detail);
  };

  var root = {
    showController: function(){
      controller = {};

      // styles

      controller.styles = DOM.createStyle([
        ".app, .connecting { bottom: 48px !important; }",
        "#dht-ctrl { position: absolute; bottom: 0; width: 100%; height: 48px; background-color: #FFF; }",
        "#dht-ctrl button { height: 32px; margin: 8px 0 8px 8px; font-size: 16px; padding: 0 12px; background-color: #7289DA; color: #FFF; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.75); }",
        "#dht-ctrl button:disabled { background-color: #7A7A7A; cursor: default; }",
        "#dht-ctrl-close { margin: 8px 8px 8px 0 !important; float: right; }",
        "#dht-ctrl p { display: inline-block; margin: 14px 12px; }",
        "#dht-ctrl input { display: none; }"
      ]);

      // main

      var btn = (id, title) => "<button id='dht-ctrl-"+id+"'>"+title+"</button>";

      controller.ele = DOM.createElement("div", document.body);
      controller.ele.id = "dht-ctrl";

      controller.ele.innerHTML = [
        btn("upload", "Upload & Combine"),
        btn("settings", "Settings"),
        btn("track", ""),
        btn("download", "Download"),
        btn("reset", "Reset"),
        "

",
        "<input id='dht-ctrl-upload-input' type='file' multiple>",
        btn("close", "X")
      ].join("");

      // elements

      controller.ui = {
        btnUpload: DOM.id("dht-ctrl-upload"),
        btnSettings: DOM.id("dht-ctrl-settings"),
        btnToggleTracking: DOM.id("dht-ctrl-track"),
        btnDownload: DOM.id("dht-ctrl-download"),
        btnReset: DOM.id("dht-ctrl-reset"),
        btnClose: DOM.id("dht-ctrl-close"),
        textStatus: DOM.id("dht-ctrl-status"),
        inputUpload: DOM.id("dht-ctrl-upload-input")
      };

      // events

      DOM.listen(controller.ui.btnUpload, "click", () => {
        controller.ui.inputUpload.click();
      });

      DOM.listen(controller.ui.btnSettings, "click", () => {
        root.showSettings();
      });

      DOM.listen(controller.ui.btnToggleTracking, "click", () => {
        STATE.toggleTracking();
      });

      DOM.listen(controller.ui.btnDownload, "click", () => {
        STATE.downloadSavefile();
      });

      DOM.listen(controller.ui.btnReset, "click", () => {
        STATE.resetState();
      });

      DOM.listen(controller.ui.btnClose, "click", () => {
        root.hideController();
        window.DHT_ON_UNLOAD.forEach(f => f());
        window.DHT_LOADED = false;
      });

      DOM.listen(controller.ui.inputUpload, "change", () => {
        for(var file of controller.ui.inputUpload.files){
          var reader = new FileReader();

          reader.onload = function(){
            var obj = {};

            try{
              obj = JSON.parse(reader.result);
            }catch(e){
              alert("Could not parse '"+file.name+"', see console for details.");
              console.error(e);
              return;
            }

            if (SAVEFILE.isValid(obj)){
              STATE.uploadSavefile(file.name, new SAVEFILE(obj));
            }
            else{
              alert("File '"+file.name+"' has an invalid format.");
            }
          };

          reader.readAsText(file, "UTF-8");
        }

        controller.ui.inputUpload.value = null;
      });

      setupStateChanged("controller");
    },

    hideController: function(){
      if (controller){
        DOM.removeElement(controller.ele);
        DOM.removeElement(controller.styles);
        controller = null;
      }
    },

    showSettings: function(){
      settings = {};

      // styles

      settings.styles = DOM.createStyle([
        "#dht-cfg-overlay { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: #000; opacity: 0.5; display: block; z-index: 1000; }",
        "#dht-cfg { position: absolute; left: 50%; top: 50%; width: 800px; height: 308px; margin-left: -400px; margin-top: -154px; padding: 8px; background-color: #fff; z-index: 1001; }",
        "#dht-cfg-note { margin-top: 22px; }",
        "#dht-cfg sub { color: #666; font-size: 13px; }"
      ]);

      // overlay

      settings.overlay = DOM.createElement("div", document.body);
      settings.overlay.id = "dht-cfg-overlay";

      DOM.listen(settings.overlay, "click", () => {
        root.hideSettings();
      });

      // main

      settings.ele = DOM.createElement("div", document.body);
      settings.ele.id = "dht-cfg";

      var radio = (type, id, label) => "<input id='dht-cfg-"+type+"-"+id+"' name='dht-"+type+"' type='radio'> "+label+"
";

      settings.ele.innerHTML = [
        "<input id='dht-cfg-autoscroll' type='checkbox'> Autoscroll
",
        "
",
        "After reaching the first message in channel...
",
        radio("afm", "nothing", "Do Nothing"),
        radio("afm", "pause", "Pause Tracking"),
        radio("afm", "switch", "Switch to Next Channel"),
        "
",
        "After reaching a previously saved message...
",
        radio("asm", "nothing", "Do Nothing"),
        radio("asm", "pause", "Pause Tracking"),
        radio("asm", "switch", "Switch to Next Channel"),
        "
",
        "Discord History Tracker will not save newly received messages, it is specifically made to load and save the server/group/DM history.

",
        "It is recommended to disable link and image previews to avoid putting unnecessary strain on your browser.

",
        "BETA v.3, released 1 Jul 2017",
        "
"
      ].join("");

      // elements

      settings.ui = {
        cbAutoscroll: DOM.id("dht-cfg-autoscroll"),
        optsAfterFirstMsg: {},
        optsAfterSavedMsg: {}
      };

      settings.ui.optsAfterFirstMsg[CONSTANTS.AUTOSCROLL_ACTION_NOTHING] = DOM.id("dht-cfg-afm-nothing");
      settings.ui.optsAfterFirstMsg[CONSTANTS.AUTOSCROLL_ACTION_PAUSE] = DOM.id("dht-cfg-afm-pause");
      settings.ui.optsAfterFirstMsg[CONSTANTS.AUTOSCROLL_ACTION_SWITCH] = DOM.id("dht-cfg-afm-switch");

      settings.ui.optsAfterSavedMsg[CONSTANTS.AUTOSCROLL_ACTION_NOTHING] = DOM.id("dht-cfg-asm-nothing");
      settings.ui.optsAfterSavedMsg[CONSTANTS.AUTOSCROLL_ACTION_PAUSE] = DOM.id("dht-cfg-asm-pause");
      settings.ui.optsAfterSavedMsg[CONSTANTS.AUTOSCROLL_ACTION_SWITCH] = DOM.id("dht-cfg-asm-switch");

      // events

      settings.ui.cbAutoscroll.addEventListener("change", () => {
        SETTINGS.autoscroll = settings.ui.cbAutoscroll.checked;
      });

      Object.keys(settings.ui.optsAfterFirstMsg).forEach(key => {
        DOM.listen(settings.ui.optsAfterFirstMsg[key], "click", () => {
          SETTINGS.afterFirstMsg = key;
        });
      });

      Object.keys(settings.ui.optsAfterSavedMsg).forEach(key => {
        DOM.listen(settings.ui.optsAfterSavedMsg[key], "click", () => {
          SETTINGS.afterSavedMsg = key;
        });
      });

      setupStateChanged("settings");
    },

    hideSettings: function(){
      if (settings){
        DOM.removeElement(settings.overlay);
        DOM.removeElement(settings.ele);
        DOM.removeElement(settings.styles);
        settings = null;
      }
    }
  };

  return root;
})();
<|endoftext|>


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

var config = nconf.file({
	file: 'defaults.json',
	dir: __dirname,
	search: true
}).env();

Object.keys(config.get()).filter(function (key) {
	return !!key.match(/^SOFTAP_.*$/)
}).forEach(function (key) {
	config.set(key.replace('SOFTAP_', '').toLowerCase(), config.get(key))
})

module.exports = config;
<commit_msg>Allow use of argv for setting config options<commit_after>var nconf = require('nconf');

var config = nconf.file({
	file: 'defaults.json',
	dir: __dirname,
	search: true
}).env().argv();

Object.keys(config.get()).filter(function (key) {
	return !!key.match(/^SOFTAP_.*$/)
}).forEach(function (key) {
	config.set(key.replace('SOFTAP_', '').toLowerCase(), config.get(key))
})

module.exports = config;
<|endoftext|>


<commit_before>
// Transaction
// -------
var Promise      = require('./promise')
var EventEmitter = require('events').EventEmitter
var inherits     = require('inherits')

var makeKnex     = require('./util/make-knex')
var debug        = require('debug')('knex:tx')

import {assign, uniqueId} from 'lodash'

// Acts as a facade for a Promise, keeping the internal state
// and managing any child transactions.
function Transaction(client, container, config, outerTx) {

  var txid = this.txid = uniqueId('trx')

  this.client    = client
  this.outerTx   = outerTx
  this.trxClient = undefined;
  this._debug    = client.config && client.config.debug

  debug('%s: Starting %s transaction', txid, outerTx ? 'nested' : 'top level')

  this._promise = Promise.using(this.acquireConnection(client, config, txid), (connection) => {

    var trxClient = this.trxClient = makeTxClient(this, client, connection)
    var init      = client.transacting ? this.savepoint(connection) : this.begin(connection)

    init.then(() => {
      return makeTransactor(this, connection, trxClient)
    })
    .then((transactor) => {
      // If we've returned a "thenable" from the transaction container, assume
      // the rollback and commit are chained to this object's success / failure.
      // Directly thrown errors are treated as automatic rollbacks.
      var result
      try {
        result = container(transactor)
      } catch (err) {
        result = Promise.reject(err)
      }
      if (result && result.then && typeof result.then === 'function') {
        result.then((val) => {
          transactor.commit(val)
        })
        .catch((err) => {
          transactor.rollback(err)
        })
      }
    })
    .catch((e) => this._rejecter(e))

    return new Promise((resolver, rejecter) => {
      this._resolver = resolver
      this._rejecter = rejecter
    })
  })

  this._completed  = false

  // If there's a wrapping transaction, we need to wait for any older sibling
  // transactions to settle (commit or rollback) before we can start, and we
  // need to register ourselves with the parent transaction so any younger
  // siblings can wait for us to complete before they can start.
  this._previousSibling = Promise.resolve(true);
  if (outerTx) {
    if (outerTx._lastChild) this._previousSibling = outerTx._lastChild;
    outerTx._lastChild = this._promise;
  }
}
inherits(Transaction, EventEmitter)

assign(Transaction.prototype, {

  isCompleted: function() {
    return this._completed || this.outerTx && this.outerTx.isCompleted() || false
  },

  begin: function(conn) {
    return this.query(conn, 'BEGIN;')
  },

  savepoint: function(conn) {
    return this.query(conn, 'SAVEPOINT ' + this.txid + ';')
  },

  commit: function(conn, value) {
    return this.query(conn, 'COMMIT;', 1, value)
  },

  release: function(conn, value) {
    return this.query(conn, 'RELEASE SAVEPOINT ' + this.txid + ';', 1, value)
  },

  rollback: function(conn, error) {
    return this.query(conn, 'ROLLBACK;', 2, error)
      .timeout(5000)
      .catch(Promise.TimeoutError, () => {
        this._resolver();
      });
  },

  rollbackTo: function(conn, error) {
    return this.query(conn, 'ROLLBACK TO SAVEPOINT ' + this.txid, 2, error)
      .timeout(5000)
      .catch(Promise.TimeoutError, () => {
        this._resolver();
      });
  },

  query: function(conn, sql, status, value) {
    var q = this.trxClient.query(conn, sql)
      .catch((err) => {
        status = 2
        value  = err
        this._completed = true
        debug('%s error running transaction query', this.txid)
      })
      .tap(() => {
        if (status === 1) this._resolver(value)
        if (status === 2) this._rejecter(value)
      })
    if (status === 1 || status === 2) {
      this._completed = true
    }
    return q;
  },

  debug: function(enabled) {
    this._debug = arguments.length ? enabled : true;
    return this
  },

  _skipping: function(sql) {
    return Promise.reject(new Error('Transaction ' + this.txid + ' has already been released skipping: ' + sql))
  },

  // Acquire a connection and create a disposer - either using the one passed
  // via config or getting one off the client. The disposer will be called once
  // the original promise is marked completed.
  acquireConnection: function(client, config, txid) {
    var configConnection = config && config.connection
    return Promise.try(function() {
      return configConnection || client.acquireConnection().completed
    })
    .disposer(function(connection) {
      if (!configConnection) {
        debug('%s: releasing connection', txid)
        client.releaseConnection(connection)
      } else {
        debug('%s: not releasing external connection', txid)
      }
    })
  }

})

// The transactor is a full featured knex object, with a "commit",
// a "rollback" and a "savepoint" function. The "savepoint" is just
// sugar for creating a new transaction. If the rollback is run
// inside a savepoint, it rolls back to the last savepoint - otherwise
// it rolls back the transaction.
function makeTransactor(trx, connection, trxClient) {

  var transactor = makeKnex(trxClient)

  transactor.transaction = function(container, options) {
    return new trxClient.Transaction(trxClient, container, options, trx)
  }
  transactor.savepoint = function(container, options) {
    return transactor.transaction(container, options)
  }

  if (trx.client.transacting) {
    transactor.commit = function(value) {
      return trx.release(connection, value)
    }
    transactor.rollback = function(error) {
      return trx.rollbackTo(connection, error);
    }
  } else {
    transactor.commit = function(value) {
      return trx.commit(connection, value)
    }
    transactor.rollback = function(error) {
      return trx.rollback(connection, error)
    }
  }

  return transactor
}

// We need to make a client object which always acquires the same
// connection and does not release back into the pool.
function makeTxClient(trx, client, connection) {

  var trxClient                = Object.create(client.constructor.prototype)
  trxClient.config             = client.config
  trxClient.driver             = client.driver
  trxClient.connectionSettings = client.connectionSettings
  trxClient.transacting        = true

  trxClient.on('query', function(arg) {
    trx.emit('query', arg)
    client.emit('query', arg)
  })

  trxClient.on('query-error', function(err, obj) {
    trx.emit('query-error', err, obj)
    client.emit('query-error', err, obj)
  })

  trxClient.on('query-response', function(response, obj, builder) {
    trx.emit('query-response', response, obj, builder)
    client.emit('query-response', response, obj, builder)
  })

  var _query = trxClient.query;
  trxClient.query  = function(conn, obj) {
    var completed = trx.isCompleted()
    return Promise.try(function() {
      if (conn !== connection) throw new Error('Invalid connection for transaction query.')
      if (completed) completedError(trx, obj)
      return _query.call(trxClient, conn, obj)
    })
  }
  var _stream = trxClient.stream
  trxClient.stream = function(conn, obj, stream, options) {
    var completed = trx.isCompleted()
    return Promise.try(function() {
      if (conn !== connection) throw new Error('Invalid connection for transaction query.')
      if (completed) completedError(trx, obj)
      return _stream.call(trxClient, conn, obj, stream, options)
    })
  }
  trxClient.acquireConnection = function() {
    return trx._previousSibling.reflect().then(function () {
      return connection
    })
  }
  trxClient.releaseConnection = function() {
    return Promise.resolve()
  }

  return trxClient
}

function completedError(trx, obj) {
  var sql = typeof obj === 'string' ? obj : obj && obj.sql
  debug('%s: Transaction completed: %s', trx.id, sql)
  throw new Error('Transaction query already complete, run with DEBUG=knex:tx for more info')
}

var promiseInterface = [
  'then', 'bind', 'catch', 'finally', 'asCallback',
  'spread', 'map', 'reduce', 'tap', 'thenReturn',
  'return', 'yield', 'ensure', 'exec', 'reflect'
]

// Creates a method which "coerces" to a promise, by calling a
// "then" method on the current `Target`.
promiseInterface.forEach(function(method) {
  Transaction.prototype[method] = function() {
    return (this._promise = this._promise[method].apply(this._promise, arguments))
  }
})

module.exports = Transaction;
<commit_msg>Make transaction-client match main client on acquireConnection()<commit_after>
// Transaction
// -------
var Promise      = require('./promise')
var EventEmitter = require('events').EventEmitter
var inherits     = require('inherits')

var noop         = require('./util/noop')
var makeKnex     = require('./util/make-knex')
var debug        = require('debug')('knex:tx')

import {assign, uniqueId} from 'lodash'

// Acts as a facade for a Promise, keeping the internal state
// and managing any child transactions.
function Transaction(client, container, config, outerTx) {

  var txid = this.txid = uniqueId('trx')

  this.client    = client
  this.outerTx   = outerTx
  this.trxClient = undefined;
  this._debug    = client.config && client.config.debug

  debug('%s: Starting %s transaction', txid, outerTx ? 'nested' : 'top level')

  this._promise = Promise.using(this.acquireConnection(client, config, txid), (connection) => {

    var trxClient = this.trxClient = makeTxClient(this, client, connection)
    var init      = client.transacting ? this.savepoint(connection) : this.begin(connection)

    init.then(() => {
      return makeTransactor(this, connection, trxClient)
    })
    .then((transactor) => {
      // If we've returned a "thenable" from the transaction container, assume
      // the rollback and commit are chained to this object's success / failure.
      // Directly thrown errors are treated as automatic rollbacks.
      var result
      try {
        result = container(transactor)
      } catch (err) {
        result = Promise.reject(err)
      }
      if (result && result.then && typeof result.then === 'function') {
        result.then((val) => {
          transactor.commit(val)
        })
        .catch((err) => {
          transactor.rollback(err)
        })
      }
    })
    .catch((e) => this._rejecter(e))

    return new Promise((resolver, rejecter) => {
      this._resolver = resolver
      this._rejecter = rejecter
    })
  })

  this._completed  = false

  // If there's a wrapping transaction, we need to wait for any older sibling
  // transactions to settle (commit or rollback) before we can start, and we
  // need to register ourselves with the parent transaction so any younger
  // siblings can wait for us to complete before they can start.
  this._previousSibling = Promise.resolve(true);
  if (outerTx) {
    if (outerTx._lastChild) this._previousSibling = outerTx._lastChild;
    outerTx._lastChild = this._promise;
  }
}
inherits(Transaction, EventEmitter)

assign(Transaction.prototype, {

  isCompleted: function() {
    return this._completed || this.outerTx && this.outerTx.isCompleted() || false
  },

  begin: function(conn) {
    return this.query(conn, 'BEGIN;')
  },

  savepoint: function(conn) {
    return this.query(conn, 'SAVEPOINT ' + this.txid + ';')
  },

  commit: function(conn, value) {
    return this.query(conn, 'COMMIT;', 1, value)
  },

  release: function(conn, value) {
    return this.query(conn, 'RELEASE SAVEPOINT ' + this.txid + ';', 1, value)
  },

  rollback: function(conn, error) {
    return this.query(conn, 'ROLLBACK;', 2, error)
      .timeout(5000)
      .catch(Promise.TimeoutError, () => {
        this._resolver();
      });
  },

  rollbackTo: function(conn, error) {
    return this.query(conn, 'ROLLBACK TO SAVEPOINT ' + this.txid, 2, error)
      .timeout(5000)
      .catch(Promise.TimeoutError, () => {
        this._resolver();
      });
  },

  query: function(conn, sql, status, value) {
    var q = this.trxClient.query(conn, sql)
      .catch((err) => {
        status = 2
        value  = err
        this._completed = true
        debug('%s error running transaction query', this.txid)
      })
      .tap(() => {
        if (status === 1) this._resolver(value)
        if (status === 2) this._rejecter(value)
      })
    if (status === 1 || status === 2) {
      this._completed = true
    }
    return q;
  },

  debug: function(enabled) {
    this._debug = arguments.length ? enabled : true;
    return this
  },

  _skipping: function(sql) {
    return Promise.reject(new Error('Transaction ' + this.txid + ' has already been released skipping: ' + sql))
  },

  // Acquire a connection and create a disposer - either using the one passed
  // via config or getting one off the client. The disposer will be called once
  // the original promise is marked completed.
  acquireConnection: function(client, config, txid) {
    var configConnection = config && config.connection
    return Promise.try(function() {
      return configConnection || client.acquireConnection().completed
    })
    .disposer(function(connection) {
      if (!configConnection) {
        debug('%s: releasing connection', txid)
        client.releaseConnection(connection)
      } else {
        debug('%s: not releasing external connection', txid)
      }
    })
  }

})

// The transactor is a full featured knex object, with a "commit",
// a "rollback" and a "savepoint" function. The "savepoint" is just
// sugar for creating a new transaction. If the rollback is run
// inside a savepoint, it rolls back to the last savepoint - otherwise
// it rolls back the transaction.
function makeTransactor(trx, connection, trxClient) {

  var transactor = makeKnex(trxClient)

  transactor.transaction = function(container, options) {
    return new trxClient.Transaction(trxClient, container, options, trx)
  }
  transactor.savepoint = function(container, options) {
    return transactor.transaction(container, options)
  }

  if (trx.client.transacting) {
    transactor.commit = function(value) {
      return trx.release(connection, value)
    }
    transactor.rollback = function(error) {
      return trx.rollbackTo(connection, error);
    }
  } else {
    transactor.commit = function(value) {
      return trx.commit(connection, value)
    }
    transactor.rollback = function(error) {
      return trx.rollback(connection, error)
    }
  }

  return transactor
}

// We need to make a client object which always acquires the same
// connection and does not release back into the pool.
function makeTxClient(trx, client, connection) {

  var trxClient                = Object.create(client.constructor.prototype)
  trxClient.config             = client.config
  trxClient.driver             = client.driver
  trxClient.connectionSettings = client.connectionSettings
  trxClient.transacting        = true

  trxClient.on('query', function(arg) {
    trx.emit('query', arg)
    client.emit('query', arg)
  })

  trxClient.on('query-error', function(err, obj) {
    trx.emit('query-error', err, obj)
    client.emit('query-error', err, obj)
  })

  trxClient.on('query-response', function(response, obj, builder) {
    trx.emit('query-response', response, obj, builder)
    client.emit('query-response', response, obj, builder)
  })

  var _query = trxClient.query;
  trxClient.query  = function(conn, obj) {
    var completed = trx.isCompleted()
    return Promise.try(function() {
      if (conn !== connection) throw new Error('Invalid connection for transaction query.')
      if (completed) completedError(trx, obj)
      return _query.call(trxClient, conn, obj)
    })
  }
  var _stream = trxClient.stream
  trxClient.stream = function(conn, obj, stream, options) {
    var completed = trx.isCompleted()
    return Promise.try(function() {
      if (conn !== connection) throw new Error('Invalid connection for transaction query.')
      if (completed) completedError(trx, obj)
      return _stream.call(trxClient, conn, obj, stream, options)
    })
  }
  trxClient.acquireConnection = function () {
    return {
      completed: trx._previousSibling.reflect().then(function () {
        return connection
      }),
      abort: noop
    }
  }
  trxClient.releaseConnection = function() {
    return Promise.resolve()
  }

  return trxClient
}

function completedError(trx, obj) {
  var sql = typeof obj === 'string' ? obj : obj && obj.sql
  debug('%s: Transaction completed: %s', trx.id, sql)
  throw new Error('Transaction query already complete, run with DEBUG=knex:tx for more info')
}

var promiseInterface = [
  'then', 'bind', 'catch', 'finally', 'asCallback',
  'spread', 'map', 'reduce', 'tap', 'thenReturn',
  'return', 'yield', 'ensure', 'exec', 'reflect'
]

// Creates a method which "coerces" to a promise, by calling a
// "then" method on the current `Target`.
promiseInterface.forEach(function(method) {
  Transaction.prototype[method] = function() {
    return (this._promise = this._promise[method].apply(this._promise, arguments))
  }
})

module.exports = Transaction;
<|endoftext|>


<commit_before>var DirectoryPickerView = fileBrowser.FileExplorer.extend({

  initialize: function() {
    this.structure                  = {};
    this.options.closeDirsByDefault = true;
    this.options.hideFiles          = true;

    this.ajaxChildDataUrl = this.options.ajaxChildDataUrl;

    this.render();

    var self = this;
    this.options.actionHandlers = [
      {
        name: 'Select',
        description: 'Select directory',
        iconHtml: 'Add',
        logic: function(result) {
          self.addSource(self, result.path);
        }
      }
    ];
  },

  addSource: function(fileExplorer, path) {
    var self = this;
    $.post(
      '/administration/sources/json/',
      {path: path},
      function(response) {
        self.updateSources();
      }
    );
  },

  deleteSource: function(id) {
    var self = this;
    this.confirm(
      'Delete source directory',
      'Are you sure you want to delete this?',
      function() {
        $.post(
          '/administration/sources/delete/json/' + id + '/',
          {},
          function(response) {
            archivematicaNotifications.add({
              message: response.message
            });
            self.updateSources();
          }
        );
      }
    );
  },

  updateSources: function(cb) {
    var self = this;
    $.get('/administration/sources/json/', function(results) {
      tableTemplate = _.template($('#template-source-directory-table').html());
      rowTemplate   = _.template($('#template-source-directory-table-row').html());

      $('#directories').empty();
      $('#directories').off('click');

      if (results['directories'].length) {
        var rowHtml = '';

        for(var index in results['directories']) {
          rowHtml += rowTemplate({
            id:   results.directories[index].id,
            path: results.directories[index].path
          });
        }

        $('#directories').append(tableTemplate({rows: rowHtml}));

        $('#directories').on('click', 'a', function() {
          var directoryId = $(this).attr('id').replace('directory_', '');
          self.deleteSource(directoryId);
        });
      }

      if (cb != undefined) {
        cb();
      }
    });
  },

  alert: function(title, message) {
    $('
' + message + '
')
      .dialog({
        title: title,
        width: 200,
        height: 200,
        modal: true,
        buttons: [
          {
            text: 'OK',
            click: function() {
              $(this).dialog('close');
            }
          }
        ]
      });
  },

  confirm: function(title, message, logic) {
    $('
' + message + '
')
      .dialog({
        title: title,
        width: 200,
        height: 200,
        modal: true,
        buttons: [
          {
            text: 'Yes',
            click: function() {
              $(this).dialog('close');
              logic();
            }
          },
          {
            text: 'Cancel',
            click: function() {
              $(this).dialog('close');
            }
          }
        ]
      });
  }
});
<commit_msg>Re-added file hiding to directory picker.<commit_after>var DirectoryPickerView = fileBrowser.FileExplorer.extend({

  initialize: function() {
    this.structure                  = {};
    this.options.closeDirsByDefault = true;

    // hide all files
    this.options.entryDisplayFilter = function(entry) {
      if (entry.children == undefined) {
          return false;
      }
      return true;
    };

    this.ajaxChildDataUrl = this.options.ajaxChildDataUrl;

    this.render();

    var self = this;
    this.options.actionHandlers = [
      {
        name: 'Select',
        description: 'Select directory',
        iconHtml: 'Add',
        logic: function(result) {
          self.addSource(self, result.path);
        }
      }
    ];
  },

  addSource: function(fileExplorer, path) {
    var self = this;
    $.post(
      '/administration/sources/json/',
      {path: path},
      function(response) {
        self.updateSources();
      }
    );
  },

  deleteSource: function(id) {
    var self = this;
    this.confirm(
      'Delete source directory',
      'Are you sure you want to delete this?',
      function() {
        $.post(
          '/administration/sources/delete/json/' + id + '/',
          {},
          function(response) {
            archivematicaNotifications.add({
              message: response.message
            });
            self.updateSources();
          }
        );
      }
    );
  },

  updateSources: function(cb) {
    var self = this;
    $.get('/administration/sources/json/', function(results) {
      tableTemplate = _.template($('#template-source-directory-table').html());
      rowTemplate   = _.template($('#template-source-directory-table-row').html());

      $('#directories').empty();
      $('#directories').off('click');

      if (results['directories'].length) {
        var rowHtml = '';

        for(var index in results['directories']) {
          rowHtml += rowTemplate({
            id:   results.directories[index].id,
            path: results.directories[index].path
          });
        }

        $('#directories').append(tableTemplate({rows: rowHtml}));

        $('#directories').on('click', 'a', function() {
          var directoryId = $(this).attr('id').replace('directory_', '');
          self.deleteSource(directoryId);
        });
      }

      if (cb != undefined) {
        cb();
      }
    });
  },

  alert: function(title, message) {
    $('
' + message + '
')
      .dialog({
        title: title,
        width: 200,
        height: 200,
        modal: true,
        buttons: [
          {
            text: 'OK',
            click: function() {
              $(this).dialog('close');
            }
          }
        ]
      });
  },

  confirm: function(title, message, logic) {
    $('
' + message + '
')
      .dialog({
        title: title,
        width: 200,
        height: 200,
        modal: true,
        buttons: [
          {
            text: 'Yes',
            click: function() {
              $(this).dialog('close');
              logic();
            }
          },
          {
            text: 'Cancel',
            click: function() {
              $(this).dialog('close');
            }
          }
        ]
      });
  }
});
<|endoftext|>


<commit_before>import extractPageContent from 'src/page-analysis/content_script/extract-page-content'

/**
 * Given a URL will attempt an async fetch of the text and metadata from the page
 * which the URL points to.
 *
 * @param {string} url The URL which points to the page to fetch text + meta-data for.
 * @return {any} Object containing `text` and `metadata` objects containing given data
 *  for the found page pointed at by the URL.
 */
export default async function fetchPageData({
    url = '',
} = {}) {
    const doc = await fetchDOMFromUrl(url)
    const loc = getLocationFromURL(url)

    const { text, metadata, favIcon } = await extractPageContent({ doc, loc, url })
    return { text, metadata, favIcon }
}

/**
 * Given a URL string, converts it to a Location-interface compatible object.
 * Uses experimental URL API: https://developer.mozilla.org/en/docs/Web/API/URL
 *
 * @param {string} url The URL to get Location-interface object for.
 * @return {Location} A Location-interface compatible object.
 */
const getLocationFromURL = url => new URL(url)

/**
 * Async function that given a URL will attempt to grab the current DOM which it points to.
 * Uses native XMLHttpRequest API, as newer Fetch API doesn't seem to support fetching of
 * the DOM; the Response object must be parsed.
 *
 * @param {string} url The URL to fetch the DOM for.
 * @return {Document} The DOM which the URL points to.
 */
const fetchDOMFromUrl = async url => new Promise((resolve, reject) => {
    const req = new XMLHttpRequest()

    req.onload = () => resolve(req.responseXML)
    req.onerror = () => reject(req.statusText)

    req.open('GET', url)

    // Sets the responseXML to be of Document/DOM type
    req.responseType = 'document'
    req.send()
})
<commit_msg>Improve fetch&analyse error handling<commit_after>import extractPageContent from 'src/page-analysis/content_script/extract-page-content'

/**
 * Given a URL will attempt an async fetch of the text and metadata from the page
 * which the URL points to.
 *
 * @param {string} url The URL which points to the page to fetch text + meta-data for.
 * @return {any} Object containing `text` and `metadata` objects containing given data
 *  for the found page pointed at by the URL.
 */
export default async function fetchPageData({
    url = '',
} = {}) {
    const loc = getLocationFromURL(url)
    const doc = await fetchDOMFromUrl(url)
    // If DOM couldn't be fetched, then we can't get text/metadata
    if (!doc) { throw new Error(`Cannot get DOM from URL: ${url}`) }

    const { text, metadata, favIcon } = await extractPageContent({ doc, loc, url })
    return { text, metadata, favIcon }
}

/**
 * Given a URL string, converts it to a Location-interface compatible object.
 * Uses experimental URL API: https://developer.mozilla.org/en/docs/Web/API/URL
 *
 * @param {string} url The URL to get Location-interface object for.
 * @return {Location} A Location-interface compatible object.
 */
const getLocationFromURL = url => new URL(url)

/**
 * Async function that given a URL will attempt to grab the current DOM which it points to.
 * Uses native XMLHttpRequest API, as newer Fetch API doesn't seem to support fetching of
 * the DOM; the Response object must be parsed.
 *
 * @param {string} url The URL to fetch the DOM for.
 * @return {Document} The DOM which the URL points to.
 */
const fetchDOMFromUrl = async url => new Promise((resolve, reject) => {
    const req = new XMLHttpRequest()

    req.onload = () => resolve(req.responseXML)
    req.onerror = () => reject(new Error(`Failed XHR fetching for URL: ${url}`))

    req.open('GET', url)

    // Sets the responseXML to be of Document/DOM type
    req.responseType = 'document'
    req.send()
})
<|endoftext|>


<commit_before>/*
 *
 * CS3283/4 - query.js
 * This class deals with query statements for the facade storage
 * This class will communicate with sqlCommands to get a certain command for
 *
 */

import SqlCommands from '../sql-related/sqlCommands';

import exceptions from '../exceptions';

const sqlCommands = new SqlCommands();

export default class query {
  static constructor() {}

  static getFullTableData(tableName) {
    return sqlCommands.getFullTableData(tableName);
  }

  static loadFolder(folderId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) => {
        resolve(() => {
          let fileArr;
          let folderArr;
          sqlCommands.loadFilesFromFolder(folderId)
            .then((returnedArr) => {
              fileArr = returnedArr;
            }).catch(sqlError => sqlError);

          sqlCommands.loadFoldersFromFolder(folderId)
            .then((returnedArr) => {
              folderArr = returnedArr;
            }).catch(sqlError => sqlError);

          resolve([fileArr, folderArr]);
        });
      });
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static loadFile(fileId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) => {
        resolve(() => {
          sqlCommands.loadFile(fileId)
          .then(data => data)
          .catch(sqlError => sqlError);
        });
      });
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

}
<commit_msg>Add searchString implementation to query.js<commit_after>/*
 *
 * CS3283/4 - query.js
 * This class deals with query statements for the facade storage
 * This class will communicate with sqlCommands to get a certain command for
 *
 */

import SqlCommands from '../sql-related/sqlCommands';

import exceptions from '../exceptions';

const sqlCommands = new SqlCommands();

export default class query {
  static constructor() {}

  static getFullTableData(tableName) {
    return sqlCommands.getFullTableData(tableName);
  }

  static searchString(searchString) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) => {
        resolve(() => {
          let fileArr;
          let folderArr;

          sqlCommands.partialSearchFile(searchString)
            .then((returnedArr) => {
              fileArr = returnedArr;
            })
            .catch(sqlError => sqlError);

          sqlCommands.partialSearchFolder(searchString)
            .then((returnedArr) => {
              folderArr = returnedArr;
            })
            .catch(sqlError => sqlError);

          resolve([fileArr, folderArr]);
        });
      });
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static loadFolder(folderId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) => {
        resolve(() => {
          let fileArr;
          let folderArr;
          sqlCommands.loadFilesFromFolder(folderId)
            .then((returnedArr) => {
              fileArr = returnedArr;
            }).catch(sqlError => sqlError);

          sqlCommands.loadFoldersFromFolder(folderId)
            .then((returnedArr) => {
              folderArr = returnedArr;
            }).catch(sqlError => sqlError);

          resolve([fileArr, folderArr]);
        });
      });
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

  static loadFile(fileId) {
    if (typeof Promise === 'function') {
      return new Promise((resolve, reject) => {
        resolve(() => {
          sqlCommands.loadFile(fileId)
          .then(data => data)
          .catch(sqlError => sqlError);
        });
      });
    } else {
      throw new exceptions.PromiseFunctionNotDefined();
    }
  }

}
<|endoftext|>


<commit_before>/**
 * Local environment settings
 *
 * Use this file to specify configuration settings for use while developing
 * the app on your personal system: for example, this would be a good place
 * to store database or email passwords that apply only to you, and shouldn't
 * be shared with others in your organization.
 *
 * These settings take precedence over all other config files, including those
 * in the env/ subfolder.
 *
 * PLEASE NOTE:
 *		local.js is included in your .gitignore, so if you're using git
 *		as a version control solution for your Sails app, keep in mind that
 *		this file won't be committed to your repository!
 *
 *		Good news is, that means you can specify configuration for your local
 *		machine in this file without inadvertently committing personal information
 *		(like database passwords) to the repo.  Plus, this prevents other members
 *		of your team from commiting their local configuration changes on top of yours.
 *
 *    In a production environment, you probably want to leave this file out
 *    entirely and leave all your settings in env/production.js
 *
 *
 * For more information, check out:
 * http://sailsjs.org/#!/documentation/anatomy/myApp/config/local.js.html
 */

module.exports = {

  /***************************************************************************
   * Your SSL certificate and key, if you want to be able to serve HTTP      *
   * responses over https:// and/or use websockets over the wss:// protocol  *
   * (recommended for HTTP, strongly encouraged for WebSockets)              *
   *                                                                         *
   * In this example, we'll assume you created a folder in your project,     *
   * `config/ssl` and dumped your certificate/key files there:               *
   ***************************************************************************/

  // ssl: {
  //   ca: require('fs').readFileSync(__dirname + './ssl/my_apps_ssl_gd_bundle.crt'),
  //   key: require('fs').readFileSync(__dirname + './ssl/my_apps_ssl.key'),
  //   cert: require('fs').readFileSync(__dirname + './ssl/my_apps_ssl.crt')
  // },

  /***************************************************************************
   * The `port` setting determines which TCP port your app will be           *
   * deployed on.                                                            *
   *                                                                         *
   * Ports are a transport-layer concept designed to allow many different    *
   * networking applications run at the same time on a single computer.      *
   * More about ports:                                                       *
   * http://en.wikipedia.org/wiki/Port_(computer_networking)                 *
   *                                                                         *
   * By default, if it's set, Sails uses the `PORT` environment variable.    *
   * Otherwise it falls back to port 1337.                                   *
   *                                                                         *
   * In env/production.js, you'll probably want to change this setting       *
   * to 80 (http://) or 443 (https://) if you have an SSL certificate        *
   ***************************************************************************/

  // port: process.env.PORT || 1337,

  /***************************************************************************
   * The runtime "environment" of your Sails app is either typically         *
   * 'development' or 'production'.                                          *
   *                                                                         *
   * In development, your Sails app will go out of its way to help you       *
   * (for instance you will receive more descriptive error and               *
   * debugging output)                                                       *
   *                                                                         *
   * In production, Sails configures itself (and its dependencies) to        *
   * optimize performance. You should always put your app in production mode *
   * before you deploy it to a server.  This helps ensure that your Sails    *
   * app remains stable, performant, and scalable.                           *
   *                                                                         *
   * By default, Sails sets its environment using the `NODE_ENV` environment *
   * variable.  If NODE_ENV is not set, Sails will run in the                *
   * 'development' environment.                                              *
   ***************************************************************************/

    environment: process.env.NODE_ENV || 'development',
    port: process.env.OPENSHIFT_NODEJS_PORT||8080,
    host: process.env.OPENSHIFT_NODEJS_IP||'127.0.0.1',
    explicitHost: process.env.OPENSHIFT_APP_DNS || "localhost"

    hookTimeout: 30000
};
<commit_msg>DEBUG: Increased grunt timeout settings<commit_after>/**
 * Local environment settings
 *
 * Use this file to specify configuration settings for use while developing
 * the app on your personal system: for example, this would be a good place
 * to store database or email passwords that apply only to you, and shouldn't
 * be shared with others in your organization.
 *
 * These settings take precedence over all other config files, including those
 * in the env/ subfolder.
 *
 * PLEASE NOTE:
 *		local.js is included in your .gitignore, so if you're using git
 *		as a version control solution for your Sails app, keep in mind that
 *		this file won't be committed to your repository!
 *
 *		Good news is, that means you can specify configuration for your local
 *		machine in this file without inadvertently committing personal information
 *		(like database passwords) to the repo.  Plus, this prevents other members
 *		of your team from commiting their local configuration changes on top of yours.
 *
 *    In a production environment, you probably want to leave this file out
 *    entirely and leave all your settings in env/production.js
 *
 *
 * For more information, check out:
 * http://sailsjs.org/#!/documentation/anatomy/myApp/config/local.js.html
 */

module.exports = {

  /***************************************************************************
   * Your SSL certificate and key, if you want to be able to serve HTTP      *
   * responses over https:// and/or use websockets over the wss:// protocol  *
   * (recommended for HTTP, strongly encouraged for WebSockets)              *
   *                                                                         *
   * In this example, we'll assume you created a folder in your project,     *
   * `config/ssl` and dumped your certificate/key files there:               *
   ***************************************************************************/

  // ssl: {
  //   ca: require('fs').readFileSync(__dirname + './ssl/my_apps_ssl_gd_bundle.crt'),
  //   key: require('fs').readFileSync(__dirname + './ssl/my_apps_ssl.key'),
  //   cert: require('fs').readFileSync(__dirname + './ssl/my_apps_ssl.crt')
  // },

  /***************************************************************************
   * The `port` setting determines which TCP port your app will be           *
   * deployed on.                                                            *
   *                                                                         *
   * Ports are a transport-layer concept designed to allow many different    *
   * networking applications run at the same time on a single computer.      *
   * More about ports:                                                       *
   * http://en.wikipedia.org/wiki/Port_(computer_networking)                 *
   *                                                                         *
   * By default, if it's set, Sails uses the `PORT` environment variable.    *
   * Otherwise it falls back to port 1337.                                   *
   *                                                                         *
   * In env/production.js, you'll probably want to change this setting       *
   * to 80 (http://) or 443 (https://) if you have an SSL certificate        *
   ***************************************************************************/

  // port: process.env.PORT || 1337,

  /***************************************************************************
   * The runtime "environment" of your Sails app is either typically         *
   * 'development' or 'production'.                                          *
   *                                                                         *
   * In development, your Sails app will go out of its way to help you       *
   * (for instance you will receive more descriptive error and               *
   * debugging output)                                                       *
   *                                                                         *
   * In production, Sails configures itself (and its dependencies) to        *
   * optimize performance. You should always put your app in production mode *
   * before you deploy it to a server.  This helps ensure that your Sails    *
   * app remains stable, performant, and scalable.                           *
   *                                                                         *
   * By default, Sails sets its environment using the `NODE_ENV` environment *
   * variable.  If NODE_ENV is not set, Sails will run in the                *
   * 'development' environment.                                              *
   ***************************************************************************/

    environment: process.env.NODE_ENV || 'development',
    port: process.env.OPENSHIFT_NODEJS_PORT||8080,
    host: process.env.OPENSHIFT_NODEJS_IP||'127.0.0.1',
    explicitHost: process.env.OPENSHIFT_APP_DNS || "localhost",

    hookTimeout: 30000
};
<|endoftext|>


<commit_before>import update from 'immutability-helper'
import { EntityType, FocusMode } from 'model'
import queries from 'queries'
import { actions } from 'actions'

let subscribeDeleting = (events, store) => {

	events.participantDelete_Requested.stream
		.subscribe(e => {
			let state = store.getState();
			let participantId = e.data.participantId;

			if (shouldAllowDeletion(state, participantId)) {
				events.participantDelete_Allowed(e.data.participantId);
			} else {
				events.participantDelete_Rejected(e.data.participantId);
			}
		});

	events.participantDelete_Allowed.stream
		.subscribe(e => store.dispatch(
            actions.setFocus(EntityType.participant, FocusMode.deleted, e.data.participantId, e)
        ));

	events.participantDelete_Rejected.stream
		.subscribe(e => store.dispatch(
            actions.setFocus(EntityType.participant, FocusMode.deleteRejected, e.data.participantId, e)
        ));

    events.participantDelete_Submitted.stream
		.subscribe(e => {
			let state = store.getState();
			let participantId = e.data.participantId;

			if (!shouldAllowDeletion(state, participantId)) {
				events.participantDelete_Rejected(e.data.participantId);
				return;
			}

			let cleanUpParticipationsToAddActions = getCleanUpParticipationsToAddActions(state, participantId, e);
			let cleanUpParticipationEditsActions = getCleanUpParticipationEditsActions(state, participantId, e);

			store.dispatchBatch([
				actions.clearFocus(EntityType.participant, e),
				...cleanUpParticipationsToAddActions,
				...cleanUpParticipationEditsActions,
				actions.clearEdit(EntityType.participant, participantId, e),
				actions.deleteEntity(EntityType.participant, participantId, e)
			], e);

			events.participantDelete_Deleted(e.data.participantId);
		});

	events.participantDelete_Cancelled.stream
		.subscribe(e => store.dispatch(
            actions.clearFocus(EntityType.participant, e)
        ));

	events.participantDelete_Deleted.stream
		.subscribe(e => events.settlementRequested());

};

let shouldAllowDeletion = (state, participantId) => {
	let itemsParticipantIsInvolvedIn = queries.itemsParticipantIsInvolvedIn(state, participantId);

	return itemsParticipantIsInvolvedIn.length === 0;
};

let getCleanUpParticipationsToAddActions = (state, participantId, origin) => {
	let participationsToAddNext = queries.toAdd_next(state, EntityType.participation);
	let result = [];

	if (participationsToAddNext.hasOwnProperty(participantId)) {
		let newToAdd = update(participationsToAddNext, { $unset: [participantId] });

		result.push(
			actions.setNextToAdd(EntityType.participation, newToAdd, origin),
			actions.clearNextToAddError(EntityType.participation, origin)
		);
	}

	return result;
};

let getCleanUpParticipationEditsActions = (state, participantId, origin) => {
	let participationEdits = queries.allEdits(state, EntityType.participation);
	let cleanUpParticipationEditsActions = [];

	Object.keys(participationEdits).forEach(itemId => {
		let participationEdit = participationEdits[itemId].data;

		if (participantId in participationEdit) {
			let newEdit = Object.assign({}, participationEdit);
			delete newEdit[participantId];

			cleanUpParticipationEditsActions.push(
				actions.setEdit(EntityType.participation, itemId, newEdit, origin),
				actions.clearEditError(EntityType.participation, itemId, origin)
			);
		}
	});

	return cleanUpParticipationEditsActions;
};

export default subscribeDeleting;
<commit_msg>Moved UI manipulation after Participant deletion to participantDelete_Deleted event handler<commit_after>import update from 'immutability-helper'
import { EntityType, FocusMode } from 'model'
import queries from 'queries'
import { actions } from 'actions'

let subscribeDeleting = (events, store) => {

	events.participantDelete_Requested.stream
		.subscribe(e => {
			let state = store.getState();
			let participantId = e.data.participantId;

			if (shouldAllowDeletion(state, participantId)) {
				events.participantDelete_Allowed(e.data.participantId);
			} else {
				events.participantDelete_Rejected(e.data.participantId);
			}
		});

	events.participantDelete_Allowed.stream
		.subscribe(e => store.dispatch(
            actions.setFocus(EntityType.participant, FocusMode.deleted, e.data.participantId, e)
        ));

	events.participantDelete_Rejected.stream
		.subscribe(e => store.dispatch(
            actions.setFocus(EntityType.participant, FocusMode.deleteRejected, e.data.participantId, e)
        ));

    events.participantDelete_Submitted.stream
		.subscribe(e => {
			let state = store.getState();
			let participantId = e.data.participantId;

			if (!shouldAllowDeletion(state, participantId)) {
				events.participantDelete_Rejected(e.data.participantId);
				return;
			}

			store.dispatch(actions.deleteEntity(EntityType.participant, participantId, e));

			events.participantDelete_Deleted(e.data.participantId);
		});

	events.participantDelete_Cancelled.stream
		.subscribe(e => store.dispatch(
            actions.clearFocus(EntityType.participant, e)
        ));

	events.participantDelete_Deleted.stream
		.subscribe(e => {
			let state = store.getState();
			let participantId = e.data.participantId;

			let cleanUpParticipationToAddActions = getCleanUpParticipationToAddActions(state, participantId, e);
			let cleanUpParticipationEditsActions = getCleanUpParticipationEditsActions(state, participantId, e);

			store.dispatchBatch([
				actions.clearFocus(EntityType.participant, e),
				...cleanUpParticipationToAddActions,
				...cleanUpParticipationEditsActions,
				actions.clearEdit(EntityType.participant, participantId, e)
			], e);
		});

};

let shouldAllowDeletion = (state, participantId) => {
	let itemsParticipantIsInvolvedIn = queries.itemsParticipantIsInvolvedIn(state, participantId);

	return itemsParticipantIsInvolvedIn.length === 0;
};

let getCleanUpParticipationToAddActions = (state, participantId, origin) => {
	let participationsToAddNext = queries.toAdd_next(state, EntityType.participation);
	let result = [];

	if (participationsToAddNext != null && participationsToAddNext.hasOwnProperty(participantId)) {
		let newToAdd = update(participationsToAddNext, { $unset: [participantId] });

		result.push(
			actions.setNextToAdd(EntityType.participation, newToAdd, origin),
			actions.clearNextToAddError(EntityType.participation, origin)
		);
	}

	return result;
};

let getCleanUpParticipationEditsActions = (state, participantId, origin) => {
	let participationEdits = queries.allEdits(state, EntityType.participation);
	let cleanUpParticipationEditsActions = [];

	Object.keys(participationEdits).forEach(itemId => {
		let participationEdit = participationEdits[itemId].data;

		if (participantId in participationEdit) {
			let newEdit = Object.assign({}, participationEdit);
			delete newEdit[participantId];

			cleanUpParticipationEditsActions.push(
				actions.setEdit(EntityType.participation, itemId, newEdit, origin),
				actions.clearEditError(EntityType.participation, itemId, origin)
			);
		}
	});

	return cleanUpParticipationEditsActions;
};

export default subscribeDeleting;
<|endoftext|>