<commit_before>/*!
 * ${copyright}
 */

// Provides control sap.m.TabStripSelect.
sap.ui.define(['jquery.sap.global', './Popover', './TabStripSelectList', './library', 'sap/ui/core/Control', 'sap/ui/core/EnabledPropagator', 'sap/ui/core/IconPool', 'sap/m/Select', 'sap/m/TabStripItem'],
	function(jQuery, Popover, TabStripSelectList, library, Control, EnabledPropagator, IconPool, Select, TabStripItem) {
		"use strict";

		/**
		 * Constructor for a new TabStripSelect.
		 *
		 * @param {string} [sId] ID for the new control, generated automatically if no ID is given
		 * @param {object} [mSettings] Initial settings for the new control
		 *
		 * @class
		 * The sap.m.TabStripSelect control provides a list of items that allows users to interact with an item.
		 * @extends sap.m.Select
		 *
		 * @author SAP SE
		 * @version ${version}
		 * @since 1.34
		 *
		 * @constructor
		 * @private
		 * @alias sap.m.TabStripSelect
		 * @ui5-metamodel This control will also be described in the UI5 (legacy) design time meta model
		 */
		var TabStripSelect = Select.extend("sap.m.TabStripSelect", /** @lends sap.m.TabStripSelect.prototype */ {
			metadata: {
				library: "sap.m"
			}
		});

		/**
		 * The default CSS class for TabStripSelect.
		 * @type {string}
		 */
		TabStripSelect.CSS_CLASS = 'sapMTSSlt';

		/**
		 * The default CSS class for TabStripItem in context of TabStripSelect.
		 * @type {string}
		 */
		TabStripSelect.CSS_CLASS_INVISIBLE = 'sapMSltInvisible';

		/**
		 * The constant space size (in pixels) between the select button and its popover to be displayed.
		 *
		 * @type {number}
		 */
		TabStripSelect.SPACE_BETWEEN_SELECT_BUTTON_AND_POPOVER = -5;

		TabStripSelect.prototype.init = function() {
			// set the picker type
			this.setPickerType(sap.ui.Device.system.phone ? "Dialog" : "Popover");

			// initialize composites
			this.createPicker(this.getPickerType());

			// selected item on focus
			this._oSelectionOnFocus = null;

			// to detect when the control is in the rendering phase
			this._bRenderingPhase = false;

			// to detect if the focusout event is triggered due a rendering
			this._bFocusoutDueRendering = false;
			// used to prevent the change event from firing when the user scrolls
			// the picker popup (dropdown) list using the mouse
			this._bProcessChange = false;
		};

		/**
		 * Creates the TabStripSelectList instance.
		 *
		 * @override
		 * @private
		 * @returns {*}
		 */
		TabStripSelect.prototype.createList = function() {
			// list to use inside the picker
			this._oList = new TabStripSelectList({
				width: "100%"
			}).attachSelectionChange(this.onSelectionChange, this)
			.addEventDelegate({
				ontap: function(oEvent) {
					this.close();
				}
			}, this);

			return this._oList;
		};

		/**
		 * Proxies the calls of the select methods to the inner TabStripSelectList methods.
		 *
		 * @override
		 * @param {string} sFunctionName The name of the called method
		 * @param {array} aArgs The supplied arguments
		 * @returns {mixed} The result of the called method
		 * @private
		 */
		TabStripSelect.prototype._callMethodInControl = function(sFunctionName, aArgs) {
			var vRes,
			    oList;
			if (aArgs[0] === "items") {
				oList = this.getList();
				if (oList) {
					vRes = TabStripSelectList.prototype[sFunctionName].apply(oList, aArgs);
				}

				// Force the picker control (holding the select list) to re-render as well, in order to have the
				// right position in case the longest item was removed (and picker width is changed)
				if (sFunctionName === 'removeAggregation' && this.isOpen()) {
					this.getPicker().rerender();
				}

			} else {
				vRes = Control.prototype[sFunctionName].apply(this, aArgs);
			}

			return vRes;
		};

		/**
		 * Creates an instance of sap.m.Popover.
		 *
		 * @override
		 * @private
		 * @returns {sap.m.Popover}
		 */
		TabStripSelect.prototype._createPopover = function() {
			var that = this,
			    oPicker = new Popover({
				    showArrow: false,
				    showHeader: false,
				    placement: sap.m.PlacementType.Vertical,
				    offsetX: 0,
				    offsetY: sap.ui.Device.system.phone ? 0 : TabStripSelect.SPACE_BETWEEN_SELECT_BUTTON_AND_POPOVER,
				    initialFocus: this,
				    bounce: false
			    });

			// detect when the scrollbar is pressed
			oPicker.addEventDelegate({
				ontouchstart: function(oEvent) {
					var oPickerDomRef = this.getDomRef("cont");

					if (oEvent.target === oPickerDomRef) {
						that._bProcessChange = false;
					}
				}
			}, oPicker);

			this._decoratePopover(oPicker);
			return oPicker;
		};

		/**
		 * Creates an instance of sap.m.Dialog.
		 *
		 * @override
		 * @private
		 * @returns {sap.m.Dialog}
		 */
		TabStripSelect.prototype._createDialog = function() {
			var CSS_CLASS_PARENT = this.getRenderer().CSS_CLASS;

			// initialize Dialog
			var oDialog = new sap.m.Dialog({
				stretch: true,
				customHeader: new sap.m.Bar({
					contentLeft: new sap.m.InputBase({
						width: "100%",
						editable: false
					})
						.addStyleClass(TabStripSelect.CSS_CLASS + "Input")
						.addStyleClass(CSS_CLASS_PARENT + "Input")
				})
					.addStyleClass(TabStripSelect.CSS_CLASS + "Bar")
					.addStyleClass(CSS_CLASS_PARENT + "Bar")
			});

			oDialog.getAggregation("customHeader").attachBrowserEvent("tap", function() {
				oDialog.close();
			}, this);

			return oDialog;
		};

		/**
		 * This event handler is called after the picker popup is rendered.
		 *
		 * @override
		 * @private
		 */
		TabStripSelect.prototype.onAfterRenderingPicker = function() {
			var iPickerOffsetX,
				bPageRTL = sap.ui.getCore().getConfiguration().getRTL();

			Select.prototype.onAfterRenderingPicker.call(this);
			if (bPageRTL) {
				iPickerOffsetX = this.$().width() - this.getPicker().$().width();
			} else {
				iPickerOffsetX = this.getPicker().$().width() - this.$().width();
			}

			// on phone the picker is a dialog and does not have an offset
			if (this.getPicker() instanceof sap.m.Popover === true) {
				this.getPicker().setOffsetX(-iPickerOffsetX);
				this.getPicker()._calcPlacement(); // needed to apply the new offset after the popup is open
			}
		};

		TabStripSelect.prototype.exit = function() {
			Select.prototype.exit.call(this);
			this._oList.destroy();
			this._oList = null;
		};

		/**
		 * Handles the touchstart event on the TabStripSelect.
		 *
		 * @param {jQuery.Event} oEvent The event object
		 * @private
		 */
		TabStripSelect.prototype.ontouchstart = function(oEvent) {

			// mark the event for components that needs to know if the event was handled
			oEvent.setMarked();

			if (this.getEnabled() && this.isOpenArea(oEvent.target)) {

				// add the active state to the TabStripSelect's field
				this.addStyleClass(this.getRenderer().CSS_CLASS + "Pressed");
			}
		};

		/**
		 * Handles the touchend event on the TabStripSelect.
		 *
		 * @param {jQuery.Event} oEvent The event object
		 * @private
		 */
		TabStripSelect.prototype.ontouchend = function(oEvent) {

			// mark the event for components that needs to know if the event was handled
			oEvent.setMarked();

			if (this.getEnabled() && (!this.isOpen() || !this.hasContent()) && this.isOpenArea(oEvent.target)) {

				// remove the active state of the Select HTMLDIVElement container
				this.removeStyleClass(this.getRenderer().CSS_CLASS + "Pressed");
			}
		};

		/**
		 * This event handler is called before the picker popup is closed.
		 *
		 * @private
		 */
		TabStripSelect.prototype.onBeforeClose = function() {
			var oDomRef = this.getFocusDomRef();

			if (oDomRef) {

				// note: the "aria-owns" attribute is removed when the list is not visible and in view
				oDomRef.removeAttribute("aria-owns");

				// the "aria-activedescendant" attribute is removed when the currently active descendant is not visible
				oDomRef.removeAttribute("aria-activedescendant");
			}

			// remove the active state of the Select's field
			this.removeStyleClass(this.getRenderer().CSS_CLASS + "Pressed");
		};

		/**
		 * This event handler is called after the picker popup is closed.
		 *
		 * @private
		 */
		TabStripSelect.prototype.onAfterClose = function() {
			var oDomRef = this.getFocusDomRef();

			if (oDomRef) {
				oDomRef.setAttribute("aria-expanded", "false");

				// note: the "aria-owns" attribute is removed when the list is not visible and in view
				oDomRef.removeAttribute("aria-owns");
			}
		};

		/**
		 * Overrides the method in order to turn off default selected item.
		 *
		 * @returns {null}
		 */
		TabStripSelect.prototype.getDefaultSelectedItem = function(aItems) {
			// always need to have default item when viewed on phone
			if (sap.ui.Device.system.phone) {
				return Select.prototype.getDefaultSelectedItem.apply(this, arguments);
			}
			return null;
		};

		/**
		 * Overridden, in order to set proper visibility for the modified state of the Select field.
		 *
		 * @override
		 * @param {string} sValue
		 * @private
		 */
		TabStripSelect.prototype.setValue = function(sValue) {
			var $ModifiedDom = this.$().find(".sapMTabStripSelectListItemModified").eq(0);
			Select.prototype.setValue.apply(this, arguments);
			if (this.getSelectedItem().getProperty('modified')) {
				$ModifiedDom.removeClass(TabStripItem.CSS_CLASS_STATE_INVISIBLE);
			} else {
				$ModifiedDom.addClass(TabStripItem.CSS_CLASS_STATE_INVISIBLE);
			}
		};

		/**
		 * Handles the selectionChange event on the list.
		 *
		 * @param {sap.ui.base.Event} oEvent
		 * @private
		 */
		TabStripSelect.prototype.onSelectionChange = function(oEvent) {
			var oItem = oEvent.getParameter("selectedItem");
			if (this.fireChange({selectedItem: oItem})) {
				this.close();
				this.setSelection(oItem);
				this.setValue(this._getSelectedItemText());
			} else {
				oEvent.preventDefault();
			}
		};

		/**
		 * Fire a 'change' event that can be prevented although by default it is not preventable
		 * @param {object} mParameters
		 * @returns {sap.ui.core.support.Support|sap.ui.base.EventProvider|boolean|sap.ui.core.Element|*}
		 */
		TabStripSelect.prototype.fireChange = function(mParameters) {
			this._oSelectionOnFocus = mParameters.selectedItem;
			var bAllowPreventDefault = true;
			return this.fireEvent("change", mParameters, bAllowPreventDefault);
		};

		return TabStripSelect;

}, /* bExport= */ false);

