<commit_before>namespace('Sy');

/**
 * Default implementation of a controller
 *
 * @package Sy
 * @class
 * @implements {Sy.ControllerInterface}
 */

Sy.Controller = function () {

    this.container = null;
    this.mediator = null;
    this.mediatorListeners = {};
    this.bundle = '';
    this.viewscreen = null;

};

Sy.Controller.prototype = Object.create(Sy.ControllerInterface.prototype, {

    /**
     * @inheritDoc
     */

    listen: {
        value: function (channel, fn) {

            var uuid = this.mediator.subscribe({
                channel: channel,
                fn: fn,
                context: this
            });

            if (!this.mediatorListeners[channel]) {
                this.mediatorListeners[channel] = [];
            }

            this.mediatorListeners[channel].push(uuid);

            return this;

        }
    },

    /**
     * @inheritDoc
     */

    broadcast: {
        value: function () {

            this.mediator.publish.apply(this.mediator, arguments);

            return this;

        }
    },

    /**
     * @inheritDoc
     */

    new: {
        value: function (entity, attributes) {

            var regexp = new RegExp(/^((\w+::)|(\w+))+$/gi),
                path = null,
                ent = null;

            if (!regexp.test(entity)) {
                throw new SyntaxError('Invalid entity name format');
            }

            path = entity.split('::');

            if (path.length === 1) {
                ent = new App.Bundle[this.bundle].Entity[path[0]]();
            } else {
                ent = new App.Bundle[path[0]].Entity[path[1]]();
            }

            if (!(ent instanceof Sy.EntityInterface)) {
                throw new TypeError('"' + entity + '" does not implement "Sy.EntityInterface"');
            }

            ent.set(attributes);

            return ent;

        }
    },

    /**
     * @inheritDoc
     */

    setBundle: {
        value: function (name) {

            if (!App.Bundle[name]) {
                throw new ReferenceError('The bundle "' + name + '" is undefined');
            }

            this.bundle = name;

            return this;

        }
    },

    /**
     * @inheritDoc
     */

    setMediator: {
        value: function (mediator) {

            if (!(mediator instanceof Sy.Lib.Mediator)) {
                throw new TypeError('Invalid mediator');
            }

            this.mediator = mediator;

            return this;

        }
    },

    /**
     * @inheritDoc
     */

    setServiceContainer: {
        value: function (container) {

            if (!(container instanceof Sy.ServiceContainerInterface)) {
                throw new TypeError('Invalid service container');
            }

            this.container = container;

            return this;

        }
    },

    /**
     * @inheritDoc
     */

    sleep: {
        value: function () {

            for (var channel in this.mediatorListeners) {
                if (this.mediatorListeners.hasOwnProperty(channel)) {
                    for (var i = 0, l = this.mediatorListeners[channel].length; i < l; i++) {
                        this.mediator.pause(channel, this.mediatorListeners[channel][i]);
                    }
                }
            }

        }
    },

    /**
     * @inheritDoc
     */

    wakeup: {
        value: function () {

            for (var channel in this.mediatorListeners) {
                if (this.mediatorListeners.hasOwnProperty(channel)) {
                    for (var i = 0, l = this.mediatorListeners[channel].length; i < l; i++) {
                        this.mediator.unpause(channel, this.mediatorListeners[channel][i]);
                    }
                }
            }

        }
    },

    /**
     * @inheritDoc
     */
    destroy: {
        value: function () {

            for (var channel in this.mediatorListeners) {
                if (this.mediatorListeners.hasOwnProperty(channel)) {
                    for (var i = 0, l = this.mediatorListeners[channel].length; i < l; i++) {
                        this.mediator.remove(channel, this.mediatorListeners[channel][i]);
                    }
                }
            }

        }
    },

    /**
     * @inheritDoc
     */

    setViewScreen: {
        value: function (viewscreen) {

            if (!(viewscreen instanceof Sy.View.ViewScreen)) {
                throw new TypeError('Invalid viewscreen');
            }

            this.viewscreen = viewscreen;

            return this;

        }
    }

});<commit_msg>added shortcuts from the controller to create forms ans form builders<commit_after>namespace('Sy');

/**
 * Default implementation of a controller
 *
 * @package Sy
 * @class
 * @implements {Sy.ControllerInterface}
 */

Sy.Controller = function () {

    this.container = null;
    this.mediator = null;
    this.mediatorListeners = {};
    this.bundle = '';
    this.viewscreen = null;

};

Sy.Controller.prototype = Object.create(Sy.ControllerInterface.prototype, {

    /**
     * @inheritDoc
     */

    listen: {
        value: function (channel, fn) {

            var uuid = this.mediator.subscribe({
                channel: channel,
                fn: fn,
                context: this
            });

            if (!this.mediatorListeners[channel]) {
                this.mediatorListeners[channel] = [];
            }

            this.mediatorListeners[channel].push(uuid);

            return this;

        }
    },

    /**
     * @inheritDoc
     */

    broadcast: {
        value: function () {

            this.mediator.publish.apply(this.mediator, arguments);

            return this;

        }
    },

    /**
     * @inheritDoc
     */

    new: {
        value: function (entity, attributes) {

            var regexp = new RegExp(/^((\w+::)|(\w+))+$/gi),
                path = null,
                ent = null;

            if (!regexp.test(entity)) {
                throw new SyntaxError('Invalid entity name format');
            }

            path = entity.split('::');

            if (path.length === 1) {
                ent = new App.Bundle[this.bundle].Entity[path[0]]();
            } else {
                ent = new App.Bundle[path[0]].Entity[path[1]]();
            }

            if (!(ent instanceof Sy.EntityInterface)) {
                throw new TypeError('"' + entity + '" does not implement "Sy.EntityInterface"');
            }

            ent.set(attributes);

            return ent;

        }
    },

    /**
     * @inheritDoc
     */

    setBundle: {
        value: function (name) {

            if (!App.Bundle[name]) {
                throw new ReferenceError('The bundle "' + name + '" is undefined');
            }

            this.bundle = name;

            return this;

        }
    },

    /**
     * @inheritDoc
     */

    setMediator: {
        value: function (mediator) {

            if (!(mediator instanceof Sy.Lib.Mediator)) {
                throw new TypeError('Invalid mediator');
            }

            this.mediator = mediator;

            return this;

        }
    },

    /**
     * @inheritDoc
     */

    setServiceContainer: {
        value: function (container) {

            if (!(container instanceof Sy.ServiceContainerInterface)) {
                throw new TypeError('Invalid service container');
            }

            this.container = container;

            return this;

        }
    },

    /**
     * @inheritDoc
     */

    sleep: {
        value: function () {

            for (var channel in this.mediatorListeners) {
                if (this.mediatorListeners.hasOwnProperty(channel)) {
                    for (var i = 0, l = this.mediatorListeners[channel].length; i < l; i++) {
                        this.mediator.pause(channel, this.mediatorListeners[channel][i]);
                    }
                }
            }

        }
    },

    /**
     * @inheritDoc
     */

    wakeup: {
        value: function () {

            for (var channel in this.mediatorListeners) {
                if (this.mediatorListeners.hasOwnProperty(channel)) {
                    for (var i = 0, l = this.mediatorListeners[channel].length; i < l; i++) {
                        this.mediator.unpause(channel, this.mediatorListeners[channel][i]);
                    }
                }
            }

        }
    },

    /**
     * @inheritDoc
     */
    destroy: {
        value: function () {

            for (var channel in this.mediatorListeners) {
                if (this.mediatorListeners.hasOwnProperty(channel)) {
                    for (var i = 0, l = this.mediatorListeners[channel].length; i < l; i++) {
                        this.mediator.remove(channel, this.mediatorListeners[channel][i]);
                    }
                }
            }

        }
    },

    /**
     * @inheritDoc
     */

    setViewScreen: {
        value: function (viewscreen) {

            if (!(viewscreen instanceof Sy.View.ViewScreen)) {
                throw new TypeError('Invalid viewscreen');
            }

            this.viewscreen = viewscreen;

            return this;

        }
    },

    /**
     * Shortcut to create a form builder
     *
     * @param {Object} object Object that will hold form data
     * @param {Object} options
     *
     * @return {Sy.Form.FormBuilderInterface}
     */

    createFormBuilder: {
        value: function (object, options) {
            return this.container
                .get('sy::core::form')
                .createFormBuilder(object, options);
        }
    },

    /**
     * Shortcut to create a form from a form type
     *
     * @param {Sy.Form.FormTypeInterface|String} formType
     * @param {Object} options
     *
     * @return {Sy.Form.FormInterface}
     */

    createForm: {
        value: function (formType, options) {
            return this.container
                .get('sy::core::form')
                .createForm(formType, options);
        }
    }

});<|endoftext|>


<commit_before>/** global: Garnish */
/**
 * Disclosure Widget
 */