<commit_msg>[INTERNAL] sap.m.TabStripSelect - Correct context is now returned from setValue setter<commit_after>/*!
 * ${copyright}
 */

// Provides control sap.m.TabStripSelect.
sap.ui.define(['jquery.sap.global', './Popover', './TabStripSelectList', './library', 'sap/ui/core/Control', 'sap/ui/core/EnabledPropagator', 'sap/ui/core/IconPool', 'sap/m/Select', 'sap/m/TabStripItem'],
	function(jQuery, Popover, TabStripSelectList, library, Control, EnabledPropagator, IconPool, Select, TabStripItem) {
		"use strict";

		/**
		 * Constructor for a new TabStripSelect.
		 *
		 * @param {string} [sId] ID for the new control, generated automatically if no ID is given
		 * @param {object} [mSettings] Initial settings for the new control
		 *
		 * @class
		 * The sap.m.TabStripSelect control provides a list of items that allows users to interact with an item.
		 * @extends sap.m.Select
		 *
		 * @author SAP SE
		 * @version ${version}
		 * @since 1.34
		 *
		 * @constructor
		 * @private
		 * @alias sap.m.TabStripSelect
		 * @ui5-metamodel This control will also be described in the UI5 (legacy) design time meta model
		 */
		var TabStripSelect = Select.extend("sap.m.TabStripSelect", /** @lends sap.m.TabStripSelect.prototype */ {
			metadata: {
				library: "sap.m"
			}
		});

		/**
		 * The default CSS class for TabStripSelect.
		 * @type {string}
		 */
		TabStripSelect.CSS_CLASS = 'sapMTSSlt';

		/**
		 * The default CSS class for TabStripItem in context of TabStripSelect.
		 * @type {string}
		 */
		TabStripSelect.CSS_CLASS_INVISIBLE = 'sapMSltInvisible';

		/**
		 * The constant space size (in pixels) between the select button and its popover to be displayed.
		 *
		 * @type {number}
		 */
		TabStripSelect.SPACE_BETWEEN_SELECT_BUTTON_AND_POPOVER = -5;

		TabStripSelect.prototype.init = function() {
			// set the picker type
			this.setPickerType(sap.ui.Device.system.phone ? "Dialog" : "Popover");

			// initialize composites
			this.createPicker(this.getPickerType());

			// selected item on focus
			this._oSelectionOnFocus = null;

			// to detect when the control is in the rendering phase
			this._bRenderingPhase = false;

			// to detect if the focusout event is triggered due a rendering
			this._bFocusoutDueRendering = false;
			// used to prevent the change event from firing when the user scrolls
			// the picker popup (dropdown) list using the mouse
			this._bProcessChange = false;
		};

		/**
		 * Creates the TabStripSelectList instance.
		 *
		 * @override
		 * @private
		 * @returns {*}
		 */
		TabStripSelect.prototype.createList = function() {
			// list to use inside the picker
			this._oList = new TabStripSelectList({
				width: "100%"
			}).attachSelectionChange(this.onSelectionChange, this)
			.addEventDelegate({
				ontap: function(oEvent) {
					this.close();
				}
			}, this);

			return this._oList;
		};

		/**
		 * Proxies the calls of the select methods to the inner TabStripSelectList methods.
		 *
		 * @override
		 * @param {string} sFunctionName The name of the called method
		 * @param {array} aArgs The supplied arguments
		 * @returns {mixed} The result of the called method
		 * @private
		 */
		TabStripSelect.prototype._callMethodInControl = function(sFunctionName, aArgs) {
			var vRes,
			    oList;
			if (aArgs[0] === "items") {
				oList = this.getList();
				if (oList) {
					vRes = TabStripSelectList.prototype[sFunctionName].apply(oList, aArgs);
				}

				// Force the picker control (holding the select list) to re-render as well, in order to have the
				// right position in case the longest item was removed (and picker width is changed)
				if (sFunctionName === 'removeAggregation' && this.isOpen()) {
					this.getPicker().rerender();
				}

			} else {
				vRes = Control.prototype[sFunctionName].apply(this, aArgs);
			}

			return vRes;
		};

		/**
		 * Creates an instance of sap.m.Popover.
		 *
		 * @override
		 * @private
		 * @returns {sap.m.Popover}
		 */
		TabStripSelect.prototype._createPopover = function() {
			var that = this,
			    oPicker = new Popover({
				    showArrow: false,
				    showHeader: false,
				    placement: sap.m.PlacementType.Vertical,
				    offsetX: 0,
				    offsetY: sap.ui.Device.system.phone ? 0 : TabStripSelect.SPACE_BETWEEN_SELECT_BUTTON_AND_POPOVER,
				    initialFocus: this,
				    bounce: false
			    });

			// detect when the scrollbar is pressed
			oPicker.addEventDelegate({
				ontouchstart: function(oEvent) {
					var oPickerDomRef = this.getDomRef("cont");

					if (oEvent.target === oPickerDomRef) {
						that._bProcessChange = false;
					}
				}
			}, oPicker);

			this._decoratePopover(oPicker);
			return oPicker;
		};

		/**
		 * Creates an instance of sap.m.Dialog.
		 *
		 * @override
		 * @private
		 * @returns {sap.m.Dialog}
		 */
		TabStripSelect.prototype._createDialog = function() {
			var CSS_CLASS_PARENT = this.getRenderer().CSS_CLASS;

			// initialize Dialog
			var oDialog = new sap.m.Dialog({
				stretch: true,
				customHeader: new sap.m.Bar({
					contentLeft: new sap.m.InputBase({
						width: "100%",
						editable: false
					})
						.addStyleClass(TabStripSelect.CSS_CLASS + "Input")
						.addStyleClass(CSS_CLASS_PARENT + "Input")
				})
					.addStyleClass(TabStripSelect.CSS_CLASS + "Bar")
					.addStyleClass(CSS_CLASS_PARENT + "Bar")
			});

			oDialog.getAggregation("customHeader").attachBrowserEvent("tap", function() {
				oDialog.close();
			}, this);

			return oDialog;
		};

		/**
		 * This event handler is called after the picker popup is rendered.
		 *
		 * @override
		 * @private
		 */
		TabStripSelect.prototype.onAfterRenderingPicker = function() {
			var iPickerOffsetX,
				bPageRTL = sap.ui.getCore().getConfiguration().getRTL();

			Select.prototype.onAfterRenderingPicker.call(this);
			if (bPageRTL) {
				iPickerOffsetX = this.$().width() - this.getPicker().$().width();
			} else {
				iPickerOffsetX = this.getPicker().$().width() - this.$().width();
			}

			// on phone the picker is a dialog and does not have an offset
			if (this.getPicker() instanceof sap.m.Popover === true) {
				this.getPicker().setOffsetX(-iPickerOffsetX);
				this.getPicker()._calcPlacement(); // needed to apply the new offset after the popup is open
			}
		};

		TabStripSelect.prototype.exit = function() {
			Select.prototype.exit.call(this);
			this._oList.destroy();
			this._oList = null;
		};

		/**
		 * Handles the touchstart event on the TabStripSelect.
		 *
		 * @param {jQuery.Event} oEvent The event object
		 * @private
		 */
		TabStripSelect.prototype.ontouchstart = function(oEvent) {

			// mark the event for components that needs to know if the event was handled
			oEvent.setMarked();

			if (this.getEnabled() && this.isOpenArea(oEvent.target)) {

				// add the active state to the TabStripSelect's field
				this.addStyleClass(this.getRenderer().CSS_CLASS + "Pressed");
			}
		};

		/**
		 * Handles the touchend event on the TabStripSelect.
		 *
		 * @param {jQuery.Event} oEvent The event object
		 * @private
		 */
		TabStripSelect.prototype.ontouchend = function(oEvent) {

			// mark the event for components that needs to know if the event was handled
			oEvent.setMarked();

			if (this.getEnabled() && (!this.isOpen() || !this.hasContent()) && this.isOpenArea(oEvent.target)) {

				// remove the active state of the Select HTMLDIVElement container
				this.removeStyleClass(this.getRenderer().CSS_CLASS + "Pressed");
			}
		};

		/**
		 * This event handler is called before the picker popup is closed.
		 *
		 * @private
		 */
		TabStripSelect.prototype.onBeforeClose = function() {
			var oDomRef = this.getFocusDomRef();

			if (oDomRef) {

				// note: the "aria-owns" attribute is removed when the list is not visible and in view
				oDomRef.removeAttribute("aria-owns");

				// the "aria-activedescendant" attribute is removed when the currently active descendant is not visible
				oDomRef.removeAttribute("aria-activedescendant");
			}

			// remove the active state of the Select's field
			this.removeStyleClass(this.getRenderer().CSS_CLASS + "Pressed");
		};

		/**
		 * This event handler is called after the picker popup is closed.
		 *
		 * @private
		 */
		TabStripSelect.prototype.onAfterClose = function() {
			var oDomRef = this.getFocusDomRef();

			if (oDomRef) {
				oDomRef.setAttribute("aria-expanded", "false");

				// note: the "aria-owns" attribute is removed when the list is not visible and in view
				oDomRef.removeAttribute("aria-owns");
			}
		};

		/**
		 * Overrides the method in order to turn off default selected item.
		 *
		 * @returns {null}
		 */
		TabStripSelect.prototype.getDefaultSelectedItem = function(aItems) {
			// always need to have default item when viewed on phone
			if (sap.ui.Device.system.phone) {
				return Select.prototype.getDefaultSelectedItem.apply(this, arguments);
			}
			return null;
		};

		/**
		 * Overridden, in order to set proper visibility for the modified state of the Select field.
		 *
		 * @override
		 * @param {string} sValue
		 * @return {sap.m.TabStripSelect} this for chaining
		 * @private
		 */
		TabStripSelect.prototype.setValue = function(sValue) {
			var $ModifiedDom = this.$().find(".sapMTabStripSelectListItemModified").eq(0),
				oSelectedItem;

			Select.prototype.setValue.apply(this, arguments);
			oSelectedItem = this.getSelectedItem();
			if (oSelectedItem && oSelectedItem.getProperty('modified')) {
				$ModifiedDom.removeClass(TabStripItem.CSS_CLASS_STATE_INVISIBLE);
			} else {
				$ModifiedDom.addClass(TabStripItem.CSS_CLASS_STATE_INVISIBLE);
			}
			return this;
		};

		/**
		 * Handles the selectionChange event on the list.
		 *
		 * @param {sap.ui.base.Event} oEvent
		 * @private
		 */
		TabStripSelect.prototype.onSelectionChange = function(oEvent) {
			var oItem = oEvent.getParameter("selectedItem");
			if (this.fireChange({selectedItem: oItem})) {
				this.close();
				this.setSelection(oItem);
				this.setValue(this._getSelectedItemText());
			} else {
				oEvent.preventDefault();
			}
		};

		/**
		 * Fire a 'change' event that can be prevented although by default it is not preventable
		 * @param {object} mParameters
		 * @returns {sap.ui.core.support.Support|sap.ui.base.EventProvider|boolean|sap.ui.core.Element|*}
		 */
		TabStripSelect.prototype.fireChange = function(mParameters) {
			this._oSelectionOnFocus = mParameters.selectedItem;
			var bAllowPreventDefault = true;
			return this.fireEvent("change", mParameters, bAllowPreventDefault);
		};

		return TabStripSelect;

}, /* bExport= */ false);