Garnish.Disclosure = Garnish.Base.extend(
  {
    settings: null,

    $trigger: null,
    $container: null,
    $anchor: null,

    _windowWidth: null,
    _windowHeight: null,
    _windowScrollLeft: null,
    _windowScrollTop: null,

    _anchorOffset: null,
    _anchorWidth: null,
    _anchorHeight: null,
    _anchorOffsetRight: null,
    _anchorOffsetBottom: null,

    _menuWidth: null,
    _menuHeight: null,

    /**
     * Constructor
     */
    init: function (trigger, settings) {
      this.setSettings(settings, Garnish.Disclosure.defaults);

      this.$trigger = $(trigger);

      this.captureToggleSettings();

      // Get disclosure container from aria-controls attribute
      var triggerId = this.$trigger.attr('aria-controls');
      this.$container = $("#" + triggerId);

      // Get and store expanded state from trigger
      var expanded = this.$trigger.attr('aria-expanded');

      // If no expanded state exists on trigger, add for a11y
      if (!expanded) {
        this.$trigger.attr('aria-expanded', 'false');
      }

      this.addDisclosureEventListeners();
    },

    /**
     * Capture whether focus out and ESC will toggle closed
     */
    captureToggleSettings: function() {
      var toggleAttribute = this.$trigger.attr('data-click-toggle-only');

      if (!toggleAttribute) return;

      this.settings.clickToggleOnly = true;
    },

    addDisclosureEventListeners: function() {
      this.addListener(this.$trigger, 'click', function() {
        this.handleTriggerClick();
      });

      if (this.settings.clickToggleOnly === true) return;

      this.addListener(this.$container, 'keyup', function(event) {
        this.handleKeypress(event);
      });

      this.addListener(this.$container, 'focusout', function(event) {
        var newTarget = event.relatedTarget;
        var newTargetIsInsideDisclosure = this.$container.has(newTarget).length > 0;

        // If click target matches trigger element or disclosure child, do nothing
      if (newTarget === this.$trigger.get(0) || newTargetIsInsideDisclosure) {
          return;
        }

        this.hide();
      });
    },

    handleKeypress: function(event) {
      if (event.key === 'Escape') {
        this.hide();
        this.$trigger.focus();
      }
    },

    isExpanded: function () {
      var isExpanded = this.$trigger.attr('aria-expanded');

      return isExpanded === 'true';
    },

    handleTriggerClick: function() {
      if (!this.isExpanded()) {
        this.show();
      } else {
        this.hide();
      }
    },

    setPositionRelativeToAnchor: function () {
      this._windowWidth = Garnish.$win.width();
      this._windowHeight = Garnish.$win.height();
      this._windowScrollLeft = Garnish.$win.scrollLeft();
      this._windowScrollTop = Garnish.$win.scrollTop();

      this._anchorOffset = this.$anchor.offset();
      this._anchorWidth = this.$anchor.outerWidth();
      this._anchorHeight = this.$anchor.outerHeight();
      this._anchorOffsetRight = this._anchorOffset.left + this._anchorHeight;
      this._anchorOffsetBottom = this._anchorOffset.top + this._anchorHeight;

      this.$container.css('minWidth', 0);
      this.$container.css(
        'minWidth',
        this._anchorWidth -
          (this.$container.outerWidth() - this.$container.width())
      );

      this._menuWidth = this.$container.outerWidth();
      this._menuHeight = this.$container.outerHeight();

      // Is there room for the menu below the anchor?
      var topClearance = this._anchorOffset.top - this._windowScrollTop,
        bottomClearance =
          this._windowHeight + this._windowScrollTop - this._anchorOffsetBottom;

      if (
        bottomClearance >= this._menuHeight ||
        (topClearance < this._menuHeight && bottomClearance >= topClearance)
      ) {
        this.$container.css({
          top: this._anchorOffsetBottom,
          maxHeight: bottomClearance - this.settings.windowSpacing,
        });
      } else {
        this.$container.css({
          top:
            this._anchorOffset.top -
            Math.min(
              this._menuHeight,
              topClearance - this.settings.windowSpacing
            ),
          maxHeight: topClearance - this.settings.windowSpacing,
        });
      }

      // Figure out how we're aliging it
      var align = this.$container.data('align');

      if (align !== 'left' && align !== 'center' && align !== 'right') {
        align = 'left';
      }

      if (align === 'center') {
        this._alignCenter();
      } else {
        // Figure out which options are actually possible
        var rightClearance =
            this._windowWidth +
            this._windowScrollLeft -
            (this._anchorOffset.left + this._menuWidth),
          leftClearance = this._anchorOffsetRight - this._menuWidth;

        if ((align === 'right' && leftClearance >= 0) || rightClearance < 0) {
          this._alignRight();
        } else {
          this._alignLeft();
        }
      }

      delete this._windowWidth;
      delete this._windowHeight;
      delete this._windowScrollLeft;
      delete this._windowScrollTop;
      delete this._anchorOffset;
      delete this._anchorWidth;
      delete this._anchorHeight;
      delete this._anchorOffsetRight;
      delete this._anchorOffsetBottom;
      delete this._menuWidth;
      delete this._menuHeight;
    },

    show: function () {
      if (this.isExpanded()) {
        return;
      }

      this.$container.velocity('stop');
      this.$container.css({
        opacity: 1,
        display: 'block',
      });

      // Set ARIA attribute for expanded
      this.$trigger.attr('aria-expanded', 'true');

      this.addListener(
        Garnish.$scrollContainer,
        'scroll',
        'setPositionRelativeToAnchor'
      );

      // Focus first focusable element
      var firstFocusableEl = this.$container.find(':focusable')[0];
      if (firstFocusableEl) {
        firstFocusableEl.focus();
      } else {
        this.$container.attr('tabindex', '-1');
        this.$container.focus();
      }
    },

    hide: function () {
      if (!this.isExpanded()) {
        return;
      }

      this.$container.velocity(
        'fadeOut',
        { duration: Garnish.FX_DURATION }
      );

      this.$trigger.attr('aria-expanded', 'false');
    },

    _alignLeft: function () {
      this.$container.css({
        left: this._anchorOffset.left,
        right: 'auto',
      });
    },

    _alignRight: function () {
      this.$container.css({
        right:
          this._windowWidth - (this._anchorOffset.left + this._anchorWidth),
        left: 'auto',
      });
    },

    _alignCenter: function () {
      var left = Math.round(
        this._anchorOffset.left + this._anchorWidth / 2 - this._menuWidth / 2
      );

      if (left < 0) {
        left = 0;
      }

      this.$container.css('left', left);
    },
  },
  {
    defaults: {
      anchor: null,
      windowSpacing: 5,
      clickToggleOnly: false,
    },
  }
);
<commit_msg>Fix vertical alignment issue caused by not appending to end of body<commit_after>/** global: Garnish */
/**
 * Disclosure Widget
 */
Garnish.Disclosure = Garnish.Base.extend(
  {
    settings: null,

    $trigger: null,
    $container: null,
    $anchor: null,

    _windowWidth: null,
    _windowHeight: null,
    _windowScrollLeft: null,
    _windowScrollTop: null,

    _anchorOffset: null,
    _anchorWidth: null,
    _anchorHeight: null,
    _anchorOffsetRight: null,
    _anchorOffsetBottom: null,

    _menuWidth: null,
    _menuHeight: null,

    /**
     * Constructor
     */
    init: function (trigger, settings) {
      this.setSettings(settings, Garnish.Disclosure.defaults);

      this.$trigger = $(trigger);
      this.$anchor = this.$trigger;

      this.captureToggleSettings();

      // Get disclosure container from aria-controls attribute
      var triggerId = this.$trigger.attr('aria-controls');
      this.$container = $("#" + triggerId);

      // Get and store expanded state from trigger
      var expanded = this.$trigger.attr('aria-expanded');

      // If no expanded state exists on trigger, add for a11y
      if (!expanded) {
        this.$trigger.attr('aria-expanded', 'false');
      }

      this.addDisclosureEventListeners();
    },

    /**
     * Capture whether focus out and ESC will toggle closed
     */
    captureToggleSettings: function() {
      var toggleAttribute = this.$trigger.attr('data-click-toggle-only');

      if (!toggleAttribute) return;

      this.settings.clickToggleOnly = true;
    },

    addDisclosureEventListeners: function() {
      this.addListener(this.$trigger, 'click', function() {
        this.handleTriggerClick();
      });

      if (this.settings.clickToggleOnly === true) return;

      this.addListener(this.$container, 'keyup', function(event) {
        this.handleKeypress(event);
      });

      this.addListener(this.$container, 'focusout', function(event) {
        var newTarget = event.relatedTarget;
        var newTargetIsInsideDisclosure = this.$container.has(newTarget).length > 0;

        // If click target matches trigger element or disclosure child, do nothing
      if (newTarget === this.$trigger.get(0) || newTargetIsInsideDisclosure) {
          return;
        }

        this.hide();
      });
    },

    handleKeypress: function(event) {
      if (event.key === 'Escape') {
        this.hide();
        this.$trigger.focus();
      }
    },

    isExpanded: function () {
      var isExpanded = this.$trigger.attr('aria-expanded');

      return isExpanded === 'true';
    },

    handleTriggerClick: function() {
      if (!this.isExpanded()) {
        this.show();
      } else {
        this.hide();
      }
    },

    setPositionRelativeToAnchor: function () {
      this._windowWidth = Garnish.$win.width();
      this._windowHeight = Garnish.$win.height();
      this._windowScrollLeft = Garnish.$win.scrollLeft();
      this._windowScrollTop = Garnish.$win.scrollTop();

      this._anchorOffset = this.$anchor.offset();
      this._anchorWidth = this.$anchor.outerWidth();
      this._anchorHeight = this.$anchor.outerHeight();
      this._anchorOffsetRight = this._anchorOffset.left + this._anchorHeight;
      this._anchorOffsetBottom = this._anchorOffset.top + this._anchorHeight;

      this.$container.css('minWidth', 0);
      this.$container.css(
        'minWidth',
        this._anchorWidth -
          (this.$container.outerWidth() - this.$container.width())
      );

      this._menuWidth = this.$container.outerWidth();
      this._menuHeight = this.$container.outerHeight();

      // Is there room for the menu below the anchor?
      var topClearance = this._anchorOffset.top - this._windowScrollTop,
        bottomClearance =
          this._windowHeight + this._windowScrollTop - this._anchorOffsetBottom;

      if (
        bottomClearance >= this._menuHeight ||
        (topClearance < this._menuHeight && bottomClearance >= topClearance)
      ) {
        this.$container.css({
          top: this._anchorOffsetBottom,
          maxHeight: bottomClearance - this.settings.windowSpacing,
        });
      } else {
        this.$container.css({
          top:
            this._anchorOffset.top -
            Math.min(
              this._menuHeight,
              topClearance - this.settings.windowSpacing
            ),
          maxHeight: topClearance - this.settings.windowSpacing,
        });
      }

      // Figure out how we're aliging it
      var align = this.$container.data('align');

      if (align !== 'left' && align !== 'center' && align !== 'right') {
        align = 'left';
      }

      if (align === 'center') {
        this._alignCenter();
      } else {
        // Figure out which options are actually possible
        var rightClearance =
            this._windowWidth +
            this._windowScrollLeft -
            (this._anchorOffset.left + this._menuWidth),
          leftClearance = this._anchorOffsetRight - this._menuWidth;

        if ((align === 'right' && leftClearance >= 0) || rightClearance < 0) {
          this._alignRight();
        } else {
          this._alignLeft();
        }
      }

      delete this._windowWidth;
      delete this._windowHeight;
      delete this._windowScrollLeft;
      delete this._windowScrollTop;
      delete this._anchorOffset;
      delete this._anchorWidth;
      delete this._anchorHeight;
      delete this._anchorOffsetRight;
      delete this._anchorOffsetBottom;
      delete this._menuWidth;
      delete this._menuHeight;
    },

    show: function () {
      if (this.isExpanded()) {
        return;
      }

      // Move the menu to the end of the DOM
      this.$container.appendTo(Garnish.$bod);
      this.setPositionRelativeToAnchor();

      this.$container.velocity('stop');
      this.$container.css({
        opacity: 1,
        display: 'block',
      });

      // Set position
      this.addListener(
        Garnish.$scrollContainer,
        'scroll',
        'setPositionRelativeToAnchor'
      );

      // Set ARIA attribute for expanded
      this.$trigger.attr('aria-expanded', 'true');

      this.addListener(
        Garnish.$scrollContainer,
        'scroll',
        'setPositionRelativeToAnchor'
      );

      // Focus first focusable element
      var firstFocusableEl = this.$container.find(':focusable')[0];
      if (firstFocusableEl) {
        firstFocusableEl.focus();
      } else {
        this.$container.attr('tabindex', '-1');
        this.$container.focus();
      }
    },

    hide: function () {
      if (!this.isExpanded()) {
        return;
      }

      this.$container.velocity(
        'fadeOut',
        { duration: Garnish.FX_DURATION }
      );

      this.$trigger.attr('aria-expanded', 'false');
    },

    _alignLeft: function () {
      this.$container.css({
        left: this._anchorOffset.left,
        right: 'auto',
      });
    },

    _alignRight: function () {
      this.$container.css({
        right:
          this._windowWidth - (this._anchorOffset.left + this._anchorWidth),
        left: 'auto',
      });
    },

    _alignCenter: function () {
      var left = Math.round(
        this._anchorOffset.left + this._anchorWidth / 2 - this._menuWidth / 2
      );

      if (left < 0) {
        left = 0;
      }

      this.$container.css('left', left);
    },
  },
  {
    defaults: {
      anchor: null,
      windowSpacing: 5,
      clickToggleOnly: false,
    },
  }
);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 The Open Source Geospatial Foundation
 *
 * Published under the BSD license.
 *
 * See https://raw.github.com/geoext/GXM/master/license.txt for the full
 * text of the license.
 */

/**
 * @class GXM.Button
 * The class that is used to construct a GXM Button.
 *
 * Sample code to create a GXM.Button that controls zooms in:
 *
 * create the GXM.Button:
 *     var btnZoomIn = Ext.create('GXM.Button', {
 *         control: new OpenLayers.Control.ZoomIn(),
 *         map: gxmMap,
 *         iconCls: 'add',
 *         iconMask: true,
 *         handler: function(){
 *             // implement Ext.Button handlers as usual
 *         }
 *      });
 *
 * The GXM.Button can be used e.g. in a toolbar that is docked to a panel:
 *      Ext.create('Ext.Panel', {
 *          items: [{
 *              xtype: 'toolbar',
 *              docked: 'top',
 *              items: [
 *                 btnZoomIn
 *              ]
 *          }]
 *      });
 */
Ext.define('GXM.Button', {

    extend: 'Ext.Button',

    requires: [
        'GXM.version'
    ],

    xtype: 'gxm_button',

    /**
     *  @private
     *  @property {Boolean} autoadded
     *  When a button instance is created with a control that did
     *  not belong to a map already we add it to the configured map. That also
     *  means that we are responsible for removing said control from the map in
     *  the case that this button is destroyed. This property tracks whether we
     *  added the control to a map.
     */
    autoadded: null,

    /**
     *  @private
     *  @property {Object} uScope
     *  The scope the user-defined button handler method shall be executed in.
     */
    uScope: null,

    /**
     *  @private
     *  @property {Function} uHandler
     *  The user-defined handler-method to invoke as handler for the button.
     */
    uHandler: null,

    /**
     *  @private
     *  @property {String} togllePressCls
     *  The CSS class name the button has in case of beeing pressed.
     */
    togglePressCls: 'x-button-pressed',

    /**
     *  @property {OpenLayers.Map} olMap
     *  The reference to the related OpenLayers Map object.
     *  This is for readonly use. To pass a map object to this class use the
     *  :attr:`map` config option.
     */
    olMap: null,

    // These do all get a getter-, setter- and applier-method

    config: {

        /**
         *  @cfg {OpenLayers.Control} control
         *  The control instance that this button shall work on.
         */
        control: null,

        /**
         *  @cfg {GXM.Map} map
         *  The GXM Map component this button belongs to. Might be used to
         *  derive the corresponding OpenLayers Map object.
         */
        map:  null,

        /**
         *  @cfg {String} exclusiveGroup
         *  An identifier for the exclusive group of GXM-Buttons that
         *  this button belongs to. This can be used to press/unpress buttons that
         *  would otherwise compete with each other.
         *
         *  An example would be the buttons to control the digitizing of geometries
         *  on the map where you would have a GXM.Button for points, linestrings and
         *  polygons. When these buttons share the same `exclusiveGroup` the pressed
         *  state will be managed automatically.
         */
        exclusiveGroup: null,

        /**
         *  @cfg {Boolean} pressed
         *  ``Boolean`` The initial state of this button.
         */
        pressed: false
    },

    /**
     *  @private
     *  The constructor function
     */
    constructor: function(config){
        // To not run into endless recursion we need to delete any
        // OpenLayers components that can be used to configure this
        // component.
        if (config && config.control) {
            this._control = config.control;
            delete config.control;
        }
        this.callParent(arguments);
    },

    /**
     *  @private
     *  Initializes the Component.
     */
    initialize: function(){
        this.callParent();

        if ( this.getMap() && this.getMap().getMap() ) {
            this.olMap = this.getMap().getMap();
        } else if ( this.getControl() && this.getControl().map ) {
            this.olMap = this.getControl().map;
        }

        // store the user scope and handlers
        this.uScope = this.getScope();
        this.uHandler = this.getHandler();

        this.setScope(this);
        this.setHandler(this.pHandler);

        if (this.getControl()) {
            if (this.olMap && !this.getControl().map ) {
                // add the control to the configured map
                this.olMap.addControl(this.getControl());
                // add internal flag to remove the control
                // from the map when this button is destroyed
                this.autoadded = true;
            } else {
                this.autoadded = false;
            }

            // register the handlers that sync the visual pressed
            // state with the controls active-state
            	this.getControl().events.on({
                    activate: this.onCtrlActivate,
                    deactivate: this.onCtrlDeactivate,
                    scope: this
                });
            // remove the control before we destroy the button
            // in case the control was not added to the map before
            	this.onBefore("destroy", function() {
                	if (this.autoadded) {
                		this.getControl().destroy();
                	}
                }, this);
        }
    },

    /**
     *  @private
     *  Intersects the :func:`setPressed` function. Adds the corresponding CSS class.
     */
    applyPressed: function(newPressedVal){
        if (this.getControl() && this.getControl().map) {
            if (newPressedVal === true) {
                this.addCls(this.togglePressCls);
                this.getControl().activate();
            } else {
                this.removeCls(this.togglePressCls);
                this.getControl().deactivate();
            }
        }
        return newPressedVal;
    },

    /**
     *  @private
     *  Called when the configured control is activated. Handles the
     *  deactivation of the other OpenLayers.Controls and the updating of the
     *  visual states (removal of `pressedCls`), but only when using a
     *  segmented button and an exclusiveGroup
     */
    onCtrlActivate: function(){
        if (this.getExclusiveGroup()) {
	        var exclusiveGroupMembers = this.getExclusiveGroupMembers();
	        var myId = this.id;
	        this._isDeactivating = true;
	        Ext.each(exclusiveGroupMembers, function(member) {
	            var elem = member.element;
	            if (myId !== member.id) {
	                member.setPressed(false);
	            }
	        });
	        this._isDeactivating = false;

	        this.setPressed(true);
        }
    },

    /**
     *  @private
     *  Called on control deactivation. Removes this button's `pressedCls`,
     *  but only when using a segmented button and an exclusiveGroup
     */
    onCtrlDeactivate: function(){
    	if (this.getExclusiveGroup()) {
	        if(!this._isDeactivating) {
	            this.setPressed(false);
	        }
    	}
    },

    /**
     *  @private
     *  The auto-defined handler-method to invoke as handler for
     *  the button. Handles the activation/deactivation or triggering of the
     *  configured OpenLayers.Control.
     */
    pHandler: function(){
        var ctrl = this.getControl();
        if(ctrl && ctrl.type === OpenLayers.Control.TYPE_BUTTON) {
            ctrl.trigger();
        } else {
            if ( !ctrl.active ) {
                ctrl.activate();
            } else {
                ctrl.deactivate();
            }
        }
        if(this.uHandler) {
            // adding the controls active state as last arg for the callback
            var args = Ext.toArray(arguments);
            args.push(ctrl.active);
            this.uHandler.apply(this.uScope, args);
        }
    },

    /**
     *  Returns an array of all members of the :attr:`exclusiveGroup` that this
     *  Button belongs to.
     *
     *  @return {GXM.Button[]} An array of all members of the :attr:`exclusiveGroup`
     *  that this Button belongs to.
     */
    getExclusiveGroupMembers: function(){
        var members = [];
        var myGroup = this.getExclusiveGroup();
        if (myGroup) {
            members = Ext.ComponentQuery.query('gxm_button[exclusiveGroup="' + myGroup + '"]')
        }
        return members;
    }

});
<commit_msg>Filter out config 'map' of GXM.Buttons.<commit_after>/*
 * Copyright (c) 2012 The Open Source Geospatial Foundation
 *
 * Published under the BSD license.
 *
 * See https://raw.github.com/geoext/GXM/master/license.txt for the full
 * text of the license.
 */

/**
 * @class GXM.Button
 * The class that is used to construct a GXM Button.
 *
 * Sample code to create a GXM.Button that controls zooms in:
 *
 * create the GXM.Button:
 *     var btnZoomIn = Ext.create('GXM.Button', {
 *         control: new OpenLayers.Control.ZoomIn(),
 *         map: gxmMap,
 *         iconCls: 'add',
 *         iconMask: true,
 *         handler: function(){
 *             // implement Ext.Button handlers as usual
 *         }
 *      });
 *
 * The GXM.Button can be used e.g. in a toolbar that is docked to a panel:
 *      Ext.create('Ext.Panel', {
 *          items: [{
 *              xtype: 'toolbar',
 *              docked: 'top',
 *              items: [
 *                 btnZoomIn
 *              ]
 *          }]
 *      });
 */