<|endoftext|>


<commit_before>/*jshint -W004 */
var $db = require(global.cwd + '/lib/mongo'),
    debug = require('debug')('wol:leaderboard'),
    Arpad = require('arpad'),
    $q = require('q'),
    DEFAULT_POINTS = 1000;

module.exports = function singles(game, match, packets) {
    debug('game: %s, idno: %d is singles', game, match.idno);

    /* stop if <> 1v1 */
    if (match.players.length != 2) return;
    packets = packets || [];

    /* find winner and loser */
    var winner = -1,
        loser = -1;

    match.players.forEach(function (player, index) {
        if (player.won > 0) winner = index;
        if (player.loss > 0) loser = index;
    });

    // lower case packet names for further comparison
    packets.forEach(function(packet) {
       if (packet.client) {
           packet.client.nick = (packet.client.nick || '').toLowerCase();
       }
    });

    /* D/C Scenario: 1 packet, no clear winner */
    if (packets[0] && !packets[1] && (winner < 0 || loser < 0)) {
        match.players.forEach(function(player, index) {
            loser = index;
            player.discon = 1;
            player.loss = 1;
            player.won = 0;

            /* assume uploader won */
            if (player.name == packets[0].client.nick) {
                winner = index;
                player.discon = 0;
                player.won = 1;
                player.loss = 0;
            }
        });
    }

    /* D/C Scenario: no clear winner, check if pils or para exists */
    if (packets.length > 1 && (winner < 0 || loser < 0)) {
        if (packets[0].client.pils >= 0 && packets[1].client.pils >= 0) {
            match.players.forEach(function(player, index) {
                loser = index;
                player.discon = 1;
                player.loss = 1;
                player.won = 0;

                /* higher pils means you lost connection */
                if (player.name == packets[0].client.nick) {
                    /* player attempted abort */
                    if (packets[0].client.para > 0) return;

                    if (packets[0].client.pils < packets[1].client.pils) {
                        winner = index;
                        player.discon = 0;
                        player.won = 1;
                        player.loss = 0;
                    }
                } else if (player.name == packets[1].client.nick) {
                    /* player attempted abort */
                    if (packets[1].client.para > 0) return;

                    if (packets[1].client.pils < packets[0].client.pils) {
                        winner = index;
                        player.discon = 0;
                        player.won = 1;
                        player.loss = 0;
                    }
                }
            });
        }
    }

    // @TODO: RA Draw Scenario: check if game is RA and both packets.cmpl are 64

    var elo = new Arpad(),
        $players = $db.get(game + '_players');

    /* get points for players */
    points(game, match.players).then(function (players) {
        /* reassign modified players */
        match.players = players;

        /* query to update match obj */
        var update = {$set: {}};

        /* note the type of match */
        update.$set.type = 'singles';

        /* determine if game is out of sync */
        packets.forEach(function(packet) {
            if (packet.client.oosy) {
                update.$set.oosy = 1;
                winner = -1;
                loser = -1;
            }
        });

        /* if packet completion status doesn't match up, consider it oos */
        if (packets.length > 1) {
            var player1 = (packets[0].players[0].cmp == packets[1].players[0].cmp);
            var player2 = (packets[0].players[1].cmp == packets[1].players[1].cmp);
            if (!player1 || !player2) {
                update.$set.oosy = 1;
                winner = -1;
                loser = -1;
            }
        }

        match.players.forEach(function (player, index) {
            /* increase out of sync stats for player */
            if (update.$set.oosy) {
                player.oos = 1;
                player.won = 0;
                player.loss = 0;
                player.discon = 0;
            }

            /* query to update player obj */
            var _player = {
                $push: {games: match.idno},
                $inc: {
                    wins: player.won || 0,
                    losses: player.loss || 0,
                    disconnects: player.discon || 0,
                    oos: player.oos || 0
                },
                $set: {
                    points: player.points || DEFAULT_POINTS
                }
            };

            /* calculate points if winner and loser */
            if (winner >= 0 && loser >= 0) {
                var opponent = match.players[loser];
                var method = 'newRatingIfWon';

                if (player.loss > 0) {
                    opponent = match.players[winner];
                    method = 'newRatingIfLost';
                }

                /* calculate new point value */
                player.exp = elo[method](player.points, opponent.points);
            }

            /* if we only have losers, deduct from both players */
            else if (winner < 0 && loser >= 0) {
                /* deduct 0.7% percent of points (higher points, d/c hits harder) */
                player.exp = player.points - Math.floor((0.7 / 100) * player.points);
            }

            /* if we have points, update the game and player records */
            if (player.exp) {
                /* update _player points */
                _player.$set = {points: player.exp};

                /* update player, experience gained/loss in match object */
                var str = ['players', index, 'exp'].join('.');
                update.$set[str] = Math.abs(player.points - player.exp);
            }

            /* update win/loss/discon in game object just in case it's changed */
            var str = ['players', index].join('.');
            update.$set[str + '.won'] = player.won;
            update.$set[str + '.loss'] = player.loss;
            update.$set[str + '.discon'] = player.discon;

            /* update or create player */
            $players.update({name: player.name}, _player, {upsert: true}).error(function(err) {
                console.log('ranking/singles player update error');
                console.log('game: %s, match: %d, player: %s', game, match.idno, player.name);
                console.dir(_player);
                console.dir(err);
            });
        });

        /* update match object */
        $db.get(game + '_games').update({idno: match.idno}, update).error(function(err) {
            console.log('ranking/singles game update error');
            console.log('game: %s, match: %d', game, match.idno);
            console.dir(update);
            console.dir(err);
        });
    });
};

function points(game, players) {
    var deferred = $q.defer();

    var search = [];
    players.forEach(function (player) {
        search.push(player.name);
    });

    $db.get(game + '_players').find({name: {$in: search}}, function (err, data) {
        players.forEach(function (player, key) {

            players[key].points = DEFAULT_POINTS;

            data.forEach(function (row) {
                if (row.points && player.name == row.name) {
                    players[key].points = row.points;
                }
            });
        });

        deferred.resolve(players);
    });

    return deferred.promise;
}
<commit_msg>store the player's last activity<commit_after>/*jshint -W004 */
var $db = require(global.cwd + '/lib/mongo'),
    debug = require('debug')('wol:leaderboard'),
    Arpad = require('arpad'),
    $q = require('q'),
    DEFAULT_POINTS = 1000;