Ext.define('GXM.Button', {

    extend: 'Ext.Button',

    requires: [
        'GXM.version'
    ],

    xtype: 'gxm_button',

    /**
     *  @private
     *  @property {Boolean} autoadded
     *  When a button instance is created with a control that did
     *  not belong to a map already we add it to the configured map. That also
     *  means that we are responsible for removing said control from the map in
     *  the case that this button is destroyed. This property tracks whether we
     *  added the control to a map.
     */
    autoadded: null,

    /**
     *  @private
     *  @property {Object} uScope
     *  The scope the user-defined button handler method shall be executed in.
     */
    uScope: null,

    /**
     *  @private
     *  @property {Function} uHandler
     *  The user-defined handler-method to invoke as handler for the button.
     */
    uHandler: null,

    /**
     *  @private
     *  @property {String} togllePressCls
     *  The CSS class name the button has in case of beeing pressed.
     */
    togglePressCls: 'x-button-pressed',

    /**
     *  @property {OpenLayers.Map} olMap
     *  The reference to the related OpenLayers Map object.
     *  This is for readonly use. To pass a map object to this class use the
     *  :attr:`map` config option.
     */
    olMap: null,

    // These do all get a getter-, setter- and applier-method

    config: {

        /**
         *  @cfg {OpenLayers.Control} control
         *  The control instance that this button shall work on.
         */
        control: null,

        /**
         *  @cfg {GXM.Map} map
         *  The GXM Map component this button belongs to. Might be used to
         *  derive the corresponding OpenLayers Map object.
         */
        map:  null,

        /**
         *  @cfg {String} exclusiveGroup
         *  An identifier for the exclusive group of GXM-Buttons that
         *  this button belongs to. This can be used to press/unpress buttons that
         *  would otherwise compete with each other.
         *
         *  An example would be the buttons to control the digitizing of geometries
         *  on the map where you would have a GXM.Button for points, linestrings and
         *  polygons. When these buttons share the same `exclusiveGroup` the pressed
         *  state will be managed automatically.
         */
        exclusiveGroup: null,

        /**
         *  @cfg {Boolean} pressed
         *  ``Boolean`` The initial state of this button.
         */
        pressed: false
    },

    /**
     *  @private
     *  The constructor function
     */
    constructor: function(config){
        // To not run into endless recursion we need to delete any
        // OpenLayers components that can be used to configure this
        // component.
        if (config && config.control) {
            this._control = config.control;
            delete config.control;
        }
        if (config && config.map) {
            this._map = config.map;
            delete config.map;
        }
        this.callParent(arguments);
    },

    /**
     *  @private
     *  Initializes the Component.
     */
    initialize: function(){
        this.callParent();

        if ( this.getMap() && this.getMap().getMap() ) {
            this.olMap = this.getMap().getMap();
        } else if ( this.getControl() && this.getControl().map ) {
            this.olMap = this.getControl().map;
        }

        // store the user scope and handlers
        this.uScope = this.getScope();
        this.uHandler = this.getHandler();

        this.setScope(this);
        this.setHandler(this.pHandler);

        if (this.getControl()) {
            if (this.olMap && !this.getControl().map ) {
                // add the control to the configured map
                this.olMap.addControl(this.getControl());
                // add internal flag to remove the control
                // from the map when this button is destroyed
                this.autoadded = true;
            } else {
                this.autoadded = false;
            }

            // register the handlers that sync the visual pressed
            // state with the controls active-state
            	this.getControl().events.on({
                    activate: this.onCtrlActivate,
                    deactivate: this.onCtrlDeactivate,
                    scope: this
                });
            // remove the control before we destroy the button
            // in case the control was not added to the map before
            	this.onBefore("destroy", function() {
                	if (this.autoadded) {
                		this.getControl().destroy();
                	}
                }, this);
        }
    },

    /**
     *  @private
     *  Intersects the :func:`setPressed` function. Adds the corresponding CSS class.
     */
    applyPressed: function(newPressedVal){
        if (this.getControl() && this.getControl().map) {
            if (newPressedVal === true) {
                this.addCls(this.togglePressCls);
                this.getControl().activate();
            } else {
                this.removeCls(this.togglePressCls);
                this.getControl().deactivate();
            }
        }
        return newPressedVal;
    },

    /**
     *  @private
     *  Called when the configured control is activated. Handles the
     *  deactivation of the other OpenLayers.Controls and the updating of the
     *  visual states (removal of `pressedCls`), but only when using a
     *  segmented button and an exclusiveGroup
     */
    onCtrlActivate: function(){
        if (this.getExclusiveGroup()) {
	        var exclusiveGroupMembers = this.getExclusiveGroupMembers();
	        var myId = this.id;
	        this._isDeactivating = true;
	        Ext.each(exclusiveGroupMembers, function(member) {
	            var elem = member.element;
	            if (myId !== member.id) {
	                member.setPressed(false);
	            }
	        });
	        this._isDeactivating = false;

	        this.setPressed(true);
        }
    },

    /**
     *  @private
     *  Called on control deactivation. Removes this button's `pressedCls`,
     *  but only when using a segmented button and an exclusiveGroup
     */
    onCtrlDeactivate: function(){
    	if (this.getExclusiveGroup()) {
	        if(!this._isDeactivating) {
	            this.setPressed(false);
	        }
    	}
    },

    /**
     *  @private
     *  The auto-defined handler-method to invoke as handler for
     *  the button. Handles the activation/deactivation or triggering of the
     *  configured OpenLayers.Control.
     */
    pHandler: function(){
        var ctrl = this.getControl();
        if(ctrl && ctrl.type === OpenLayers.Control.TYPE_BUTTON) {
            ctrl.trigger();
        } else {
            if ( !ctrl.active ) {
                ctrl.activate();
            } else {
                ctrl.deactivate();
            }
        }
        if(this.uHandler) {
            // adding the controls active state as last arg for the callback
            var args = Ext.toArray(arguments);
            args.push(ctrl.active);
            this.uHandler.apply(this.uScope, args);
        }
    },

    /**
     *  Returns an array of all members of the :attr:`exclusiveGroup` that this
     *  Button belongs to.
     *
     *  @return {GXM.Button[]} An array of all members of the :attr:`exclusiveGroup`
     *  that this Button belongs to.
     */
    getExclusiveGroupMembers: function(){
        var members = [];
        var myGroup = this.getExclusiveGroup();
        if (myGroup) {
            members = Ext.ComponentQuery.query('gxm_button[exclusiveGroup="' + myGroup + '"]')
        }
        return members;
    }

});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { mapProps } from 'recompose';
import Snake from './Snake';
import Candy from './Candy';
import GameState from './utils/GameState';
import pointShape from './pointShape';
import './Board.css';

const Message = ({ text }) => (

{text}

);

const addSize = mapProps(({ ...props, width, height }) => {
  const w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
  const h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
  return { ...props, size: Math.min(16, ~~Math.min(w / width, h / height)) };
});

const Board = ({ snake, candy, state, width, height, score, current, direction, size }) => (

Score: {score}

      <Snake shape={snake} size={size} direction={direction}/>
      <Candy point={candy} size={size}/>
      {state === GameState.loaded ? <Message text="Swipe or Press any arrow to move"/> : null}
      {state === GameState.ended ? <Message text="GAME ENDED"/> : null}

);

Board.propTypes = {
  snake: PropTypes.arrayOf(pointShape).isRequired,
  candy: pointShape.isRequired,
  width: PropTypes.number.isRequired,
  height: PropTypes.number.isRequired,
};

export default addSize(Board);
<commit_msg>fix screen size<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { mapProps } from 'recompose';
import Snake from './Snake';
import Candy from './Candy';
import GameState from './utils/GameState';
import pointShape from './pointShape';
import './Board.css';

const Message = ({ text }) => (

{text}

);

const addSize = mapProps(({ ...props, width, height }) => {
  const w = Math.min(document.documentElement.clientWidth, window.innerWidth || Infinity);
  const h = Math.min(document.documentElement.clientHeight, window.innerHeight || Infinity);
  return { ...props, size: Math.min(16, ~~Math.min(w / width, h / height)) };
});

const Board = ({ snake, candy, state, width, height, score, current, direction, size }) => (

Score: {score}

      <Snake shape={snake} size={size} direction={direction}/>
      <Candy point={candy} size={size}/>
      {state === GameState.loaded ? <Message text="Swipe or Press any arrow to move"/> : null}
      {state === GameState.ended ? <Message text="GAME ENDED"/> : null}

);

Board.propTypes = {
  snake: PropTypes.arrayOf(pointShape).isRequired,
  candy: pointShape.isRequired,
  width: PropTypes.number.isRequired,
  height: PropTypes.number.isRequired,
};

export default addSize(Board);
<|endoftext|>


<commit_before>'use strict';

var Ship = require('./Ship');

class Fleet {
    constructor () {
        this._ships = [];
    }

    /**
     * @param {Ship} ship
     * @returns {Fleet}
     */
    addShip (ship) {
        this._ships.push(ship);

        return this;
    }

    getState() {
        var state = Ship.STATE.HEALTHY;
        this._ships.forEach(ship => {
            state = Math.max(ship.getState(), state);
        });

        return state;
    }

    getShipAtPosition(x, y) {
        var targetShip = null;
        this._ships.some(ship => {
            return ship.getFields().some(field => {
                if (field.getX() === x && field.getY() === y) {
                    targetShip = ship;
                    return true;
                }

                return false;
            })
        });

        return targetShip;
    }
}

module.exports = Fleet;<commit_msg>be able to create a random fleet with pre-defined ship sizes<commit_after>'use strict';

var _ = require('lodash');
var Ship = require('./Ship');
var Field = require('./Field');

class Fleet {

    /**
     * @param {Grid} grid
     * @param shipSizes
     */
    static createRandomFleetOnGrid(grid, shipSizes) {
        var fleet = new Fleet();

        shipSizes.forEach(shipSize => {
            var possibleAreas = [];

            // checking if the complete area is free
            grid.getFieldsByState(Field.STATE.UNKNOWN)
                .forEach(field => {
                    var horizontal = grid.getAreaFields(field.getX(), field.getY(),shipSize, 1);
                    var vertical = grid.getAreaFields(field.getX(), field.getY(), 1, shipSize);

                    if (Field.filterByState(horizontal, Field.STATE.UNKNOWN).length === shipSize) {
                        possibleAreas.push({
                            x: field.getX(),
                            y: field.getY(),
                            width: shipSize,
                            height: 1
                        });
                    }

                    if (Field.filterByState(vertical, Field.STATE.UNKNOWN).length === shipSize) {
                        possibleAreas.push({
                            x: field.getX(),
                            y: field.getY(),
                            width: 1,
                            height: shipSize
                        });
                    }
                });

            // verify that there is no neighbor ship
            possibleAreas = possibleAreas.filter(area => {
                return !grid.getAreaNeighborsWithDiagonal(area.x, area.y, area.width, area.height).some(neighbor => {
                    return !!fleet.getShipAtPosition(neighbor.getX(), neighbor.getY());
                });
            });

            if (!possibleAreas.length) {
                throw new Error('unable to place all ships on the battlefield');
            }

            // choose some random area and place the ship
            var position = _.shuffle(possibleAreas).pop();
            fleet.addShip(new Ship(grid.getAreaFields(position.x, position.y, position.width, position.height)));
        });

        return fleet;

    }

    constructor () {
        this._ships = [];
    }

    /**
     * @param {Ship} ship
     * @returns {Fleet}
     */
    addShip (ship) {
        this._ships.push(ship);

        return this;
    }

    getState() {
        var state = Ship.STATE.HEALTHY;
        this._ships.forEach(ship => {
            state = Math.max(ship.getState(), state);
        });

        return state;
    }

    getShipAtPosition(x, y) {
        var targetShip = null;
        this._ships.some(ship => {
            return ship.getFields().some(field => {
                if (field.getX() === x && field.getY() === y) {
                    targetShip = ship;
                    return true;
                }

                return false;
            })
        });

        return targetShip;
    }
}

module.exports = Fleet;<|endoftext|>


<commit_before>(function(exports) {

var MapReader = typeof require === 'undefined' ? MazeGame.MapReader : require('./MapReader.js').MapReader;
var linearToXY = typeof require === 'undefined' ? MazeGame.linearToXY : require('./MapReader.js').linearToXY;
var xyToLinear = typeof require === 'undefined' ? MazeGame.xyToLinear : require('./MapReader.js').xyToLinear;

var DOOR_TIME = 10;

var GameRunner = function(displayer, raw_data) {
	var self = this;
	var reader_ = new MapReader(raw_data);
	var displayer_ = displayer;

	var map_ = reader_.getMap();
	var mapping_button_to_id_ = undefined;
	var mapping_door_to_id_ = undefined;
	var mapping_id_to_doors_ = undefined;
	var doors_status_ = {};

	var num_moves_ = 0;
	var pos_x_, pos_y_;
	var end_x_, end_y_;

	this.getStart = function() {
		return reader_.getStart();
	};

	this.getEnd = function() {
		return reader_.getEnd();
	};

	var refreshDoors = function(group_id) {
		var value = doors_status_[group_id] > 0 ? 'r' : 'd';
		var doors = mapping_id_to_doors_[group_id];
		for (var i = 0 ; i < doors.length ; ++i) {
			var xy = linearToXY(doors[i], reader_.getSizeX());
			var x = xy[0];
			var y = xy[1];
			displayer_.displayCell(x, y, value, group_id);
		}
	}

	this.move = function(direction) {
		var next_x = pos_x_;
		var next_y = pos_y_;

		switch (direction) {
			case 'left':
				next_x -= 1;
				break;
			case 'right':
				next_x += 1;
				break;
			case 'up':
				next_y -= 1;
				break;
			case 'down':
				next_y += 1;
				break;
		}

		if (next_x < 0 || next_x >= reader_.getSizeX() || next_y < 0 || next_y >= reader_.getSizeY()) {
			return false;
		}

		switch (map_[next_y][next_x]) {
			case '#':
				return false;
			case 'b':
				var group_id = mapping_button_to_id_[next_y][next_x];
				if (doors_status_[group_id] !== undefined) {
					doors_status_[group_id] = DOOR_TIME +1; // +1 because decreased just after
					refreshDoors(group_id);
				}
				break;
			case 'd':
				var group_id = mapping_door_to_id_[next_y][next_x];
				if (doors_status_[group_id] == 0) {
					return false;
				}
				break;
		}

		// Override previous drawing of the character

		switch (map_[pos_y_][pos_x_]) {
			case 'b':
				displayer.displayCell(pos_x_, pos_y_, 'b', mapping_button_to_id_[pos_y_][pos_x_]);
				break;
			case 'd':
				var group_id = mapping_door_to_id_[pos_y_][pos_x_];
				displayer.displayCell(pos_x_, pos_y_, doors_status_[group_id] == 0 ? 'd' : 'r', group_id);
				break;
			default:
				displayer.displayCell(pos_x_, pos_y_, map_[pos_y_][pos_x_]);
				break;
		}

		// Update doors

		var keys = Object.keys(doors_status_);
		for (var i = 0 ; i < keys.length ; ++i) {
			var key = keys[i];
			if (doors_status_[key] > 0) {
				doors_status_[key] -= 1;
				if (doors_status_[key] == 0) {
					refreshDoors(key);
				}
			}
		}

		// Draw characters

		displayer.displayCharacter(next_x, next_y);

		num_moves_ += 1;
		pos_x_ = next_x;
		pos_y_ = next_y;
		return true;
	};

	this.restart = function() {
		num_moves_ = 0;

		var xy = linearToXY(reader_.getStart(), reader_.getSizeX());
		pos_x_ = xy[0];
		pos_y_ = xy[1];

		xy = linearToXY(reader_.getEnd(), reader_.getSizeX());
		end_x_ = xy[0];
		end_y_ = xy[1];

		var keys = Object.keys(doors_status_);
		for (var i = 0 ; i < keys.length ; ++i) {
			doors_status_[keys[i]] = 0;
		}

		displayer_.display(map_, mapping_button_to_id_, mapping_door_to_id_);
		displayer_.displayCharacter(pos_x_, pos_y_);
	};

	var revampMapping = function(raw_mapping) {
		var mapping = {};
		var keys = Object.keys(raw_mapping);
		for (var i = 0 ; i < keys.length ; ++i) {
			var key = keys[i];
			var xy = linearToXY(key, reader_.getSizeX());
			var x = xy[0];
			var y = xy[1];
			if (mapping[y] === undefined) {
				mapping[y] = {};
			}
			mapping[y][x] = raw_mapping[key];
		}
		return mapping;
	};

	{
		map_ = reader_.getMap();
		mapping_button_to_id_ = revampMapping(reader_.getMappingButtonId());
		mapping_door_to_id_ = revampMapping(reader_.getMappingDoorId());
		mapping_id_to_doors_ = reader_.getMappingIdDoors();

		var keys = Object.keys(mapping_id_to_doors_);
		for (var i = 0 ; i < keys.length ; ++i) {
			doors_status_[keys[i]] = 0;
		}

		this.restart();
	}
};

exports.GameRunner = GameRunner;
}(typeof exports === 'undefined'
		? (this['MazeGame'] === undefined ? this['MazeGame']={} : this['MazeGame'])
		: exports));