module.exports = function singles(game, match, packets) {
    debug('game: %s, idno: %d is singles', game, match.idno);

    /* stop if <> 1v1 */
    if (match.players.length != 2) return;
    packets = packets || [];

    /* find winner and loser */
    var winner = -1,
        loser = -1;

    match.players.forEach(function (player, index) {
        if (player.won > 0) winner = index;
        if (player.loss > 0) loser = index;
    });

    // lower case packet names for further comparison
    packets.forEach(function(packet) {
       if (packet.client) {
           packet.client.nick = (packet.client.nick || '').toLowerCase();
       }
    });

    /* D/C Scenario: 1 packet, no clear winner */
    if (packets[0] && !packets[1] && (winner < 0 || loser < 0)) {
        match.players.forEach(function(player, index) {
            loser = index;
            player.discon = 1;
            player.loss = 1;
            player.won = 0;

            /* assume uploader won */
            if (player.name == packets[0].client.nick) {
                winner = index;
                player.discon = 0;
                player.won = 1;
                player.loss = 0;
            }
        });
    }

    /* D/C Scenario: no clear winner, check if pils or para exists */
    if (packets.length > 1 && (winner < 0 || loser < 0)) {
        if (packets[0].client.pils >= 0 && packets[1].client.pils >= 0) {
            match.players.forEach(function(player, index) {
                loser = index;
                player.discon = 1;
                player.loss = 1;
                player.won = 0;

                /* higher pils means you lost connection */
                if (player.name == packets[0].client.nick) {
                    /* player attempted abort */
                    if (packets[0].client.para > 0) return;

                    if (packets[0].client.pils < packets[1].client.pils) {
                        winner = index;
                        player.discon = 0;
                        player.won = 1;
                        player.loss = 0;
                    }
                } else if (player.name == packets[1].client.nick) {
                    /* player attempted abort */
                    if (packets[1].client.para > 0) return;

                    if (packets[1].client.pils < packets[0].client.pils) {
                        winner = index;
                        player.discon = 0;
                        player.won = 1;
                        player.loss = 0;
                    }
                }
            });
        }
    }

    // @TODO: RA Draw Scenario: check if game is RA and both packets.cmpl are 64

    var elo = new Arpad(),
        $players = $db.get(game + '_players');

    /* get points for players */
    points(game, match.players).then(function (players) {
        /* reassign modified players */
        match.players = players;

        /* query to update match obj */
        var update = {$set: {}};

        /* note the type of match */
        update.$set.type = 'singles';

        /* determine if game is out of sync */
        packets.forEach(function(packet) {
            if (packet.client.oosy) {
                update.$set.oosy = 1;
                winner = -1;
                loser = -1;
            }
        });

        /* if packet completion status doesn't match up, consider it oos */
        if (packets.length > 1) {
            var player1 = (packets[0].players[0].cmp == packets[1].players[0].cmp);
            var player2 = (packets[0].players[1].cmp == packets[1].players[1].cmp);
            if (!player1 || !player2) {
                update.$set.oosy = 1;
                winner = -1;
                loser = -1;
            }
        }

        match.players.forEach(function (player, index) {
            /* increase out of sync stats for player */
            if (update.$set.oosy) {
                player.oos = 1;
                player.won = 0;
                player.loss = 0;
                player.discon = 0;
            }

            /* query to update player obj */
            var _player = {
                $push: {games: match.idno},
                $inc: {
                    wins: player.won || 0,
                    losses: player.loss || 0,
                    disconnects: player.discon || 0,
                    oos: player.oos || 0
                },
                $set: {
                    points: player.points || DEFAULT_POINTS,
                    activity: Math.floor(Date.now() / 1000)
                }
            };

            /* calculate points if winner and loser */
            if (winner >= 0 && loser >= 0) {
                var opponent = match.players[loser];
                var method = 'newRatingIfWon';

                if (player.loss > 0) {
                    opponent = match.players[winner];
                    method = 'newRatingIfLost';
                }

                /* calculate new point value */
                player.exp = elo[method](player.points, opponent.points);
            }

            /* if we only have losers, deduct from both players */
            else if (winner < 0 && loser >= 0) {
                /* deduct 0.7% percent of points (higher points, d/c hits harder) */
                player.exp = player.points - Math.floor((0.7 / 100) * player.points);
            }

            /* if we have points, update the game and player records */
            if (player.exp) {
                /* update _player points */
                _player.$set = {points: player.exp};

                /* update player, experience gained/loss in match object */
                var str = ['players', index, 'exp'].join('.');
                update.$set[str] = Math.abs(player.points - player.exp);
            }

            /* update win/loss/discon in game object just in case it's changed */
            var str = ['players', index].join('.');
            update.$set[str + '.won'] = player.won;
            update.$set[str + '.loss'] = player.loss;
            update.$set[str + '.discon'] = player.discon;

            /* update or create player */
            $players.update({name: player.name}, _player, {upsert: true}).error(function(err) {
                console.log('ranking/singles player update error');
                console.log('game: %s, match: %d, player: %s', game, match.idno, player.name);
                console.dir(_player);
                console.dir(err);
            });
        });

        /* update match object */
        $db.get(game + '_games').update({idno: match.idno}, update).error(function(err) {
            console.log('ranking/singles game update error');
            console.log('game: %s, match: %d', game, match.idno);
            console.dir(update);
            console.dir(err);
        });
    });
};

function points(game, players) {
    var deferred = $q.defer();

    var search = [];
    players.forEach(function (player) {
        search.push(player.name);
    });

    $db.get(game + '_players').find({name: {$in: search}}, function (err, data) {
        players.forEach(function (player, key) {

            players[key].points = DEFAULT_POINTS;

            data.forEach(function (row) {
                if (row.points && player.name == row.name) {
                    players[key].points = row.points;
                }
            });
        });

        deferred.resolve(players);
    });

    return deferred.promise;
}
<|endoftext|>


<commit_before>/** @jsx React.DOM */
'use strict';
var React = require('react');
var _ = require('underscore');
var globals = require('./globals');
var $script = require('scriptjs');

// The JsonGraph object helps build JSON graph objects. Create a new object
// with the constructor, then add edges and nodes with the methods.
// Uses a variation of the JSON Graph structure described in:
// http://rtsys.informatik.uni-kiel.de/confluence/display/KIELER/JSON+Graph+Format
// The variation is that this one doesn't use 'width' and 'height' in the node, and it
// adds 'cssClass' and 'type' to the node.

// Constructor for a graph architecture
function JsonGraph(id) {
    this.id = id;
    this.type = '';
    this.children = [];
    this.edges = [];
    this.cssClass = '';
}

// Add node to the graph architecture
// id: uniquely identify the node
// label: text to display in the node
// cssClass: optional CSS class to assign to the SVG object for this node
// type: Optional text type to track the type of node this is
JsonGraph.prototype.addNode = function(id, label, cssClass, type) {
    var newNode = {};
    newNode.id = id;
    newNode.type = type;
    newNode.labels = [];
    newNode.labels[0] = {};
    newNode.labels[0].text = label;
    newNode.cssClass = cssClass;
    this.children.push(newNode);
};

// Add edge to the graph architecture
// source: ID of node for the edge to originate; corresponds to 'id' parm of addNode
// target: ID of node for the edge to terminate
JsonGraph.prototype.addEdge = function(source, target) {
    var newEdge = {};
    newEdge.id = '';
    newEdge.source = source;
    newEdge.target = target;
    this.edges.push(newEdge);
};

// Return the JSON graph node matching the given ID
JsonGraph.prototype.getNode = function(id) {
    var node;

    return _(this.children).find(function(child) {
        return child.id === id;
    });
};

module.exports.JsonGraph = JsonGraph;

var Graph = module.exports.Graph = React.createClass({
    getInitialState: function() {
        return {
            leftScrollDisabled: true,
            rightScrollDisabled: true
        };
    },

    // Take a JsonGraph object and convert it to an SVG graph with the Dagre-D3 library.
    // jsonGraph: JsonGraph object containing nodes and edges.
    // graph: Initialized empty Dagre-D3 graph.
    convertGraph: function(jsonGraph, graph) {
        // Convert the nodes
        jsonGraph.children.forEach(function(node) {
            graph.setNode(node.id, {label: node.labels[0].text, rx: 4, ry: 4, class: node.cssClass});
        });

        // Convert the edges
        jsonGraph.edges.forEach(function(edge) {
            graph.setEdge(edge.source, edge.target, {lineInterpolate: 'basis'});
        });
    },

    // Draw the graph on initial draw as well as on state changes.
    // An <svg> element to draw into must already exist in the HTML element in the el parm.
    drawGraph: function(el) {
        var d3 = require('d3');
        var dagreD3 = require('dagre-d3');
        var svg = d3.select(el).select('svg');

        // Create a new empty graph
        var g = new dagreD3.graphlib.Graph()
            .setGraph({rankdir: "TB"})
            .setDefaultEdgeLabel(function() { return {}; });

        // Convert from given node architecture to the dagre nodes and edges
        this.convertGraph(this.props.graph, g);

        // Run the renderer. This is what draws the final graph.
        var render = new dagreD3.render();
        render(d3.select("svg g"), g);

        // Dagre-D3 has a width and height for the graph.
        // Set the viewbox's and viewport's width and height to that plus a little extra
        var width = g.graph().width;
        var height = g.graph().height;
        svg.attr("width", width + 40).attr("height", height + 40)
            .attr("viewBox", "-20 -20 " + (width + 40) + " " + (height + 40));
    },

    componentDidMount: function () {
        globals.bindEvent(window, 'resize', this.handleResize);

        // Delay loading dagre for Jest testing compatibility;
        // Both D3 and Jest have their own conflicting JSDOM instances
        $script('dagre', function() {
            var d3 = require('d3');
            var dagreD3 = require('dagre-d3');
            var el = this.refs.graphdisplay.getDOMNode();

            // Add SVG element to the graph component, and assign it classes, sizes, and a group
            var svg = d3.select(el).insert('svg', '#scroll-buttons')
                .attr('id', 'graphsvg')
                .attr('preserveAspectRatio', 'xMidYMid');
            var svgGroup = svg.append("g");

            // Draw the graph into the panel
            this.drawGraph(el);

            // Add click event listeners to each node rendering. Node's ID is its ENCODE object ID
            var reactThis = this;
            svg.selectAll("g.node").each(function(nodeId) {
                globals.bindEvent(this, 'click', function(e) {
                    reactThis.props.nodeClickHandler(e, nodeId);
                });
            });
            this.dagreLoaded = true;
        }.bind(this));
    },

    // State change; redraw the graph
    componentDidUpdate: function() {
        if (this.dagreLoaded) {
            var el = this.refs.graphdisplay.getDOMNode();
            this.drawGraph(el);
        }
    },

    handleResize: function() {
        var container = this.refs.graphdisplay.getDOMNode();
        var svg = container.getElementById('graphsvg');
        if (container.clientWidth < svg.scrollWidth) {
            console.log('scroll needed');
        }
    },

    componentWillUnmount: function() {
        globals.unbindEvent(window, 'resize', this.handleResize);
    },

    scrollLeftStart: function() {
        var displayNode = this.refs.graphdisplay.getDOMNode();
        displayNode.scrollLeft = displayNode.scrollLeft + 30;
        this.scrollTimer = setTimeout(this.scrollLeftStart, 100);
    },

    scrollRightStart: function() {
        var displayNode = this.refs.graphdisplay.getDOMNode();
        displayNode.scrollLeft = displayNode.scrollLeft - 30;
        this.scrollTimer = setTimeout(this.scrollRightStart, 100);
    },

    scrollStop: function() {
        clearTimeout(this.scrollTimer);
    },

    scrollHandler: function(e) {
        var container = this.refs.graphdisplay.getDOMNode();
        var svg = document.getElementById('graphsvg');
        console.log(container.scrollLeft);
    },

    render: function() {
        console.log('render graph');
        return (

                    <button onMouseDown={this.scrollLeftStart} onMouseUp={this.scrollStop} disabled={this.state.leftScrollDisabled}>Left</button>
                    <button onMouseDown={this.scrollRightStart} onMouseUp={this.scrollStop} disabled={this.state.rightScrollDisabled}>Right</button>

                {this.props.children}

        );
    }
});
<commit_msg>Completed initial implementation of scroll buttons<commit_after>/** @jsx React.DOM */
'use strict';
var React = require('react');
var _ = require('underscore');
var globals = require('./globals');
var $script = require('scriptjs');