<commit_msg>Adapt GameRunner to work with reversed doors<commit_after>(function(exports) {

var MapReader = typeof require === 'undefined' ? MazeGame.MapReader : require('./MapReader.js').MapReader;
var linearToXY = typeof require === 'undefined' ? MazeGame.linearToXY : require('./MapReader.js').linearToXY;
var xyToLinear = typeof require === 'undefined' ? MazeGame.xyToLinear : require('./MapReader.js').xyToLinear;

var DOOR_TIME = 10;

var GameRunner = function(displayer, raw_data) {
	var self = this;
	var reader_ = new MapReader(raw_data);
	var displayer_ = displayer;

	var map_ = reader_.getMap();
	var mapping_button_to_id_ = undefined;
	var mapping_door_to_id_ = undefined;
	var mapping_id_to_doors_ = undefined;
	var mapping_reversed_to_id_ = undefined;
	var mapping_id_to_reversed_ = undefined;
	var doors_status_ = {};

	var num_moves_ = 0;
	var pos_x_, pos_y_;
	var end_x_, end_y_;

	this.getStart = function() {
		return reader_.getStart();
	};

	this.getEnd = function() {
		return reader_.getEnd();
	};

	var refreshDoors = function(group_id) {
		var value = doors_status_[group_id] > 0 ? 'r' : 'd';
		var doors = mapping_id_to_doors_[group_id];
		if (doors !== undefined) {
			for (var i = 0 ; i < doors.length ; ++i) {
				var xy = linearToXY(doors[i], reader_.getSizeX());
				var x = xy[0];
				var y = xy[1];
				displayer_.displayCell(x, y, value, group_id);
			}
		}
		value =  value == 'r' ? 'd' : 'r';
		var reversed = mapping_id_to_reversed_[group_id];
		if (reversed !== undefined) {
			for (var i = 0 ; i < reversed.length ; ++i) {
				var xy = linearToXY(reversed[i], reader_.getSizeX());
				var x = xy[0];
				var y = xy[1];
				displayer_.displayCell(x, y, value, group_id);
			}
		}
	}

	this.move = function(direction) {
		var next_x = pos_x_;
		var next_y = pos_y_;

		switch (direction) {
			case 'left':
				next_x -= 1;
				break;
			case 'right':
				next_x += 1;
				break;
			case 'up':
				next_y -= 1;
				break;
			case 'down':
				next_y += 1;
				break;
		}

		if (next_x < 0 || next_x >= reader_.getSizeX() || next_y < 0 || next_y >= reader_.getSizeY()) {
			return false;
		}

		switch (map_[next_y][next_x]) {
			case '#':
				return false;
			case 'b':
				var group_id = mapping_button_to_id_[next_y][next_x];
				if (doors_status_[group_id] !== undefined) {
					doors_status_[group_id] = DOOR_TIME +1; // +1 because decreased just after
					refreshDoors(group_id);
				}
				break;
			case 'd':
				var group_id = mapping_door_to_id_[next_y][next_x];
				if (doors_status_[group_id] <= 0) {
					return false;
				}
				break;
			case 'r':
				var group_id = mapping_reversed_to_id_[next_y][next_x];
				if (doors_status_[group_id] > 0) {
					return false;
				}
				break;
		}

		// Override previous drawing of the character

		switch (map_[pos_y_][pos_x_]) {
			case 'b':
				displayer.displayCell(pos_x_, pos_y_, 'b', mapping_button_to_id_[pos_y_][pos_x_]);
				break;
			case 'd':
				var group_id = mapping_door_to_id_[pos_y_][pos_x_];
				displayer.displayCell(pos_x_, pos_y_, doors_status_[group_id] <= 0 ? 'd' : 'r', group_id);
				break;
			case 'r':
				var group_id = mapping_reversed_to_id_[pos_y_][pos_x_];
				displayer.displayCell(pos_x_, pos_y_, doors_status_[group_id] <= 0 ? 'r' : 'd', group_id);
				break;
			default:
				displayer.displayCell(pos_x_, pos_y_, map_[pos_y_][pos_x_]);
				break;
		}

		// Update doors

		var keys = Object.keys(doors_status_);
		for (var i = 0 ; i < keys.length ; ++i) {
			var key = keys[i];
			if (doors_status_[key] > 0) {
				doors_status_[key] -= 1;
				if (doors_status_[key] == 0) {
					refreshDoors(key);
				}
			}
		}

		// Draw characters

		displayer.displayCharacter(next_x, next_y);

		num_moves_ += 1;
		pos_x_ = next_x;
		pos_y_ = next_y;
		return true;
	};

	this.restart = function() {
		num_moves_ = 0;

		var xy = linearToXY(reader_.getStart(), reader_.getSizeX());
		pos_x_ = xy[0];
		pos_y_ = xy[1];

		xy = linearToXY(reader_.getEnd(), reader_.getSizeX());
		end_x_ = xy[0];
		end_y_ = xy[1];

		var keys = Object.keys(doors_status_);
		for (var i = 0 ; i < keys.length ; ++i) {
			doors_status_[keys[i]] = 0;
		}

		displayer_.display(map_, mapping_button_to_id_, mapping_door_to_id_, mapping_reversed_to_id_);
		displayer_.displayCharacter(pos_x_, pos_y_);
	};

	var revampMapping = function(raw_mapping) {
		var mapping = {};
		var keys = Object.keys(raw_mapping);
		for (var i = 0 ; i < keys.length ; ++i) {
			var key = keys[i];
			var xy = linearToXY(key, reader_.getSizeX());
			var x = xy[0];
			var y = xy[1];
			if (mapping[y] === undefined) {
				mapping[y] = {};
			}
			mapping[y][x] = raw_mapping[key];
		}
		return mapping;
	};

	{
		map_ = reader_.getMap();
		mapping_button_to_id_ = revampMapping(reader_.getMappingButtonId());
		mapping_door_to_id_ = revampMapping(reader_.getMappingDoorId());
		mapping_id_to_doors_ = reader_.getMappingIdDoors();
		mapping_reversed_to_id_ = revampMapping(reader_.getMappingReversedId());
		mapping_id_to_reversed_ = reader_.getMappingIdReversed();

		var keys = Object.keys(mapping_id_to_doors_);
		for (var i = 0 ; i < keys.length ; ++i) {
			doors_status_[keys[i]] = 0;
		}
		keys = Object.keys(mapping_id_to_reversed_);
		for (var i = 0 ; i < keys.length ; ++i) {
			doors_status_[keys[i]] = 0;
		}

		this.restart();
	}
};

exports.GameRunner = GameRunner;
}(typeof exports === 'undefined'
		? (this['MazeGame'] === undefined ? this['MazeGame']={} : this['MazeGame'])
		: exports));

<|endoftext|>


<commit_before>import React from "react";

import EventComponent from "./internals/EventComponent";
/*eslint-disable no-unused-vars */
import VirtualContainer from "./internals/VirtualContainer";
/*eslint-enable no-unused-vars */
import exposeGetters from "./internals/exposeGetters";
import createRegisterEvents from "./internals/createRegisterEvents";

const {PropTypes} = React;

class GoogleMaps extends EventComponent {
  /*
   * Some public API we'd like to expose
   */
  panBy (x, y) {
    const {instance} = this.state;
    if (instance) {
      instance.panBy(x, y);
    }
  }

  panTo (latLng) {
    const {instance} = this.state;
    if (instance) {
      instance.panTo(latLng);
    }
  }

  panToBounds (latLngBounds) {
    const {instance} = this.state;
    if (instance) {
      instance.panToBounds(latLngBounds);
    }
  }

  fitBounds (latLngBounds) {
    const {instance} = this.state;
    if (instance) {
      instance.fitBounds(latLngBounds);
    }
  }
  /*
   * Internals
   */
  constructor (...args) {
    super(...args);
    this.state = {};
  }

  _createOrUpdateInstance () {
    const {props} = this;
    if (!props.googleMapsApi) {
      return;
    }
    // googleMapsApi can be async loaded
    /*eslint-disable no-unused-vars */
    const {containerProps, googleMapsApi, ...googleMapsConfig} = props;
    /*eslint-enable no-unused-vars */
    var {instance} = this.state;

    if (instance) {
      instance.setOptions(googleMapsConfig);
    } else {
      const GoogleMapsClass = googleMapsApi.Map;
      instance = new GoogleMapsClass(
        React.findDOMNode(this.refs.googleMaps),
        googleMapsConfig
      );
      exposeGetters(this, GoogleMapsClass.prototype, instance);

      this.setState({instance});
    }
    return instance;
  }

  render () {
    const {props} = this;

    return (
      <div {...props.containerProps} ref="googleMaps">
        { this._render_virtual_container_() }
      </div>
    );
  }

  _render_virtual_container_ () {
    const {props} = this,
          {googleMapsApi, children} = props,
          {instance} = this.state;
    if (!googleMapsApi || !instance) {
      return;
    }
    return (
      <VirtualContainer
        googleMapsApi={googleMapsApi}
        map={instance}>
        {children}
      </VirtualContainer>
    );
  }

}

GoogleMaps.propTypes = {
  ...EventComponent.propTypes,
  containerProps: PropTypes.object.isRequired,
};

GoogleMaps._registerEvents = createRegisterEvents(
  "bounds_changed center_changed click dblclick drag dragend dragstart heading_changed idle maptypeid_changed mousemove mouseout mouseover projection_changed resize rightclick tilesloaded tilt_changed zoom_changed"
);

export default GoogleMaps;
<commit_msg>feat(GoogleMaps): specify map bounds via declarative props.bounds<commit_after>import React from "react";

import EventComponent from "./internals/EventComponent";
/*eslint-disable no-unused-vars */
import VirtualContainer from "./internals/VirtualContainer";
/*eslint-enable no-unused-vars */
import exposeGetters from "./internals/exposeGetters";
import createRegisterEvents from "./internals/createRegisterEvents";

const {PropTypes} = React;

class GoogleMaps extends EventComponent {
  /*
   * Some public API we'd like to expose
   */
  panBy (x, y) {
    const {instance} = this.state;
    if (instance) {
      instance.panBy(x, y);
    }
  }

  panTo (latLng) {
    const {instance} = this.state;
    if (instance) {
      instance.panTo(latLng);
    }
  }

  panToBounds (latLngBounds) {
    const {instance} = this.state;
    if (instance) {
      instance.panToBounds(latLngBounds);
    }
  }

  fitBounds (latLngBounds) {
    const {instance} = this.state;
    if (instance) {
      instance.fitBounds(latLngBounds);
    }
  }
  /*
   * Internals
   */
  constructor (...args) {
    super(...args);
    this.state = {};
  }

  _createOrUpdateInstance () {
    const {props} = this;
    if (!props.googleMapsApi) {
      return;
    }
    // googleMapsApi can be async loaded
    /*eslint-disable no-unused-vars */
    const {containerProps, googleMapsApi, bounds, ...googleMapsConfig} = props;
    /*eslint-enable no-unused-vars */
    var {instance} = this.state;

    if (bounds) {
      delete googleMapsConfig.zoom;
      delete googleMapsConfig.center;
    }

    if (instance) {
      instance.setOptions(googleMapsConfig);
    } else {
      const GoogleMapsClass = googleMapsApi.Map;
      instance = new GoogleMapsClass(
        React.findDOMNode(this.refs.googleMaps),
        googleMapsConfig
      );
      exposeGetters(this, GoogleMapsClass.prototype, instance);

      this.setState({instance});
    }

    if (bounds) {
      instance.fitBounds(bounds);
    }

    return instance;
  }

  render () {
    const {props} = this;

    return (
      <div {...props.containerProps} ref="googleMaps">
        { this._render_virtual_container_() }
      </div>
    );
  }

  _render_virtual_container_ () {
    const {props} = this,
          {googleMapsApi, children} = props,
          {instance} = this.state;
    if (!googleMapsApi || !instance) {
      return;
    }
    return (
      <VirtualContainer
        googleMapsApi={googleMapsApi}
        map={instance}>
        {children}
      </VirtualContainer>
    );
  }

}

GoogleMaps.propTypes = {
  ...EventComponent.propTypes,
  containerProps: PropTypes.object.isRequired,
  bounds: React.PropTypes.object
};

GoogleMaps._registerEvents = createRegisterEvents(
  "bounds_changed center_changed click dblclick drag dragend dragstart heading_changed idle maptypeid_changed mousemove mouseout mouseover projection_changed resize rightclick tilesloaded tilt_changed zoom_changed"
);

export default GoogleMaps;
<|endoftext|>


<commit_before>//@flow

import request from 'request';

function isSuccess(code: number): boolean {
  return (code - (code % 200)) === 200;
}

export class ClientError extends Error {
  body: Object;
  response: Object;
  constructor(response: Object, body: Object) {
    super(`Error in response: ${response.statusCode}`);
    this.response = response;
    this.body = body;
  }
}

const DEFAULT_HEADERS = {
  'Accept': 'application/json, */*',
  'User-Agent': 'node-k8s-client'
};

export default class HttpClient {

  _baseUrl: string;
  _ca: ?string;
  _authToken: ?string;

  constructor(host: string, protocol: string = 'https', port?: number, ca?: string, token?: string) {
    const actualPort = port || (protocol === 'https' ? 443 : 80);
    this._baseUrl = `${protocol}://${host}:${actualPort}`;
    this._ca = ca;
    this._authToken = token;
  }

  request(method: string, path: string, opts: { body?: any, headers?: Object, query?: Object } = {}): Promise<Object> {

    const req = {
      method: method,
      url: `${this._baseUrl}${path}`,
      json: true,
      body: opts.body,
      qs: opts.query,
      ca: this._ca,
      headers: Object.assign(DEFAULT_HEADERS, opts.headers || {}),
      auth: this._authToken ? { bearer: this._authToken } : undefined
    };

//    console.log("Sending request:", JSON.stringify(req, null, 2));
//    console.log("Sending request:", req.method, req.url);
    return new Promise( (resolve, reject) => {
      request(req, (err, resp, body) => {
        if(err) {
          return reject(err);
        }
        if(!isSuccess(resp.statusCode)) {
          return reject(new ClientError(resp, body));
        }
        resolve(body || {});
      });
    });

  }

}

<commit_msg>Add serviceaccount token discovery.<commit_after>//@flow

import request from 'request';
import fs from 'fs';

function isSuccess(code: number): boolean {
  return (code - (code % 200)) === 200;
}

export class ClientError extends Error {
  body: Object;
  response: Object;
  constructor(response: Object, body: Object) {
    super(`Error in response: ${response.statusCode}`);
    this.response = response;
    this.body = body;
  }
}

const SERVICE_ACCOUNT_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token";
const SERVICE_ACCOUNT_CA_CRT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";

const DEFAULT_HEADERS = {
  'Accept': 'application/json, */*',
  'User-Agent': 'node-k8s-client'
};

export default class HttpClient {

  _baseUrl: string;
  _ca: ?string;
  _authToken: ?string;

  constructor(host: string, protocol: string = 'https', port?: number, ca?: string, token?: string) {
    const actualPort = port || (protocol === 'https' ? 443 : 80);
    this._baseUrl = `${protocol}://${host}:${actualPort}`;
    this._ca = ca || this._readOptionalFile(SERVICE_ACCOUNT_CA_CRT_PATH);
    this._authToken = token || this._readOptionalFile(SERVICE_ACCOUNT_TOKEN_PATH);
  }

  _readOptionalFile(path: string): ?string {
    try {
      return fs.readFileSync(path, 'utf8');
    } catch(err) {
      return null;
    }
  }

  request(method: string, path: string, opts: { body?: any, headers?: Object, query?: Object } = {}): Promise<Object> {

    const req = {
      method: method,
      url: `${this._baseUrl}${path}`,
      json: true,
      body: opts.body,
      qs: opts.query,
      ca: this._ca,
      headers: Object.assign(DEFAULT_HEADERS, opts.headers || {}),
      auth: this._authToken ? { bearer: this._authToken } : undefined
    };

    return new Promise( (resolve, reject) => {
      request(req, (err, resp, body) => {
        if(err) {
          return reject(err);
        }
        if(!isSuccess(resp.statusCode)) {
          return reject(new ClientError(resp, body));
        }
        resolve(body || {});
      });
    });

  }

}

<|endoftext|>


<commit_before>/**
 * This Source Code is licensed under the MIT license. If a copy of the
 * MIT-license was not distributed with this file, You can obtain one at:
 * http://opensource.org/licenses/mit-license.html.
 *
 * @author: Hein Rutjes (IjzerenHein)
 * @license MIT
 * @copyright Gloey Apps, 2014
 */

/**
 * Internal LayoutNode class used by `LayoutController`.
 *
 * @module
 */
define(function (require, exports, module) {

    var Transitionable = require('famous/transitions/Transitionable');
    var Transform = require('famous/core/Transform');
    var LayoutUtility = require('./LayoutUtility');

    /**
     * @class
     * @param {Object} renderNode Render-node which this layout-node represents
     * @alias module:LayoutNode
     */
    function LayoutNode(renderNode, spec) {
        this.renderNode = renderNode;
        this._spec = spec ? LayoutUtility.cloneSpec(spec) : {};
        this._spec.renderNode = renderNode; // also store in spec
        this._specModified = true;
        this._invalidated = false;
        this._removing = false;
    }

    /**
     * Called to update the underlying render-node
     */
    LayoutNode.prototype.setRenderNode = function (renderNode) {
        this.renderNode = renderNode;
        this._spec.renderNode = renderNode;
    };

    /**
     * Called to update the options for the node
     */
    LayoutNode.prototype.setOptions = function (options) {
        // override to implement
    };

    /**
     * Called when the node is destroyed
     */
    LayoutNode.prototype.destroy = function () {
        this.renderNode = undefined;
        this._spec.renderNode = undefined;
        this._viewSequence = undefined;
    };

    /**
     * Reset the end-state. This function is called on all layout-nodes prior to
     * calling the layout-function. So that the layout-function starts with a clean slate.
     */
    LayoutNode.prototype.reset = function () {
        this._invalidated = false;
        this.trueSizeRequested = false;
    };

    /**
     * Set the spec of the node
     *
     * @param {Object} spec
     */
    LayoutNode.prototype.setSpec = function (spec) {
        this._specModified = true;
        if (spec.align) {
            if (!spec.align) {
                this._spec.align = [0, 0];
            }
            this._spec.align[0] = spec.align[0];
            this._spec.align[1] = spec.align[1];
        }
        else {
            this._spec.align = undefined;
        }
        if (spec.origin) {
            if (!spec.origin) {
                this._spec.origin = [0, 0];
            }
            this._spec.origin[0] = spec.origin[0];
            this._spec.origin[1] = spec.origin[1];
        }
        else {
            this._spec.origin = undefined;
        }
        if (spec.size) {
            if (!spec.size) {
                this._spec.size = [0, 0];
            }
            this._spec.size[0] = spec.size[0];
            this._spec.size[1] = spec.size[1];
        }
        else {
            this._spec.size = undefined;
        }

        this._spec.hide = spec.hide;

        if (spec.transform) {
            if (!spec.transform) {
                this._spec.transform = spec.transform.slice(0);
            }
            else {
                for (var i = 0; i < 16; i++) {
                    this._spec.transform[i] = spec.transform[i];
                }
            }
        }
        else {
            this._spec.transform = undefined;
        }
        this._spec.opacity = spec.opacity;
    };

    /**
     * Initiates a transition on the layout node
     * @param transition
     * @param {Function} onComplete callback when complete
     */
    LayoutNode.prototype.transition = function (transition, onComplete) {

        this._originalSet = this._transitionable ?
            /* If there's already something in progress, then interpolate the current starting position */
            this._interpolateSet() :
            /* Otherwise, it's the most recent set */
            this._lastSet;
        var transitionable = this._transitionable = new Transitionable(0);
        if(!transition.duration){
            transition.duration = 200;
        }
        transitionable.set(1, transition, function() {
            /* If the transitionable wasn't replaced by another transitionable, then delete the transitionable
            * because we are finished */
            if (this._transitionable === transitionable) {
                delete this._transitionable;
                this._originalSet = this._targetSet;
                if(onComplete){
                    onComplete();
                }
            }
        })
    };

    /**
     * Set the content of the node
     *
     * @param {Object} set
     */
    LayoutNode.prototype.set = function (set, size) {

        var transition = set.transition;

        if (transition) {
            this._targetSet = set;
            if (!this._transitionable) {
                var transitionable = this._transitionable = new Transitionable(0);
                transitionable.set(1, transition, function()  {
                    /* If the transitionable wasn't replaced by another transitionable, then delete the transitionable
                    * because we are finished */
                    if (this._transitionable === transitionable) {
                        delete this._transitionable;
                        this._originalSet = this._targetSet;
                    }
                })
            }
        }
        if (!this._transitionable) {
            this._originalSet = set;
        }

        this._lastSet = set;
        this._invalidated = true;
        this._specModified = true;
        this._removing = false;
        this._modifySpecFromSet(this._spec, set);
        this.scrollLength = set.scrollLength;
    };

    var propertiesWithDefaults = {
        size: [undefined, undefined],
        origin: [0, 0],
        align: [0, 0],
        skew: [0, 0, 0],
        rotate: [0, 0, 0],
        scale: [1, 1, 1],
        translate: [0, 0, 0],
        opacity: [1]
    };

    LayoutNode.prototype._modifySpecFromSet = function (spec, set) {
        spec.opacity = set.opacity;

        var propertiesWithEasyDefaults = ['size', 'origin', 'align'];
        for (var i = 0; i < propertiesWithEasyDefaults.length; i++) {
            var propertyName = propertiesWithEasyDefaults[i];
            var defaultProperty = propertiesWithDefaults[propertyName];
            spec[propertyName] = set[propertyName] ? [
                set[propertyName][0] === undefined ? defaultProperty[0] : set[propertyName][0],
                set[propertyName][1] === undefined ? defaultProperty[1] : set[propertyName][1]
            ] : undefined;
        }

        spec.hide = set.hide;

        if (set.skew || set.rotate || set.scale) {
            spec.transform = Transform.build({
                translate: set.translate || [0, 0, 0],
                skew: set.skew || [0, 0, 0],
                scale: set.scale || [1, 1, 1],
                rotate: set.rotate || [0, 0, 0]
            });
        }
        else {
            spec.transform = set.translate ?
                Transform.translate(set.translate[0], set.translate[1], set.translate[2]) :
                undefined;
        }
    };

    /**
     * Creates the render-spec
     */
    LayoutNode.prototype.getSpec = function () {
        this._specModified = false;
        this._spec.removed = !this._invalidated;
        return this._getFinalSpec();
    };

    /**
     * Gets the calculated spec based on the time passed, or what the last setting was
     * @returns {*}
     * @private
     */
    LayoutNode.prototype._getFinalSpec = function () {
        if (this._transitionable && !this._spec.hide) {
            if (!this._transitionable.isActive()) {
                delete this._transitionable;
            }
            var spec = {renderNode: this._spec.renderNode};
            this._modifySpecFromSet(spec, this._interpolateSet());
            return spec;
        }

        return this._spec;

    };

    /**
     * Interpolates an ongoing animation
     * @returns {Object|*}
     * @private
     */
    LayoutNode.prototype._interpolateSet = function () {
        var tweenValue = this._transitionable.get();
        if (!this._originalSet || tweenValue === 1) {
            return this._lastSet;
        }
        var resultingSet = {};
        var tweenToSet = this._lastSet;
        var tweenFromSet = this._originalSet;

        for(var property in propertiesWithDefaults){
            var defaultProperty = propertiesWithDefaults[property];
            var tweenFrom = tweenFromSet[property] !== undefined ? tweenFromSet[property] : defaultProperty;
            var tweenTo = tweenToSet[property] !== undefined ? tweenToSet[property] : tweenFrom;
            if(tweenTo === tweenFrom){
                resultingSet[property] = tweenTo;
                continue;
            }
            tweenTo = Array.isArray(tweenTo) ? tweenTo : [tweenTo];
            tweenFrom = Array.isArray(tweenFrom) ? tweenFrom: [tweenFrom];
            this._specModified = true;
            var numberOfDimensions = defaultProperty.length;
            /* Clone the property so that we can start from the default setting */
            var tweenedSet = resultingSet[property] = new Array(numberOfDimensions);
            for(var dimension = 0;dimension<numberOfDimensions;dimension++){
                tweenedSet[dimension] = tweenFrom[dimension] + (tweenTo[dimension] - tweenFrom[dimension]) * tweenValue;
            }
        }
        resultingSet.opacity = resultingSet.opacity[0];
        return resultingSet;
    };
    /**
     * Marks the node for removal
     */
    LayoutNode.prototype.remove = function (removeSpec) {
        this._removing = true;
    };

    module.exports = LayoutNode;
});
<commit_msg>Fixed bugs in transitions<commit_after>/**
 * This Source Code is licensed under the MIT license. If a copy of the
 * MIT-license was not distributed with this file, You can obtain one at:
 * http://opensource.org/licenses/mit-license.html.
 *
 * @author: Hein Rutjes (IjzerenHein)
 * @license MIT
 * @copyright Gloey Apps, 2014
 */