// The JsonGraph object helps build JSON graph objects. Create a new object
// with the constructor, then add edges and nodes with the methods.
// Uses a variation of the JSON Graph structure described in:
// http://rtsys.informatik.uni-kiel.de/confluence/display/KIELER/JSON+Graph+Format
// The variation is that this one doesn't use 'width' and 'height' in the node, and it
// adds 'cssClass' and 'type' to the node.

// Constructor for a graph architecture
function JsonGraph(id) {
    this.id = id;
    this.type = '';
    this.children = [];
    this.edges = [];
    this.cssClass = '';
}

// Add node to the graph architecture
// id: uniquely identify the node
// label: text to display in the node
// cssClass: optional CSS class to assign to the SVG object for this node
// type: Optional text type to track the type of node this is
JsonGraph.prototype.addNode = function(id, label, cssClass, type) {
    var newNode = {};
    newNode.id = id;
    newNode.type = type;
    newNode.labels = [];
    newNode.labels[0] = {};
    newNode.labels[0].text = label;
    newNode.cssClass = cssClass;
    this.children.push(newNode);
};

// Add edge to the graph architecture
// source: ID of node for the edge to originate; corresponds to 'id' parm of addNode
// target: ID of node for the edge to terminate
JsonGraph.prototype.addEdge = function(source, target) {
    var newEdge = {};
    newEdge.id = '';
    newEdge.source = source;
    newEdge.target = target;
    this.edges.push(newEdge);
};

// Return the JSON graph node matching the given ID
JsonGraph.prototype.getNode = function(id) {
    var node;

    return _(this.children).find(function(child) {
        return child.id === id;
    });
};

module.exports.JsonGraph = JsonGraph;

var Graph = module.exports.Graph = React.createClass({
    getInitialState: function() {
        return {
            leftScrollDisabled: true,
            rightScrollDisabled: true
        };
    },

    // Take a JsonGraph object and convert it to an SVG graph with the Dagre-D3 library.
    // jsonGraph: JsonGraph object containing nodes and edges.
    // graph: Initialized empty Dagre-D3 graph.
    convertGraph: function(jsonGraph, graph) {
        // Convert the nodes
        jsonGraph.children.forEach(function(node) {
            graph.setNode(node.id, {label: node.labels[0].text, rx: 4, ry: 4, class: node.cssClass});
        });

        // Convert the edges
        jsonGraph.edges.forEach(function(edge) {
            graph.setEdge(edge.source, edge.target, {lineInterpolate: 'basis'});
        });
    },

    // Draw the graph on initial draw as well as on state changes.
    // An <svg> element to draw into must already exist in the HTML element in the el parm.
    drawGraph: function(el) {
        var d3 = require('d3');
        var dagreD3 = require('dagre-d3');
        var svg = d3.select(el).select('svg');

        // Create a new empty graph
        var g = new dagreD3.graphlib.Graph()
            .setGraph({rankdir: "TB"})
            .setDefaultEdgeLabel(function() { return {}; });

        // Convert from given node architecture to the dagre nodes and edges
        this.convertGraph(this.props.graph, g);

        // Run the renderer. This is what draws the final graph.
        var render = new dagreD3.render();
        render(d3.select("svg g"), g);

        // Dagre-D3 has a width and height for the graph.
        // Set the viewbox's and viewport's width and height to that plus a little extra
        var width = g.graph().width;
        var height = g.graph().height;
        svg.attr("width", width + 40).attr("height", height + 40)
            .attr("viewBox", "-20 -20 " + (width + 40) + " " + (height + 40));
    },

    componentDidMount: function () {
        globals.bindEvent(window, 'resize', this.handleResize);

        // Delay loading dagre for Jest testing compatibility;
        // Both D3 and Jest have their own conflicting JSDOM instances
        $script('dagre', function() {
            var d3 = require('d3');
            var dagreD3 = require('dagre-d3');
            var el = this.refs.graphdisplay.getDOMNode();

            // Add SVG element to the graph component, and assign it classes, sizes, and a group
            var svg = d3.select(el).insert('svg', '#scroll-buttons')
                .attr('id', 'graphsvg')
                .attr('preserveAspectRatio', 'xMidYMid');
            var svgGroup = svg.append("g");

            // Draw the graph into the panel
            this.drawGraph(el);

            // Add click event listeners to each node rendering. Node's ID is its ENCODE object ID
            var reactThis = this;
            svg.selectAll("g.node").each(function(nodeId) {
                globals.bindEvent(this, 'click', function(e) {
                    reactThis.props.nodeClickHandler(e, nodeId);
                });
            });
            this.dagreLoaded = true;
        }.bind(this));
    },

    // State change; redraw the graph
    componentDidUpdate: function() {
        if (this.dagreLoaded) {
            var el = this.refs.graphdisplay.getDOMNode();
            this.drawGraph(el);
        }
    },

    handleResize: function() {
        var container = this.refs.graphdisplay.getDOMNode();
        var svg = container.getElementById('graphsvg');
        if (container.clientWidth < svg.scrollWidth) {
            console.log('scroll needed');
        }
    },

    componentWillUnmount: function() {
        globals.unbindEvent(window, 'resize', this.handleResize);
    },

    scrollLeftStart: function() {
        this.scrollTimer = null;
        var displayNode = this.refs.graphdisplay.getDOMNode();
        var newScrollLeft = displayNode.scrollLeft - 30;
        if (newScrollLeft > 0) {
            displayNode.scrollLeft = newScrollLeft;
            this.scrollTimer = setTimeout(this.scrollLeftStart, 100);
        } else {
            displayNode.scrollLeft = 0;
        }
        console.log('scrollLeftStart');
    },

    scrollRightStart: function() {
        this.scrollTimer = null;
        var displayNode = this.refs.graphdisplay.getDOMNode();
        var newScrollLeft = displayNode.scrollLeft + 30;
        var svg = document.getElementById('graphsvg');
        var widthDiff = svg.scrollWidth - displayNode.clientWidth;
        if (newScrollLeft < widthDiff) {
            displayNode.scrollLeft = newScrollLeft;
            this.scrollTimer = setTimeout(this.scrollRightStart, 100);
        } else {
            displayNode.scrollLeft = widthDiff;
        }
        console.log('scrollRightStart');
    },

    scrollStop: function() {
        if (this.scrollTimer) {
            clearTimeout(this.scrollTimer);
            this.scrollTimer = null;
            this.scrollHandler();
        }
        console.log('scrollStop');
    },

    scrollHandler: function() {
        if (!this.scrollTimer) {
            var container = this.refs.graphdisplay.getDOMNode();
            var svg = document.getElementById('graphsvg');
            var containerWidth = container.clientWidth;
            var svgWidth = svg.scrollWidth;
            if (containerWidth < svgWidth) {
                var leftScrollDisabled = container.scrollLeft === 0;
                var rightScrollDisabled = container.scrollLeft >= svgWidth - containerWidth;
                if (this.state.rightScrollDisabled !== rightScrollDisabled) {
                    this.setState({rightScrollDisabled: rightScrollDisabled});
                }
                if (this.state.leftScrollDisabled !== leftScrollDisabled) {
                    this.setState({leftScrollDisabled: leftScrollDisabled});
                }
            }
        }
    },

    render: function() {
        console.log('render graph');
        return (

                    <button onMouseDown={this.scrollLeftStart} onMouseUp={this.scrollStop} disabled={this.state.leftScrollDisabled}>Left</button>
                    <button onMouseDown={this.scrollRightStart} onMouseUp={this.scrollStop} disabled={this.state.rightScrollDisabled}>Right</button>

                {this.props.children}

        );
    }
});
<|endoftext|>


<commit_before>import {
  cmsData
  ,abeExtend
} from '../../cli'

var route = function(req, res, next){
  abeExtend.hooks.instance.trigger('beforeRoute', req, res, next)
  if(typeof res._header !== 'undefined' && res._header !== null) return

  var sourceString = req.body.sourceString
  var prefillQuantity = req.body.prefillQuantity
  var folder = req.body.folder
  var key = req.body.key
  var jsonPage = (req.body.json) ? JSON.parse(JSON.stringify(req.body.json)) : {}

  jsonPage[key] = null

  var request = `{{abe type="data" key="${key}" source="${sourceString}" prefill="true" prefill-quantity='${prefillQuantity}' editable="true"}}`
  var obj = cmsData.attributes.getAll(request, jsonPage)

  cmsData.source.requestList(obj, folder, request, jsonPage)
    .then(() => {
      return res.send(jsonPage[key])
    })
}

export default route<commit_msg>bug: missing res.sent mine type<commit_after>import {
  cmsData
  ,abeExtend
} from '../../cli'

var route = function(req, res, next){
  abeExtend.hooks.instance.trigger('beforeRoute', req, res, next)
  if(typeof res._header !== 'undefined' && res._header !== null) return

  var sourceString = req.body.sourceString
  var prefillQuantity = req.body.prefillQuantity
  var folder = req.body.folder
  var key = req.body.key
  var jsonPage = (req.body.json) ? JSON.parse(JSON.stringify(req.body.json)) : {}

  jsonPage[key] = null

  var request = `{{abe type="data" key="${key}" source="${sourceString}" prefill="true" prefill-quantity='${prefillQuantity}' editable="true"}}`
  var obj = cmsData.attributes.getAll(request, jsonPage)

  cmsData.source.requestList(obj, folder, request, jsonPage)
    .then(() => {

      res.set('Content-Type', 'application/json')
      res.send(JSON.stringify(jsonPage[key]))
    })
}