/**
 * Internal LayoutNode class used by `LayoutController`.
 *
 * @module
 */
define(function (require, exports, module) {

    var Transitionable = require('famous/transitions/Transitionable');
    var Transform = require('famous/core/Transform');
    var LayoutUtility = require('./LayoutUtility');

    /**
     * @class
     * @param {Object} renderNode Render-node which this layout-node represents
     * @alias module:LayoutNode
     */
    function LayoutNode(renderNode, spec) {
        this.renderNode = renderNode;
        this._spec = spec ? LayoutUtility.cloneSpec(spec) : {};
        this._spec.renderNode = renderNode; // also store in spec
        this._specModified = true;
        this._invalidated = false;
        this._removing = false;
    }

    /**
     * Called to update the underlying render-node
     */
    LayoutNode.prototype.setRenderNode = function (renderNode) {
        this.renderNode = renderNode;
        this._spec.renderNode = renderNode;
    };

    /**
     * Called to update the options for the node
     */
    LayoutNode.prototype.setOptions = function (options) {
        // override to implement
    };

    /**
     * Called when the node is destroyed
     */
    LayoutNode.prototype.destroy = function () {
        this.renderNode = undefined;
        this._spec.renderNode = undefined;
        this._viewSequence = undefined;
    };

    /**
     * Reset the end-state. This function is called on all layout-nodes prior to
     * calling the layout-function. So that the layout-function starts with a clean slate.
     */
    LayoutNode.prototype.reset = function () {
        this._invalidated = false;
        this.trueSizeRequested = false;
    };

    /**
     * Set the spec of the node
     *
     * @param {Object} spec
     */
    LayoutNode.prototype.setSpec = function (spec) {
        this._specModified = true;
        if (spec.align) {
            if (!spec.align) {
                this._spec.align = [0, 0];
            }
            this._spec.align[0] = spec.align[0];
            this._spec.align[1] = spec.align[1];
        }
        else {
            this._spec.align = undefined;
        }
        if (spec.origin) {
            if (!spec.origin) {
                this._spec.origin = [0, 0];
            }
            this._spec.origin[0] = spec.origin[0];
            this._spec.origin[1] = spec.origin[1];
        }
        else {
            this._spec.origin = undefined;
        }
        if (spec.size) {
            if (!spec.size) {
                this._spec.size = [0, 0];
            }
            this._spec.size[0] = spec.size[0];
            this._spec.size[1] = spec.size[1];
        }
        else {
            this._spec.size = undefined;
        }

        this._spec.hide = spec.hide;

        if (spec.transform) {
            if (!spec.transform) {
                this._spec.transform = spec.transform.slice(0);
            }
            else {
                for (var i = 0; i < 16; i++) {
                    this._spec.transform[i] = spec.transform[i];
                }
            }
        }
        else {
            this._spec.transform = undefined;
        }
        this._spec.opacity = spec.opacity;
    };

    /**
     * Initiates a transition on the layout node
     * @param transition
     * @param {Function} onComplete callback when complete
     */
    LayoutNode.prototype.transition = function (transition, onComplete) {

        this._originalSet = this._transitionable ?
            /* If there's already something in progress, then interpolate the current starting position */
            this._interpolateSet() :
            /* Otherwise, it's the most recent set */
            this._lastSet;
        var transitionable = this._transitionable = new Transitionable(0);
        if(!transition.duration){
            transition.duration = 200;
        }

        var callback = (function(isImmediate) {
            /* If the transitionable wasn't replaced by another transitionable, then delete the transitionable
            * because we are finished */
            if (this._transitionable === transitionable) {
                delete this._transitionable;
                this._originalSet = this._targetSet;
                if(onComplete){
                    onComplete(isImmediate);
                }
            }
        }).bind(this);
        if(!this._originalSet){
            callback(true);
        } else {
            transitionable.set(1, transition, callback);
        }
    };

    /**
     * Set the content of the node
     *
     * @param {Object} set
     */
    LayoutNode.prototype.set = function (set, size) {

        if (!this._transitionable) {
            this._originalSet = set;
        }

        this._lastSet = set;
        this._invalidated = true;
        this._specModified = true;
        this._removing = false;
        this._modifySpecFromSet(this._spec, set);
        this.scrollLength = set.scrollLength;
    };

    var propertiesWithDefaults = {
        size: [undefined, undefined],
        origin: [0, 0],
        align: [0, 0],
        skew: [0, 0, 0],
        rotate: [0, 0, 0],
        scale: [1, 1, 1],
        translate: [0, 0, 0],
        opacity: [1]
    };

    LayoutNode.prototype._modifySpecFromSet = function (spec, set) {
        spec.opacity = set.opacity;

        var propertiesWithEasyDefaults = ['size', 'origin', 'align'];
        for (var i = 0; i < propertiesWithEasyDefaults.length; i++) {
            var propertyName = propertiesWithEasyDefaults[i];
            var defaultProperty = propertiesWithDefaults[propertyName];
            spec[propertyName] = set[propertyName] ? [
                set[propertyName][0] === undefined ? defaultProperty[0] : set[propertyName][0],
                set[propertyName][1] === undefined ? defaultProperty[1] : set[propertyName][1]
            ] : undefined;
        }

        spec.hide = set.hide;

        if (set.skew || set.rotate || set.scale) {
            spec.transform = Transform.build({
                translate: set.translate || [0, 0, 0],
                skew: set.skew || [0, 0, 0],
                scale: set.scale || [1, 1, 1],
                rotate: set.rotate || [0, 0, 0]
            });
        }
        else {
            spec.transform = set.translate ?
                Transform.translate(set.translate[0], set.translate[1], set.translate[2]) :
                undefined;
        }
    };

    /**
     * Creates the render-spec
     */
    LayoutNode.prototype.getSpec = function () {
        this._specModified = false;
        this._spec.removed = !this._invalidated;
        return this._getFinalSpec();
    };

    /**
     * Gets the calculated spec based on the time passed, or what the last setting was
     * @returns {*}
     * @private
     */
    LayoutNode.prototype._getFinalSpec = function () {
        if (this._transitionable && !this._spec.hide) {
            var spec = {renderNode: this._spec.renderNode};
            this._modifySpecFromSet(spec, this._interpolateSet());
            return spec;
        }

        return this._spec;

    };

    /**
     * Interpolates an ongoing animation
     * @returns {Object|*}
     * @private
     */
    LayoutNode.prototype._interpolateSet = function () {
        var tweenValue = this._transitionable.get();
        if (!this._originalSet || tweenValue === 1 || !this._transitionable.isActive()) {
            this._transitionable.get(); // Run again to make sure that the callback can run
            delete this._transitionable;
            return this._lastSet;
        }
        var resultingSet = {};
        var tweenToSet = this._lastSet;
        var tweenFromSet = this._originalSet;

        for(var property in propertiesWithDefaults){
            var defaultProperty = propertiesWithDefaults[property];
            var tweenFrom = tweenFromSet[property] !== undefined ? tweenFromSet[property] : defaultProperty;
            var tweenTo = tweenToSet[property] !== undefined ? tweenToSet[property] : tweenFrom;
            if(tweenTo === tweenFrom){
                resultingSet[property] = tweenTo;
                continue;
            }
            tweenTo = Array.isArray(tweenTo) ? tweenTo : [tweenTo];
            tweenFrom = Array.isArray(tweenFrom) ? tweenFrom: [tweenFrom];
            this._specModified = true;
            var numberOfDimensions = defaultProperty.length;
            /* Clone the property so that we can start from the default setting */
            var tweenedSet = resultingSet[property] = new Array(numberOfDimensions);
            for(var dimension = 0;dimension<numberOfDimensions;dimension++){
                tweenedSet[dimension] = tweenFrom[dimension] + (tweenTo[dimension] - tweenFrom[dimension]) * tweenValue;
            }
        }
        resultingSet.opacity = resultingSet.opacity[0] || resultingSet.opacity;
        return resultingSet;
    };
    /**
     * Marks the node for removal
     */
    LayoutNode.prototype.remove = function (removeSpec) {
        this._removing = true;
    };

    module.exports = LayoutNode;
});
<|endoftext|>