export default route<|endoftext|>


<commit_before>// data
import { ARTIFICIAL_DELAY } from 'data/app.settings';

// state
import * as api_constants from 'state/redux-json-api.constants';
import * as user_constants from 'state/user/user.constants';

// local
import * as app_constants from '../app.constants';

// ---------------------------
// public
// ---------------------------

export function app (state = app_constants.DEFAULT_STATE, action) {

    switch (action.type) {

        case app_constants.ACTION_TOGGLE_ARTIFICIAL_DELAY:
            if (state.artificial_delay > 0) {
                return Object.assign({}, state, { artificial_delay: 0 });
            }
            return Object.assign({}, state, { artificial_delay: ARTIFICIAL_DELAY });

        case app_constants.ACTION_TOGGLE_MESSAGE_MINIMAL:
            return Object.assign({}, state, { is_message_minimal: state.is_message_minimal ? false : true });

        case api_constants.API_CREATE_FAILED:
        case api_constants.API_CREATED:
        case api_constants.API_DELETE_FAILED:
        case api_constants.API_DELETED:
        case api_constants.API_READ_FAILED:
        case api_constants.API_READ:
        case api_constants.API_UPDATE_FAILED:
        case api_constants.API_UPDATED:
        case user_constants.ACTION_USER_LOGGED_OUT:
            return Object.assign({}, state, { is_disabled: false });

        // case api_constants.API_WILL_CREATE:
        case api_constants.API_WILL_DELETE:
        case api_constants.API_WILL_READ:
        // case api_constants.API_WILL_UPDATE:
        case user_constants.ACTION_WILL_LOGOUT_USER:
            return Object.assign({}, state, { is_disabled: true });

        default:
            return state;
    }
}
<commit_msg>Re-add create HTTP call to those that disable app, to avoid duplicates created by editing new records before they have been successfuly created.<commit_after>// data
import { ARTIFICIAL_DELAY } from 'data/app.settings';

// state
import * as api_constants from 'state/redux-json-api.constants';
import * as user_constants from 'state/user/user.constants';

// local
import * as app_constants from '../app.constants';

// ---------------------------
// public
// ---------------------------

export function app (state = app_constants.DEFAULT_STATE, action) {

    switch (action.type) {

        case app_constants.ACTION_TOGGLE_ARTIFICIAL_DELAY:
            if (state.artificial_delay > 0) {
                return Object.assign({}, state, { artificial_delay: 0 });
            }
            return Object.assign({}, state, { artificial_delay: ARTIFICIAL_DELAY });

        case app_constants.ACTION_TOGGLE_MESSAGE_MINIMAL:
            return Object.assign({}, state, { is_message_minimal: state.is_message_minimal ? false : true });

        case api_constants.API_CREATE_FAILED:
        case api_constants.API_CREATED:
        case api_constants.API_DELETE_FAILED:
        case api_constants.API_DELETED:
        case api_constants.API_READ_FAILED:
        case api_constants.API_READ:
        case api_constants.API_UPDATE_FAILED:
        case api_constants.API_UPDATED:
        case user_constants.ACTION_USER_LOGGED_OUT:
            return Object.assign({}, state, { is_disabled: false });

        case api_constants.API_WILL_CREATE:
        case api_constants.API_WILL_DELETE:
        case api_constants.API_WILL_READ:
        // case api_constants.API_WILL_UPDATE:
        case user_constants.ACTION_WILL_LOGOUT_USER:
            return Object.assign({}, state, { is_disabled: true });

        default:
            return state;
    }
}
<|endoftext|>


<commit_before>(function($) {
  $.entwine('ss', function($) {
    $('.external-links-report__create-report').entwine({
      PollTimeout: null,
      ButtonIsLoading: false,

      onclick: function(e) {
        e.preventDefault();

        this.buttonLoading();
        this.start();
      },

      onmatch: function() {
        // poll the current job and update the front end status
        this.poll();
      },

      start: function() {
        // initiate a new job
        $('.external-links-report__report-progress')
          .empty()
          .text('Running report 0%');

        $.ajax({
          url: "admin/externallinks/start",
          async: true,
          timeout: 3000
        });

        this.poll();
      },

      /**
       * Get the "create report" button selector
       *
       * @return {Object}
       */
      getButton: function() {
        return $('.external-links-report__create-report');
      },

      /**
       * Sets the button into a loading state. See LeftAndMain.js.
       */
      buttonLoading: function() {
        if (this.getButtonIsLoading()) {
          return;
        }
        this.setButtonIsLoading(true);

        var $button = this.getButton();

        // set button to "submitting" state
        $button.addClass('btn--loading loading');

        if ($button.is('button')) {
          $button.append($(
            '
'+
              ''+
              ''+
              ''+
            '
'));

          $button.css($button.outerWidth() + 'px');
        }
      },

      /**
       * Reset the button back to its original state after loading. See LeftAndMain.js.
       */
      buttonReset: function() {
        this.setButtonIsLoading(false);

        var $button = this.getButton();

        $button.removeClass('btn--loading loading');
        $button.find('.btn__loading-icon').remove();
        $button.css('width', 'auto');
      },

      poll: function() {
        var self = this;
        this.buttonLoading();

        $.ajax({
          url: "admin/externallinks/getJobStatus",
          async: true,
          success: function(data) {
            // No report, so let user create one
            if (!data) {
              this.buttonReset();
              return;
            }

            // Parse data
            var completed = data.Completed ? data.Completed : 0;
            var total = data.Total ? data.Total : 0;

            // If complete status
            if (data.Status === 'Completed') {
              $('.external-links-report__report-progress')
                .text('Report finished ' + completed + '/' + total);

              self.buttonReset();
              return;
            }

            // If incomplete update status
            if (completed < total) {
              var percent = (completed / total) * 100;
              $('.external-links-report__report-progress')
                .text('Running report  ' + completed + '/' +  total + ' (' + percent.toFixed(2) + '%)');
            }

            // Ensure the regular poll method is run
            // kill any existing timeout
            if (self.getPollTimeout() !== null) {
              clearTimeout(self.getPollTimeout());
            }

            self.setPollTimeout(setTimeout(function() {
              $('.external-links-report__create-report').poll();
            }, 1000));
          },
          error: function(e) {
            if (typeof console !== 'undefined') {
              console.log(e);
            }
          }
        });
      }
    });
  });
}(jQuery));
<commit_msg>FIX External links report generate button is now disabled while it is loading<commit_after>(function($) {
  $.entwine('ss', function($) {
    $('.external-links-report__create-report').entwine({
      PollTimeout: null,
      ButtonIsLoading: false,

      onclick: function(e) {
        e.preventDefault();

        this.buttonLoading();
        this.start();
      },

      onmatch: function() {
        // poll the current job and update the front end status
        this.poll();
      },

      start: function() {
        // initiate a new job
        $('.external-links-report__report-progress')
          .empty()
          .text('Running report 0%');

        $.ajax({
          url: "admin/externallinks/start",
          async: true,
          timeout: 3000
        });

        this.poll();
      },

      /**
       * Get the "create report" button selector
       *
       * @return {Object}
       */
      getButton: function() {
        return $('.external-links-report__create-report');
      },

      /**
       * Sets the button into a loading state. See LeftAndMain.js.
       */
      buttonLoading: function() {
        if (this.getButtonIsLoading()) {
          return;
        }
        this.setButtonIsLoading(true);

        var $button = this.getButton();

        // set button to "submitting" state
        $button.addClass('btn--loading loading');
        $button.attr('disabled', true);

        if ($button.is('button')) {
          $button.append($(
            '
'+
              ''+
              ''+
              ''+
            '
'));

          $button.css($button.outerWidth() + 'px');
        }
      },

      /**
       * Reset the button back to its original state after loading. See LeftAndMain.js.
       */
      buttonReset: function() {
        this.setButtonIsLoading(false);

        var $button = this.getButton();

        $button.removeClass('btn--loading loading');
        $button.attr('disabled', false);
        $button.find('.btn__loading-icon').remove();
        $button.css('width', 'auto');
      },

      poll: function() {
        var self = this;
        this.buttonLoading();

        $.ajax({
          url: "admin/externallinks/getJobStatus",
          async: true,
          success: function(data) {
            // No report, so let user create one
            if (!data) {
              this.buttonReset();
              return;
            }

            // Parse data
            var completed = data.Completed ? data.Completed : 0;
            var total = data.Total ? data.Total : 0;

            // If complete status
            if (data.Status === 'Completed') {
              $('.external-links-report__report-progress')
                .text('Report finished ' + completed + '/' + total);

              self.buttonReset();
              return;
            }

            // If incomplete update status
            if (completed < total) {
              var percent = (completed / total) * 100;
              $('.external-links-report__report-progress')
                .text('Running report  ' + completed + '/' +  total + ' (' + percent.toFixed(2) + '%)');
            }

            // Ensure the regular poll method is run
            // kill any existing timeout
            if (self.getPollTimeout() !== null) {
              clearTimeout(self.getPollTimeout());
            }

            self.setPollTimeout(setTimeout(function() {
              $('.external-links-report__create-report').poll();
            }, 1000));
          },
          error: function(e) {
            if (typeof console !== 'undefined') {
              console.log(e);
            }
          }
        });
      }
    });
  });
}(jQuery));
<|endoftext|>


<commit_before>// SortedLayout Container
// Original Author: Daniel Plemmons
// Created: Nov. 3, 2014
//
// This is a container for InteractablePlanes that:
// - Allows the planes to be interactively layed out on screen
//   (via sorting) by given properties
// - Allows users to determine the behavior of the on-screen
//   sorting based on the position of their hands
// - Allows the user to manually layout the items in the
//   container and persists that layout in application state
//   (for the duration of the application runtime)
//
// For the moment, the container will make all calculations for
// the plane locations in world space. Eventually this class
// should be modified to give each container it's own local
// coordinate space.
//
// The container currently handles it's own list of child planes.
// This means that multiple containers could reasonably contain
// the same plane. This class should eventually be refactored to
// have the plane's parent determine its container. ( this goes along
// with the proposed coordinate space refactor. )

(function () {
  //The list of valid states for the manner of sorting.
  var validSortStates = [
    "USER_SORTED",
    "DYNAMIC_SORTED"
  ];
  // SortedLayoutContainer Constructor
  // Optional argument "planeList" is a list of InteractivePlanes to be added to the container on creation
  // Optional argument "sortState" is a string given one of the validSortStates listed above.
  window.SortedLayoutContainer = function(planeList, sortState){
    this.planeList = planeList || [];

    if (sortState !== undefined && validSortStates.indexOf(sortState) != -1) {
      this.changeSortState(sortState);
    }
    else {
      this.changeSortState("DYNAMIC_SORTED");
    }
  };

  // Pubic Methods
  window.SortedLayoutContainer.prototype = {
    // Takes two hand positions
    // if the container's current state allows programatic layout (as opposed to manual user layout )
    // will sort the InteractablePlane(s) in the container accordingly (and return true).
    // Update will return false if the current container state does not support
    // programatic layout
    update: function(hand1Position, hand2Position) {
      if ( this.sortState == "DYNAMIC_SORTED" ) {
        var diff = new THREE.Vector3().subVectors(hand2Position, hand1Position);
        var weightX = diff.x / (diff.x + diff.y);
        var weightY = diff.y / (diff.x + diff.y);
        var alphabeticalLayout = listLayout(this.planeList, hand1Position, new THREE.Vector3(hand1Position.x + diff.x, hand1Position.y, hand1Position.z)).alphabetical();
        var chronologicalLayout = listLayout(this.planeList, hand1Position, new THREE.Vector3(hand1Position.x, hand1Position.y + diff.y, hand1Position.z)).alphabetical();
        var blendedLayout = blendLayouts([alphabeticalLayout, chronologicalLayout], [weightX, weightY]);
        applyLayoutList(blendedLayout);
        return true;
      }
      else {
        return false;
      }
    },

    changeSortState: function(newSortState) {
      if (validSortStates.indexOf(newSortState) == -1 || newSortState == this.sortState) {
        return false;
      }

      if ( newSortState == "DYNAMIC_SORTED" ) {
        for(var i=0; i<this.planeList.length; i++) {
          this.planeList[i].interactable = false;
        }
      }
      else if ( newSortState == "USER_SORTED" ) {
        for(var i=0; i<this.planeList.length; i++) {
          this.planeList[i].interactable = true;
        }
      }

      this.sortState = newSortState;
    },

    // Adds the given plane to list of planes managed by the container.
    // Returns true if the plane is successfully added.
    // Returns false if the plane is a duplicate and cannot be added.
    addPlane: function(newPlane) {
      if ( this.planeList.indexOf(newPlane) == -1 ) {
        if ( this.sortState == "DYNAMIC_SORTED" ) {
          newPlane.interactable = false;
        }
        this.planeList.push(newPlane);
        return true;
      }
      else {
        return false;
      }
    },

    // Removes the given plane from the list of planes.
    // Returns true if the plane was successfully removed.
    // Returns false if the plane could not be found and was not removed.
    removePlane: function(toRemove) {
      var planeIndex;
      if ( (planeIndex = this.planeList.indexOf(toRemove)) != -1 ) {
        toRemove.interactable = true;
        this.planeList.splice(planeIndex, 1);
        return true;
      }
      else {
        return false;
      }
    }
  };

  // Generates a LayoutNode object which is just a
  // simple structure to hold a plane and the position
  // generated for it.
  function LayoutNode(plane, position) {
    var returnNode = {
      "plane": plane,
      "position": position
    };
    return returnNode;
  }

  // If called with a sort comparitor, returns an array of layoutNodes specifying the InteractivePlane and the
  // location in space according to the supplied sort comparitor function.
  // Locations are given in a 0-1 space if no positions are specified.
  //
  // if called with just start and end position, returns an object that containts the relevant
  // calls to generate the layout with different sorting properties.
  function listLayout(planeList, startPosition, endPosition, sortComparitor) {
    // If only start and end position are given, utilize partial evaluation
    // to allow a call formated like: listLayout(startPosition, endPosition).alphabetical();
    if (arguments.length == 3) {
      return {
        alphabetical: (function() {
          return listLayout(planeList, startPosition, endPosition, function(a,b) {
            if ( a.mesh.name <= b.mesh.name ) { return -1; }
            else { return 1; }
          });
        }),
        chronological: (function() {
          return listLayout(planeList, startPosition, endPosition, function(a,b) {
            if ( a.uid <= b.uid ) { return -1; }
            else { return 1; }
          });
        })
      };
    }
    else if (arguments.length == 4) {
      var layoutList = [];
      var ittr = 0;
      startPosition || (startPosition = 0);
      endPosition || (endPosition = 0);

      // Sort the planelist alphabetically by "plane.mesh.name"
      planeList.sort(sortComparitor);

      // Generate the layout list full of layout nodes
      for ( var plane in planeList ) {
        var listPercentage = (ittr*1.0) / (planeList.length*1.0); // force double division
        var position = startPosition.lerp(endPosition, listPercentage);
        layoutList.push(LayoutNode(plane, position));
        ittr += 1;
      }

      return layoutList;
    }
    else {
      return false;
    }
  }

  // - Retruns a weighted mean of the given layout node lists as an array of layout nodes
  // - Assumes that each index of each list in layout lists referes to the
  //   same node.
  // - The length of the weightList and the layoutList must be equal.
  function blendLayouts(layoutLists, weightList) {
    var ittr = 0;
    var weightSumList = [];
    var vectorSumList = [];
    var blendedLayoutList = [];

    // Confirm arguments are valid(ish)
    if( layoutList === undefined || weightList === undefined) { return false; }
    else if ( layoutList.length != weightList.length ) { return false; }
    else if ( layoutList.length === 0 ) { return false; }

    // For each node, calculate the weighted sum of vectors
    // along with the sum of weights.
    for( var layoutList in layoutLists ) {
      var weight = weightList[ittr];
      for ( var layoutNode in layoutList ) {
        if (vectorSumList[ittr] === undefined) { vectorSumList[ittr] = layoutNode.position; }
        else { vectorSumList[ittr].add(layoutNode.position.multiplyScalar(weight)); }
        weightSum += weight;
      }
      ittr+=1;
    }

    ittr = 0; // reset itterator

    //Calculate the weighted mean for each node
    for ( var vecSum in sumList ) {
      var plane = layoutLists[0][ittr].plane;
      var weightSum = weightSumList[ittr];
      blendedLayoutList[ittr] = LayoutNode(plane, vecSum.divideScalar(weightSum));
      ittr += 1;
    }

    return weightedMeanList;
  }

  // Itterate through a layout list and move the given elements to the
  // given positions.
  function applyLayoutList(layoutList) {
    for(var node in layoutList) {
      var plane = node.plane;
      var localSpace = plane.worldToLocal(node.position); // Convert our global coordinates to local space.
      plane.position = localSpace; // Set the local space position of the plane.
    }
  }
}).call(this);<commit_msg>switch for-in loops to classic for loops<commit_after>// SortedLayout Container
// Original Author: Daniel Plemmons
// Created: Nov. 3, 2014
//
// This is a container for InteractablePlanes that:
// - Allows the planes to be interactively layed out on screen
//   (via sorting) by given properties
// - Allows users to determine the behavior of the on-screen
//   sorting based on the position of their hands
// - Allows the user to manually layout the items in the
//   container and persists that layout in application state
//   (for the duration of the application runtime)
//
// For the moment, the container will make all calculations for
// the plane locations in world space. Eventually this class
// should be modified to give each container it's own local
// coordinate space.
//
// The container currently handles it's own list of child planes.
// This means that multiple containers could reasonably contain
// the same plane. This class should eventually be refactored to
// have the plane's parent determine its container. ( this goes along
// with the proposed coordinate space refactor. )

(function () {
  //The list of valid states for the manner of sorting.
  var validSortStates = [
    "USER_SORTED",
    "DYNAMIC_SORTED"
  ];
  // SortedLayoutContainer Constructor
  // Optional argument "planeList" is a list of InteractivePlanes to be added to the container on creation
  // Optional argument "sortState" is a string given one of the validSortStates listed above.
  window.SortedLayoutContainer = function(planeList, sortState){
    this.planeList = planeList || [];

    if (sortState !== undefined && validSortStates.indexOf(sortState) != -1) {
      this.changeSortState(sortState);
    }
    else {
      this.changeSortState("DYNAMIC_SORTED");
    }
  };

  // Pubic Methods
  window.SortedLayoutContainer.prototype = {
    // Takes two hand positions
    // if the container's current state allows programatic layout (as opposed to manual user layout )
    // will sort the InteractablePlane(s) in the container accordingly (and return true).
    // Update will return false if the current container state does not support
    // programatic layout
    update: function(hand1Position, hand2Position) {
      if ( this.sortState == "DYNAMIC_SORTED" ) {
        var diff = new THREE.Vector3().subVectors(hand2Position, hand1Position);
        var weightX = diff.x / (diff.x + diff.y);
        var weightY = diff.y / (diff.x + diff.y);
        var alphabeticalLayout = listLayout(this.planeList, hand1Position, new THREE.Vector3(hand1Position.x + diff.x, hand1Position.y, hand1Position.z)).alphabetical();
        var chronologicalLayout = listLayout(this.planeList, hand1Position, new THREE.Vector3(hand1Position.x, hand1Position.y + diff.y, hand1Position.z)).alphabetical();
        var blendedLayout = blendLayouts([alphabeticalLayout, chronologicalLayout], [weightX, weightY]);
        applyLayoutList(blendedLayout);
        return true;
      }
      else {
        return false;
      }
    },

    changeSortState: function(newSortState) {
      if (validSortStates.indexOf(newSortState) == -1 || newSortState == this.sortState) {
        return false;
      }

      if ( newSortState == "DYNAMIC_SORTED" ) {
        for(var i=0; i<this.planeList.length; i++) {
          this.planeList[i].interactable = false;
        }
      }
      else if ( newSortState == "USER_SORTED" ) {
        for(var i=0; i<this.planeList.length; i++) {
          this.planeList[i].interactable = true;
        }
      }

      this.sortState = newSortState;
    },

    // Adds the given plane to list of planes managed by the container.
    // Returns true if the plane is successfully added.
    // Returns false if the plane is a duplicate and cannot be added.
    addPlane: function(newPlane) {
      if ( this.planeList.indexOf(newPlane) == -1 ) {
        if ( this.sortState == "DYNAMIC_SORTED" ) {
          newPlane.interactable = false;
        }
        this.planeList.push(newPlane);
        return true;
      }
      else {
        return false;
      }
    },

    // Removes the given plane from the list of planes.
    // Returns true if the plane was successfully removed.
    // Returns false if the plane could not be found and was not removed.
    removePlane: function(toRemove) {
      var planeIndex;
      if ( (planeIndex = this.planeList.indexOf(toRemove)) != -1 ) {
        toRemove.interactable = true;
        this.planeList.splice(planeIndex, 1);
        return true;
      }
      else {
        return false;
      }
    }
  };

  // Generates a LayoutNode object which is just a
  // simple structure to hold a plane and the position
  // generated for it.
  function LayoutNode(plane, position) {
    var returnNode = {
      "plane": plane,
      "position": position
    };
    return returnNode;
  }

  // If called with a sort comparitor, returns an array of layoutNodes specifying the InteractivePlane and the
  // location in space according to the supplied sort comparitor function.
  // Locations are given in a 0-1 space if no positions are specified.
  //
  // if called with just start and end position, returns an object that containts the relevant
  // calls to generate the layout with different sorting properties.
  function listLayout(planeList, startPosition, endPosition, sortComparitor) {
    // If only start and end position are given, utilize partial evaluation
    // to allow a call formated like: listLayout(startPosition, endPosition).alphabetical();
    if (arguments.length == 3) {
      return {
        alphabetical: (function() {
          return listLayout(planeList, startPosition, endPosition, function(a,b) {
            if ( a.mesh.name <= b.mesh.name ) { return -1; }
            else { return 1; }
          });
        }),
        chronological: (function() {
          return listLayout(planeList, startPosition, endPosition, function(a,b) {
            if ( a.uid <= b.uid ) { return -1; }
            else { return 1; }
          });
        })
      };
    }
    else if (arguments.length == 4) {
      var layoutList = [];
      startPosition || (startPosition = 0);
      endPosition || (endPosition = 0);

      // Sort the planelist alphabetically by "plane.mesh.name"
      planeList.sort(sortComparitor);

      // Generate the layout list full of layout nodes
      for (var i=0; i<planeList.length; i++) {
        var plane = planeList[i];
        var listPercentage = (i*1.0) / (planeList.length*1.0); // force double division
        var position = new THREE.Vector3();
        position.copy(startPosition).lerp(endPosition, listPercentage);
        layoutList.push(LayoutNode(plane, position));
      }

      return layoutList;
    }
    else {
      return false;
    }
  }

  // - Retruns a weighted mean of the given layout node lists as an array of layout nodes
  // - Assumes that each index of each list in layout lists referes to the
  //   same node.
  // - The length of the weightList and the layoutList must be equal.
  function blendLayouts(layoutLists, weightList) {
    var ittr = 0;
    var weightSumList = [];
    var vectorSumList = [];
    var blendedLayoutList = [];

    // Confirm arguments are valid(ish)
    if( layoutLists === undefined || weightList === undefined) { return false; }
    else if ( !(Array.isArray(layoutLists)) || !(Array.isArray(layoutLists))) { return false; }
    else if ( layoutLists.length != weightList.length ) { return false; }
    else if ( layoutLists.length === 0 ) { return false; }

    // For each node, calculate the weighted sum of vectors
    // along with the sum of weights.
    for( var i=0; i < layoutLists.length; i++ ) {
      var layoutList = layoutLists[i];
      var weight = weightList[i];
      for ( var j=0; j<layoutList.length; j++ ) {
        var layoutNode = layoutList[j];
        if (vectorSumList[j] === undefined) { vectorSumList[j] = layoutNode.position.multiplyScalar(weight); }
        else { vectorSumList[j].add(layoutNode.position.multiplyScalar(weight)); }

        if (weightSumList[j] === undefined) { weightSumList[j] = weight; }
        else { weightSumList[j] += weight; }
      }
    }

    //Calculate the weighted mean for each node
    for ( var i=0; i < vectorSumList.length; i++) {
      vecSum = vectorSumList[i];
      var plane = layoutLists[0][i].plane;
      var weightSum = weightSumList[i];
      blendedLayoutList[i] = LayoutNode(plane, vecSum.divideScalar(weightSum));
    }

    return blendedLayoutList;
  }

  // Itterate through a layout list and move the given elements to the
  // given positions.
  function applyLayoutList(layoutList) {
    console.log("applyLayoutList");
    console.log(layoutList);
    for(var i=0; i<layoutList.length; i++) {
      var node = layoutList[i];
      var plane = node.plane;
      console.log("plane interactable: " + plane.interactable);
      var localSpace = plane.mesh.worldToLocal(node.position); // Convert our global coordinates to local space.

      //plane.mesh.position = localSpace; // Set the local space position of the plane.
      plane.mesh.position.copy(localSpace);
    }
  }
}).call(this);<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  name: 'EMailCitationView',
  package: 'com.google.mail',
  extends: 'foam.ui.DetailView',
  requires: [
    'foam.ui.md.MonogramStringView',
    'foam.ui.RelativeDateTimeFieldView',
    'foam.ui.ImageBooleanView'
  ],
  imports: [
    'controller'
  ],
  properties: [
    { name: 'className', defaultValue: 'email-citation' },
    {
      name: 'preferredHeight',
      help: 'Specifying the preferred height of this view for the ScrollView, since an empty row is too small.',
      defaultValue: 82
    }
  ],
  templates: [
    function CSS() {/*
      .email-citation {
        display: flex;
        display: -webkit-flex;
        border-bottom: solid #B5B5B5 1px;
        padding: 10px 14px 10px 6px;
      }

      .email-citation.unread {
        font-weight: bold;
      }

      .email-citation .from {
        display: block;
        font-size: 17px;
        line-height: 24px;
        white-space: nowrap;
        overflow-x:hidden;
        text-overflow: ellipsis;
        flex-grow: 1;
        -webkit-flex-grow: 1;
      }

      .email-citation .timestamp {
        font-size: 12px;
        color: rgb(17, 85, 204);
        white-space: nowrap;
        flex-shrink: 0;
        -webkit-flex-shrink: 0;
      }

      .email-citation .subject {
        display: block;
        font-size: 13px;
        line-height: 17px;
        overflow-x:hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .email-citation .snippet {
        color: rgb(119, 119, 119);
        display: block;
        font-size: 13px;
        height: 20px;
        overflow-x: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .email-citation .monogram-string-view {
        margin: auto 6px auto 0;
      }
    */},
    function toHTML() {/*
      <%
        var id = this.setClass('unread', function() { return self.data && self.data.unread; }, this.id);
      %>

      <div id="<%= id %>" %%cssClassAttr() >
        $$from{model_: 'foam.ui.md.MonogramStringView'}

            $$from{mode: 'read-only', className: 'from', escapeHTML: true}
            $$timestamp{ model_: 'foam.ui.RelativeDateTimeFieldView', mode: 'read-only', className: 'timestamp' }

              $$subject{mode: 'read-only', className: 'subject'}
              $$snippet{mode: 'read-only', className: 'snippet'}

            $$starred{
              model_: 'foam.ui.ImageBooleanView',
              className:  'star',
              trueImage:  'images/ic_star_24dp.png',
              falseImage: 'images/ic_star_outline_24dp.png'
            }

    */}
   ]
});
<commit_msg>Quick fix for Gmail app rows being too small.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  name: 'EMailCitationView',
  package: 'com.google.mail',
  extends: 'foam.ui.DetailView',
  requires: [
    'foam.ui.md.MonogramStringView',
    'foam.ui.RelativeDateTimeFieldView',
    'foam.ui.ImageBooleanView'
  ],
  imports: [
    'controller'
  ],
  properties: [
    { name: 'className', defaultValue: 'email-citation' },
    {
      name: 'preferredHeight',
      help: 'Specifying the preferred height of this view for the ScrollView, since an empty row is too small.',
      defaultValue: 82
    }
  ],
  templates: [
    function CSS() {/*
      .email-citation {
        display: flex;
        display: -webkit-flex;
        border-bottom: solid #B5B5B5 1px;
        padding: 10px 14px 10px 6px;
        height: 82px;
      }

      .email-citation.unread {
        font-weight: bold;
      }

      .email-citation .from {
        display: block;
        font-size: 17px;
        line-height: 24px;
        white-space: nowrap;
        overflow-x:hidden;
        text-overflow: ellipsis;
        flex-grow: 1;
        -webkit-flex-grow: 1;
      }

      .email-citation .timestamp {
        font-size: 12px;
        color: rgb(17, 85, 204);
        white-space: nowrap;
        flex-shrink: 0;
        -webkit-flex-shrink: 0;
      }

      .email-citation .subject {
        display: block;
        font-size: 13px;
        line-height: 17px;
        overflow-x:hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .email-citation .snippet {
        color: rgb(119, 119, 119);
        display: block;
        font-size: 13px;
        height: 20px;
        overflow-x: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .email-citation .monogram-string-view {
        margin: auto 6px auto 0;
      }
    */},
    function toHTML() {/*
      <%
        var id = this.setClass('unread', function() { return self.data && self.data.unread; }, this.id);
      %>

      <div id="<%= id %>" %%cssClassAttr() >
        $$from{model_: 'foam.ui.md.MonogramStringView'}

            $$from{mode: 'read-only', className: 'from', escapeHTML: true}
            $$timestamp{ model_: 'foam.ui.RelativeDateTimeFieldView', mode: 'read-only', className: 'timestamp' }

              $$subject{mode: 'read-only', className: 'subject'}
              $$snippet{mode: 'read-only', className: 'snippet'}

            $$starred{
              model_: 'foam.ui.ImageBooleanView',
              className:  'star',
              trueImage:  'images/ic_star_24dp.png',
              falseImage: 'images/ic_star_outline_24dp.png'
            }

    */}
   ]
});
<|endoftext|>