<commit_before>import Class from 'lowclass'
import {Camera as ThreeCamera, Object3D} from 'three'
import Mixin from './Mixin'
import ElementOperations from './ElementOperations'
import Transformable from './Transformable'
import Node from './Node'
import Scene from './Scene'
import Motor from './Motor'
import {isInstanceof} from './Utility'
import {CSS3DObjectNested} from '../lib/three/CSS3DRendererNested'
import {disposeObject} from '../utils/three'

window.addEventListener('error', (event) => {
    const error = event.error
    if (/Illegal constructor/i.test(error.message)) {
        console.error(`
            One of the reasons the following error can happen is if a Custom
            Element is called with 'new' before being defined.
            For other reasons, see: https://www.google.com/search?q=chrome%20illegal%20constructor
        `)
    }
})

let three = null
let domPlane = null

// We explicitly use `var` instead of `let` here because it is hoisted for the
// Node and Scene modules. This, along with the following initImperativeBase
// function, allows the circular dependency between this module and the Node and
// Scene modules to work. For details on why, see
// https://esdiscuss.org/topic/how-to-solve-this-basic-es6-module-circular-dependency-problem.
var ImperativeBase

// Here we wrap the definition of the ImperativeBase class with this function in
// order to solve the circular depdendency problem caused by the
// Node<->ImperativeBase and Scene<->ImperativeBase circles. The Node and Scene
// modules call initImperativeBase to ensure that the ImperativeBase declaration
// happens first, and then those modules can use the live binding in their
// declarations.
initImperativeBase()
export function initImperativeBase() {
    if (ImperativeBase) return

    /**
     * The ImperativeBase class is the base class for the Imperative version of the
     * API, for people who chose to take the all-JavaScript approach and who will
     * not use the HTML-based API (infamous/motor-html).
     *
     * In the future when there is an option to disable the HTML-DOM rendering (and
     * render only WebGL, for example) then the imperative API will be the only API
     * available since the HTML API will be turned off as a result of disabling
     * HTML rendering. Disabling both WebGL and HTML won't make sense, as we'll need
     * at least one of those to render with.
     */
    ImperativeBase = Mixin(Base =>

        Class('ImperativeBase').extends( Transformable.mixin( Base ), ({ Super, Private, Protected }) => ({
            constructor(options = {}) {
                const self = Super(this).constructor(options)

                // we don't need this, keep for backward compatibility (mainly
                // all my demos at trusktr.io).
                self.imperativeCounterpart = self

                self._willBeRendered = false

                // Here we create the DOM HTMLElement associated with this
                // Imperative-API Node.
                self._elementOperations = new ElementOperations(self)

                // stores a ref to this Node's root Scene when/if this Node is
                // in a scene.
                self._scene = null

                // See Transformable/Sizeable propertychange event.
                // TODO: defer size calculation to render task
                self.on('propertychange', self._onPropertyChange, self)

                return self
            },

            get glLoaded() {
                return Protected(this).__glLoaded
            },

            get cssLoaded() {
                return Protected(this).__cssLoaded
            },

            get three() {
                // if (!(this.scene && this.scene.experimentalWebgl)) return null

                if (!Private(this).__three)
                    Private(this).__three = this.makeThreeObject3d()

                return Private(this).__three
            },

            get threeCSS() {
                // if (!(this.scene && !this.scene.disableCss)) return null

                if (!Private(this).__threeCSS)
                    Private(this).__threeCSS = this.makeThreeCSSObject()

                return Private(this).__threeCSS
            },

            connectedCallback() {
                Super(this).connectedCallback()

                // If a subclass needs to initialize values in its Three.js
                // object, it will have the passInitialValuesToThree method for
                // that.
                //
                // TODO we shouldn't need to define passInitialValuesToThree in
                // sub classes, the default values of the props should
                // automatically be in place.
                this.passInitialValuesToThree && this.passInitialValuesToThree()
            },

            _onPropertyChange(prop) {
                if ( prop == 'sizeMode' || prop == 'size' ) {
                    this._calcSize()
                }

                this._needsToBeRendered()
            },

            loadGL() {
                if (!(this.scene && this.scene.experimentalWebgl)) return

                if (Protected(this).__glLoaded) return
                Protected(this).__glLoaded = true

                // we don't let Three update local matrices automatically, we do
                // it ourselves in Transformable._calculateMatrix and
                // Transformable._calculateWorldMatricesInSubtree
                this.three.matrixAutoUpdate = false

                // NOTE, this.parent works here because loadGL is called by
                // childConnectedCallback at which point a child is already
                // upgraded and thus has this.parent API ready.
                this.parent && this.parent.three.add(this.three)

                this._needsToBeRendered()
            },

            unloadGL() {
                if (!Protected(this).__glLoaded) return
                Protected(this).__glLoaded = false

                disposeObject(Private(this).__three)
                Private(this).__three = null

                this._needsToBeRendered()
            },

            makeThreeObject3d() {
                return new Object3D
            },

            loadCSS() {
                if (!(this.scene && !this.scene.disableCss)) return

                if (Protected(this).__cssLoaded) return
                Protected(this).__cssLoaded = true

                // we don't let Three update local matrices automatically, we do
                // it ourselves in Transformable._calculateMatrix and
                // Transformable._calculateWorldMatricesInSubtree
                this.threeCSS.matrixAutoUpdate = false

                // NOTE, this.parent works here because loadGL is called by
                // childConnectedCallback at which point a child is already
                // upgraded and thus has this.parent API ready.
                this.parent && this.parent.threeCSS.add(this.threeCSS)

                this._needsToBeRendered()
            },

            unloadCSS() {
                if (!Protected(this).__cssLoaded) return
                Protected(this).__cssLoaded = false

                disposeObject(Private(this).__threeCSS)
                Private(this).__threeCSS = null

                this._needsToBeRendered()
            },

            makeThreeCSSObject() {
                return new CSS3DObjectNested(this)
            },

            childConnectedCallback(child) {
                Super(this).childConnectedCallback(child)

                // children can be non-lib DOM nodes (f.e. div, h1, etc)
                if (isInstanceof(child, Node)) {
                    child.loadGL()
                    child.loadCSS()
                }
            },

            childDisconnectedCallback(child) {
                Super(this).childDisconnectedCallback(child)

                // children can be non-lib DOM nodes (f.e. div, h1, etc)
                if (isInstanceof(child, Node)) {
                    child.unloadGL()
                    child.unloadCSS()
                }
            },

            /**
             * Subclasses are required to override this. It should return the HTML-API
             * counterpart for this Imperative-API instance. See Node or Scene classes
             * for example.
             *
             * @private
             */
            _makeElement() {
                throw new Error('Subclasses need to override ImperativeBase#_makeElement.')
            },

            /**
             * @readonly
             */
            get element() {
                return this._elementOperations.element
            },

            /**
             * Get the Scene that this Node is in, null if no Scene. This traverses up recursively
             * at first, then the value is cached.
             *
             * @readonly
             */
            get scene() {
                // NOTE: this._scene is initally null, created in the constructor.

                // if already cached, return it. Or if no parent, return it (it'll be null).
                // Additionally, Scenes have this._scene already set to themselves.
                if (this._scene || !this.parent) return this._scene

                // if the parent node already has a ref to the scene, use that.
                if (this.parent._scene) {
                    this._scene = this.parent._scene
                }
                else if (this.parent instanceof Scene) {
                    this._scene = this.parent
                }
                // otherwise call the scene getter on the parent, which triggers
                // traversal up the scene graph in order to find the root scene (null
                // if none).
                else {
                    this._scene = this.parent.scene
                }

                return this._scene
            },

            /**
             * @override
             */
            add(childNode) {
                if (!isInstanceof(childNode, ImperativeBase)) return

                // We cannot add Scenes to Nodes, for now.
                if (childNode instanceof Scene) {
                    throw new Error(`
                        A Scene cannot be added to another Node or Scene (at
                        least for now). To place a Scene in a Node, just mount
                        a new Scene onto a MotorHTMLNode with Scene.mount().
                    `)
                }

                Super(this).add(childNode)

                // Calculate sizing because proportional size might depend on
                // the new parent.
                childNode._calcSize()
                childNode._needsToBeRendered()

                // child should watch the parent for size changes.
                this.on('sizechange', childNode._onParentSizeChange)

                this._elementOperations.connectChildElement(childNode)

                return this
            },

            remove(childNode, /* private */__leaveInDom) {
                if (!(childNode instanceof Node)) return

                Super(this).remove(childNode)

                this.off('sizechange', childNode._onParentSizeChange)

                if (!__leaveInDom)
                    this._elementOperations.disconnectChildElement(childNode)
            },

            _needsToBeRendered() {
                // we don't need to render until we're connected into a tree with a scene.
                if (!this.scene || !this.isConnected) return
                // TODO make sure we render when connected into a tree with a scene

                this._willBeRendered = true
                Motor.setNodeToBeRendered(this)
            },

            // This method is used by Motor._renderNodes().
            _getNearestAncestorThatShouldBeRendered() {
                let parent = this.parent

                while (parent) {
                    if (parent._willBeRendered) return parent
                    parent = parent.parent
                }

                return false
            },

            _render(timestamp) {
                if ( Super(this)._render ) Super(this)._render()

                this._elementOperations.applyImperativeNodeProperties(this)
            },

            // TODO make a classes prop?
            // set properties(properties = {}) {
            //     Super(this).properties = properties
            //
            //     if (properties.classes)
            //         this._elementOperations.setClasses(...properties.classes);
            // },

            protected: {
                __glLoaded: false,
                __cssLoaded: false,
            },

            private: {
                __three: null,
                __threeCSS: null,
            },
        }))

    )

}

export {ImperativeBase as default}
<commit_msg>pass initial values to GL objects in loadGL, instead of connectedCallback, so that GL properties are applied every time GL is loaded which is not only on connectedCallback<commit_after>import Class from 'lowclass'
import {Camera as ThreeCamera, Object3D} from 'three'
import Mixin from './Mixin'
import ElementOperations from './ElementOperations'
import Transformable from './Transformable'
import Node from './Node'
import Scene from './Scene'
import Motor from './Motor'
import {isInstanceof} from './Utility'
import {CSS3DObjectNested} from '../lib/three/CSS3DRendererNested'
import {disposeObject} from '../utils/three'

window.addEventListener('error', (event) => {
    const error = event.error
    if (/Illegal constructor/i.test(error.message)) {
        console.error(`
            One of the reasons the following error can happen is if a Custom
            Element is called with 'new' before being defined.
            For other reasons, see: https://www.google.com/search?q=chrome%20illegal%20constructor
        `)
    }
})

let three = null
let domPlane = null

// We explicitly use `var` instead of `let` here because it is hoisted for the
// Node and Scene modules. This, along with the following initImperativeBase
// function, allows the circular dependency between this module and the Node and
// Scene modules to work. For details on why, see
// https://esdiscuss.org/topic/how-to-solve-this-basic-es6-module-circular-dependency-problem.
var ImperativeBase

// Here we wrap the definition of the ImperativeBase class with this function in
// order to solve the circular depdendency problem caused by the
// Node<->ImperativeBase and Scene<->ImperativeBase circles. The Node and Scene
// modules call initImperativeBase to ensure that the ImperativeBase declaration
// happens first, and then those modules can use the live binding in their
// declarations.
initImperativeBase()
export function initImperativeBase() {
    if (ImperativeBase) return

    /**
     * The ImperativeBase class is the base class for the Imperative version of the
     * API, for people who chose to take the all-JavaScript approach and who will
     * not use the HTML-based API (infamous/motor-html).
     *
     * In the future when there is an option to disable the HTML-DOM rendering (and
     * render only WebGL, for example) then the imperative API will be the only API
     * available since the HTML API will be turned off as a result of disabling
     * HTML rendering. Disabling both WebGL and HTML won't make sense, as we'll need
     * at least one of those to render with.
     */
    ImperativeBase = Mixin(Base =>

        Class('ImperativeBase').extends( Transformable.mixin( Base ), ({ Super, Private, Protected }) => ({
            constructor(options = {}) {
                const self = Super(this).constructor(options)

                // we don't need this, keep for backward compatibility (mainly
                // all my demos at trusktr.io).
                self.imperativeCounterpart = self

                self._willBeRendered = false

                // Here we create the DOM HTMLElement associated with this
                // Imperative-API Node.
                self._elementOperations = new ElementOperations(self)

                // stores a ref to this Node's root Scene when/if this Node is
                // in a scene.
                self._scene = null

                // See Transformable/Sizeable propertychange event.
                // TODO: defer size calculation to render task
                self.on('propertychange', self._onPropertyChange, self)

                return self
            },

            get glLoaded() {
                return Protected(this).__glLoaded
            },

            get cssLoaded() {
                return Protected(this).__cssLoaded
            },

            get three() {
                // if (!(this.scene && this.scene.experimentalWebgl)) return null

                if (!Private(this).__three)
                    Private(this).__three = this.makeThreeObject3d()

                return Private(this).__three
            },

            get threeCSS() {
                // if (!(this.scene && !this.scene.disableCss)) return null

                if (!Private(this).__threeCSS)
                    Private(this).__threeCSS = this.makeThreeCSSObject()

                return Private(this).__threeCSS
            },

            connectedCallback() {
                Super(this).connectedCallback()
            },

            _onPropertyChange(prop) {
                if ( prop == 'sizeMode' || prop == 'size' ) {
                    this._calcSize()
                }

                this._needsToBeRendered()
            },

            loadGL() {
                if (!(this.scene && this.scene.experimentalWebgl)) return

                if (Protected(this).__glLoaded) return
                Protected(this).__glLoaded = true

                // we don't let Three update local matrices automatically, we do
                // it ourselves in Transformable._calculateMatrix and
                // Transformable._calculateWorldMatricesInSubtree
                this.three.matrixAutoUpdate = false

                // NOTE, this.parent works here because loadGL is called by
                // childConnectedCallback at which point a child is already
                // upgraded and thus has this.parent API ready.
                this.parent && this.parent.three.add(this.three)

                // If a subclass needs to initialize values in its Three.js
                // object, it will have the passInitialValuesToThree method for
                // that.
                //
                // TODO we shouldn't need to define passInitialValuesToThree in
                // sub classes, the default values of the props should
                // automatically be in place.
                this.passInitialValuesToThree && this.passInitialValuesToThree()

                this._needsToBeRendered()
            },

            unloadGL() {
                if (!Protected(this).__glLoaded) return
                Protected(this).__glLoaded = false

                disposeObject(Private(this).__three)
                Private(this).__three = null

                this._needsToBeRendered()
            },

            makeThreeObject3d() {
                return new Object3D
            },

            loadCSS() {
                if (!(this.scene && !this.scene.disableCss)) return

                if (Protected(this).__cssLoaded) return
                Protected(this).__cssLoaded = true

                // we don't let Three update local matrices automatically, we do
                // it ourselves in Transformable._calculateMatrix and
                // Transformable._calculateWorldMatricesInSubtree
                this.threeCSS.matrixAutoUpdate = false

                // NOTE, this.parent works here because loadCSS is called by
                // childConnectedCallback at which point a child is already
                // upgraded and thus has this.parent API ready.
                this.parent && this.parent.threeCSS.add(this.threeCSS)

                this._needsToBeRendered()
            },

            unloadCSS() {
                if (!Protected(this).__cssLoaded) return
                Protected(this).__cssLoaded = false

                disposeObject(Private(this).__threeCSS)
                Private(this).__threeCSS = null

                this._needsToBeRendered()
            },

            makeThreeCSSObject() {
                return new CSS3DObjectNested(this)
            },

            childConnectedCallback(child) {
                Super(this).childConnectedCallback(child)

                // children can be non-lib DOM nodes (f.e. div, h1, etc)
                if (isInstanceof(child, Node)) {
                    child.loadGL()
                    child.loadCSS()
                }
            },

            childDisconnectedCallback(child) {
                Super(this).childDisconnectedCallback(child)

                // children can be non-lib DOM nodes (f.e. div, h1, etc)
                if (isInstanceof(child, Node)) {
                    child.unloadGL()
                    child.unloadCSS()
                }
            },

            /**
             * Subclasses are required to override this. It should return the HTML-API
             * counterpart for this Imperative-API instance. See Node or Scene classes
             * for example.
             *
             * @private
             */
            _makeElement() {
                throw new Error('Subclasses need to override ImperativeBase#_makeElement.')
            },

            /**
             * @readonly
             */
            get element() {
                return this._elementOperations.element
            },

            /**
             * Get the Scene that this Node is in, null if no Scene. This traverses up recursively
             * at first, then the value is cached.
             *
             * @readonly
             */
            get scene() {
                // NOTE: this._scene is initally null, created in the constructor.

                // if already cached, return it. Or if no parent, return it (it'll be null).
                // Additionally, Scenes have this._scene already set to themselves.
                if (this._scene || !this.parent) return this._scene

                // if the parent node already has a ref to the scene, use that.
                if (this.parent._scene) {
                    this._scene = this.parent._scene
                }
                else if (this.parent instanceof Scene) {
                    this._scene = this.parent
                }
                // otherwise call the scene getter on the parent, which triggers
                // traversal up the scene graph in order to find the root scene (null
                // if none).
                else {
                    this._scene = this.parent.scene
                }

                return this._scene
            },

            /**
             * @override
             */
            add(childNode) {
                if (!isInstanceof(childNode, ImperativeBase)) return

                // We cannot add Scenes to Nodes, for now.
                if (childNode instanceof Scene) {
                    throw new Error(`
                        A Scene cannot be added to another Node or Scene (at
                        least for now). To place a Scene in a Node, just mount
                        a new Scene onto a MotorHTMLNode with Scene.mount().
                    `)
                }

                Super(this).add(childNode)

                // Calculate sizing because proportional size might depend on
                // the new parent.
                childNode._calcSize()
                childNode._needsToBeRendered()

                // child should watch the parent for size changes.
                this.on('sizechange', childNode._onParentSizeChange)

                this._elementOperations.connectChildElement(childNode)

                return this
            },

            remove(childNode, /* private */__leaveInDom) {
                if (!(childNode instanceof Node)) return

                Super(this).remove(childNode)

                this.off('sizechange', childNode._onParentSizeChange)

                if (!__leaveInDom)
                    this._elementOperations.disconnectChildElement(childNode)
            },

            _needsToBeRendered() {
                // we don't need to render until we're connected into a tree with a scene.
                if (!this.scene || !this.isConnected) return
                // TODO make sure we render when connected into a tree with a scene

                this._willBeRendered = true
                Motor.setNodeToBeRendered(this)
            },

            // This method is used by Motor._renderNodes().
            _getNearestAncestorThatShouldBeRendered() {
                let parent = this.parent

                while (parent) {
                    if (parent._willBeRendered) return parent
                    parent = parent.parent
                }

                return false
            },

            _render(timestamp) {
                if ( Super(this)._render ) Super(this)._render()

                this._elementOperations.applyImperativeNodeProperties(this)
            },

            // TODO make a classes prop?
            // set properties(properties = {}) {
            //     Super(this).properties = properties
            //
            //     if (properties.classes)
            //         this._elementOperations.setClasses(...properties.classes);
            // },

            protected: {
                __glLoaded: false,
                __cssLoaded: false,
            },

            private: {
                __three: null,
                __threeCSS: null,
            },
        }))

    )

}

export {ImperativeBase as default}
<|endoftext|>


<commit_before>/*
 * @license
 * Copyright 2016 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.
 */

/**
  Imports and Exports provide implicit Context dependency management.

  A class can list which values it requires from the Context, and then
  these values will be added to the object itself so that it doesn't need
  to explicitly work with the Context.

  A class can list which values (properties, methods, or method-like axioms)
  that it exports, and these will automatically be added to the object's
  sub-Context. The object's sub-Context is the context that is used when
  new objects are created by the object.

  Ex.:

foam.CLASS({
  name: 'ImportsTest',

  imports: [ 'log', 'warn' ],

  methods: [ function foo() {
    this.log('log foo from ImportTest');
    this.warn('warn foo from ImportTest');
  } ]
});

foam.CLASS({
  name: 'ExportsTest',
  requires: [ 'ImportsTest' ],

  exports: [ 'log', 'log as warn' ],

  methods: [
    function init() {
      // ImportsTest will be created in ExportTest's
      // sub-Context, which will have 'log' and 'warn'
      // exported.
      this.ImportsTest.create().foo();
    },
    function log(msg) {
      console.log('log:', msg);
    }
  ]
});

  Aliasing:
    Bindings can be renamed or aliased when they're imported or exported using
    'as alias'.

  Examples:
    // import 'userDAO' from the Context and make available as this.dao
    imports: [ 'userDAO as dao' ]

    // export my log method as 'warn'
    exports: [ 'log as warn' ]

    // If the axiom to be exported isn't named, but just aliased, then 'this'
    // is exported as the named alias.  This is how objects export themselves.
    exports: [ 'as Controller' ]

  See Context.js.
 */
foam.CLASS({
  package: 'foam.core',
  name: 'Import',

  // documentation: 'Import Context Value Axiom',

  properties: [
    'name',
    'key',
    {
      name: 'slotName_',
      factory: function() { return this.name + '$'; }
    }
  ],

  methods: [
    function installInProto(proto) {
      var name     = this.name;
      var key      = this.key + '$';
      var slotName = this.slotName_;

      Object.defineProperty(proto, slotName, {
        get: function importsSlotGetter() {
          if ( ! this.hasOwnPrivate_(slotName) ) {
            var ctx = this.__context__ || foam.__context__;
            var val = ctx[key];
            this.setPrivate_(slotName, val);
            return val;
          }

          return this.getPrivate_(slotName);
        },
        configurable: true,
        enumerable: false
      });

      Object.defineProperty(proto, name, {
        get: function importsGetter()  {
          var slot = this[slotName];
          if ( slot ) return slot.get();
          console.warn('Access missing import:', name);
          return undefined;
        },
        set: function importsSetter(v) {
          var slot = this[slotName];
          if ( slot ) slot.set(v);
          console.warn('Attempt to set missing import:', name);
        },
        configurable: true,
        enumerable: false
      });
    },

    function toSlot(obj) {
      return obj[this.slotName_];
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Export',

  // documentation: 'Export Sub-Context Value Axiom',

  properties: [
    'name',
    {
      name: 'exportName',
      postSet: function(_, name) {
        this.name = 'export_' + name;
      }
    },
    'key'
  ],

  methods: [
    function installInProto(proto) {
      if ( Object.prototype.hasOwnProperty.call(proto, '__subContext__' ) ) {
        return;
      }

      Object.defineProperty(proto, '__subContext__', {
        get: function YGetter() {
          if ( ! this.hasOwnPrivate_('__subContext__') ) {
            var ctx = this.__context__ || foam.__context__;
            var m = {};
            var bs = proto.cls_.getAxiomsByClass(foam.core.Export);
            for ( var i = 0 ; i < bs.length ; i++ ) {
              var b = bs[i];

              if ( b.key ) {
                var a = this.cls_.getAxiomByName(b.key);

                if ( ! a ) {
                  console.error(
                    'Unknown export: "' +
                      b.key +
                      '" in model: ' +
                      this.cls_.id);
                }

                // Axioms have an option of wrapping a value for export.
                // This could be used to bind a method to 'this', for example.
                m[b.exportName] = a.exportAs ? a.exportAs(this) : this[b.key] ;
              } else {
                // Ex. 'as Bank', which exports an implicit 'this'
                m[b.exportName] = this;
              }
            }
            this.setPrivate_('__subContext__', ctx.createSubContext(m));
          }

          return this.getPrivate_('__subContext__');
        },
        set: function() {
          throw new Error('Attempted to set unsettable __subContext__ in ' + this.cls_.id);
        },
        configurable: true,
        enumerable: false
      });
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Model',
  properties: [
    {
      class: 'AxiomArray',
      of: 'Import',
      name: 'imports',
      adaptArrayElement: function(o) {
        if ( typeof o === 'string' ) {
          var a = o.split(' as ');
          return foam.core.Import.create({name: a[1] || a[0], key: a[0]});
        }

        return foam.core.Import.create(o);
      }
    },
    {
      class: 'AxiomArray',
      of: 'Export',
      name: 'exports',
      adaptArrayElement: function(o) {
        if ( typeof o === 'string' ) {
          var a = o.split(' ');

          switch ( a.length ) {
            case 1:
              return foam.core.Export.create({exportName: a[0], key: a[0]});

            case 2:
              // Export 'this'
              console.assert(
                  a[0] === 'as',
                  'Invalid export syntax: key [as value] | as value');
              return foam.core.Export.create({exportName: a[1], key: null});

            case 3:
              console.assert(
                  a[1] === 'as',
                  'Invalid export syntax: key [as value] | as value');
              return foam.core.Export.create({exportName: a[2], key: a[0]});

            default:
              console.error(
                  'Invalid export syntax: key [as value] | as value');
          }
        }

        return foam.core.Export.create(o);
      }
    }
  ]
});
<commit_msg>Imports no longer cached in private_.<commit_after>/*
 * @license
 * Copyright 2016 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.
 */

/**
  Imports and Exports provide implicit Context dependency management.

  A class can list which values it requires from the Context, and then
  these values will be added to the object itself so that it doesn't need
  to explicitly work with the Context.

  A class can list which values (properties, methods, or method-like axioms)
  that it exports, and these will automatically be added to the object's
  sub-Context. The object's sub-Context is the context that is used when
  new objects are created by the object.

  Ex.:

foam.CLASS({
  name: 'ImportsTest',

  imports: [ 'log', 'warn' ],

  methods: [ function foo() {
    this.log('log foo from ImportTest');
    this.warn('warn foo from ImportTest');
  } ]
});

foam.CLASS({
  name: 'ExportsTest',
  requires: [ 'ImportsTest' ],

  exports: [ 'log', 'log as warn' ],

  methods: [
    function init() {
      // ImportsTest will be created in ExportTest's
      // sub-Context, which will have 'log' and 'warn'
      // exported.
      this.ImportsTest.create().foo();
    },
    function log(msg) {
      console.log('log:', msg);
    }
  ]
});

  Aliasing:
    Bindings can be renamed or aliased when they're imported or exported using
    'as alias'.

  Examples:
    // import 'userDAO' from the Context and make available as this.dao
    imports: [ 'userDAO as dao' ]

    // export my log method as 'warn'
    exports: [ 'log as warn' ]

    // If the axiom to be exported isn't named, but just aliased, then 'this'
    // is exported as the named alias.  This is how objects export themselves.
    exports: [ 'as Controller' ]

  See Context.js.
 */
foam.CLASS({
  package: 'foam.core',
  name: 'Import',

  // documentation: 'Import Context Value Axiom',

  properties: [
    'name',
    'key',
    {
      name: 'slotName_',
      factory: function() { return this.name + '$'; }
    }
  ],

  methods: [
    function installInProto(proto) {
      var name     = this.name;
      var key      = this.key + '$';
      var slotName = this.slotName_;

      Object.defineProperty(proto, slotName, {
        get: function importsSlotGetter() {
          return this.__context__[key];
        },
        configurable: false,
        enumerable: false
      });

      Object.defineProperty(proto, name, {
        get: function importsGetter()  {
          var slot = this[slotName];
          if ( slot ) return slot.get();
          console.warn('Access missing import:', name);
          return undefined;
        },
        set: function importsSetter(v) {
          var slot = this[slotName];
          if ( slot ) slot.set(v);
          console.warn('Attempt to set missing import:', name);
        },
        configurable: true,
        enumerable: false
      });
    },

    function toSlot(obj) {
      return obj[this.slotName_];
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Export',

  // documentation: 'Export Sub-Context Value Axiom',

  properties: [
    'name',
    {
      name: 'exportName',
      postSet: function(_, name) {
        this.name = 'export_' + name;
      }
    },
    'key'
  ],

  methods: [
    function installInProto(proto) {
      if ( Object.prototype.hasOwnProperty.call(proto, '__subContext__' ) ) {
        return;
      }

      Object.defineProperty(proto, '__subContext__', {
        get: function YGetter() {
          if ( ! this.hasOwnPrivate_('__subContext__') ) {
            var ctx = this.__context__ || foam.__context__;
            var m = {};
            var bs = proto.cls_.getAxiomsByClass(foam.core.Export);
            for ( var i = 0 ; i < bs.length ; i++ ) {
              var b = bs[i];

              if ( b.key ) {
                var a = this.cls_.getAxiomByName(b.key);

                if ( ! a ) {
                  console.error(
                    'Unknown export: "' +
                      b.key +
                      '" in model: ' +
                      this.cls_.id);
                }

                // Axioms have an option of wrapping a value for export.
                // This could be used to bind a method to 'this', for example.
                m[b.exportName] = a.exportAs ? a.exportAs(this) : this[b.key] ;
              } else {
                // Ex. 'as Bank', which exports an implicit 'this'
                m[b.exportName] = this;
              }
            }
            this.setPrivate_('__subContext__', ctx.createSubContext(m));
          }

          return this.getPrivate_('__subContext__');
        },
        set: function() {
          throw new Error('Attempted to set unsettable __subContext__ in ' + this.cls_.id);
        },
        configurable: true,
        enumerable: false
      });
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Model',
  properties: [
    {
      class: 'AxiomArray',
      of: 'Import',
      name: 'imports',
      adaptArrayElement: function(o) {
        if ( typeof o === 'string' ) {
          var a = o.split(' as ');
          return foam.core.Import.create({name: a[1] || a[0], key: a[0]});
        }

        return foam.core.Import.create(o);
      }
    },
    {
      class: 'AxiomArray',
      of: 'Export',
      name: 'exports',
      adaptArrayElement: function(o) {
        if ( typeof o === 'string' ) {
          var a = o.split(' ');

          switch ( a.length ) {
            case 1:
              return foam.core.Export.create({exportName: a[0], key: a[0]});

            case 2:
              // Export 'this'
              console.assert(
                  a[0] === 'as',
                  'Invalid export syntax: key [as value] | as value');
              return foam.core.Export.create({exportName: a[1], key: null});

            case 3:
              console.assert(
                  a[1] === 'as',
                  'Invalid export syntax: key [as value] | as value');
              return foam.core.Export.create({exportName: a[2], key: a[0]});

            default:
              console.error(
                  'Invalid export syntax: key [as value] | as value');
          }
        }

        return foam.core.Export.create(o);
      }
    }
  ]
});
<|endoftext|>


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

    /**
     * Responsible for application-wide issues such as the creation of modules and establishing connections between them.
     *
     * @author Remo Brunschwiler
     * @namespace Tc
     * @class Application
     */
    Tc.Application = Class.extend({

        /**
         * Initializes the application.
         *
         * @method init
         * @constructor
         * @param {jQuery} $ctx
         *      The jQuery context
         * @param {Object} config
         *      The configuration
         */
        init: function($ctx, config) {
            /**
             * The configuration.
             *
             * @property config
             * @type Object
             */
            this.config = $.extend(Tc.Config, config);

            /**
             * The jQuery context.
             *
             * @property $ctx
             * @type jQuery
             */
            this.$ctx = $ctx || $('body');

            /**
             * Contains references to all modules on the page. This can, for
             * example, be useful when there are interactions between Flash
             * objects and Javascript.
             *
             * @property modules
             * @type Array
             */
            this.modules = [];

            /**
             * Contains references to all connectors on the page.
             *
             * @property connectors
             * @type Object
             */
            this.connectors = {};

            /**
             * The sandbox to get the resources from
             * This sandbox is shared between all modules.
             *
             * @property sandbox
             * @type Sandbox
             */
            this.sandbox = new Tc.Sandbox(this, this.config);
        },

        /**
         * Register modules withing scope
         * Automatically registers all modules within the scope,
         * as long as the modules use the OOCSS naming conventions.
         *
         * @method registerModules
         * @param {jQuery} $ctx
         *      The jQuery context
         * @return {Array}
         *      A list containing the references of the registered modules
         */
        registerModules : function($ctx) {
            var self = this,
                modules = [],
                stringUtils = Tc.Utils.String;

            $ctx = $ctx || this.$ctx;

            $ctx.find('.mod:not([data-ignore="true"])').each(function() {
                var $this = $(this),
                    classes = $this.attr('class').split(' ');

                /*
                 * A module can have several different classes and data attributes.
                 * See below for possible values.
                 */

                /*
                 * @config .mod
                 *
                 * Indicates that it is a base module, this is the default and
                 * no JavaScript needs to be involved. It must occur excactly
                 * once.
                 */

                /*
                 * @config .mod{moduleName} || .mod-{module-name}
                 *
                 * Indicates that it is a module of type basic, which is
                 * derived from the base module. It can occur at most
                 * once. Example: .modBasic || .mod-basic
                 */

                /*
                 * @config .skin{moduleName}{skinName} || .skin-{module-name}-{skin-name}
                 *
                 * Indicates that the module basic has the submarine skin. It
                 * will be decorated by the skin JS (if it exists). It can occur
                 * arbitrarily. Example: .skinBasicSubmarine || .skin-basic-submarine
                 */

                /*
                 * @config data-connectors
                 *
                 * A module can have a comma-separated list of data connectors.
                 * The list contains the IDs of the connectors in the following
                 * schema: {connectorType}-{connectorId}
                 *
                 * {connectorType} is optional. If only the {connectorId} is given, the
                 * default connector is instantiated.
                 *
                 * The example MasterSlave-Navigation decodes to: type =
                 * MasterSlave, id = Navigation. This instantiates the MasterSlave
                 * connector (as mediator) with the connector id Navigation.
                 * The connector id is used to chain the appropriate (the ones with the same id)
                 * modules together and to improve the reusability of the connector.
                 * It can contain multiple connector ids (e.g. 1,2,MasterSlave-Navigation).
                 */

                if (classes.length > 1) {
                    var modName,
                        skins = [],
                        connectors = [],
                        dataConnectors;

                    for (var i = 0, len = classes.length; i < len; i++) {
                        var part = $.trim(classes[i]);

                        // do nothing for empty parts
                        if(part) {
                            // convert to camel if necessary
                            if (part.indexOf('-') > -1) {
                                part = stringUtils.toCamel(part);
                            }

                            if (part.indexOf('mod') === 0 && part.length > 3) {
                                modName = part.substr(3);
                            }
                            else if (part.indexOf('skin') === 0) {
                                // Remove the mod name part from the skin name
                                skins.push(part.substr(4).replace(modName, ''));
                            }
                        }
                    }

                    /*
                     * This needs to be done via attr() instead of data().
                     * As data() cast a single number-only connector to an integer, the split will fail.
                     */
                    dataConnectors = $this.attr('data-connectors');

                    if (dataConnectors) {
                        connectors = dataConnectors.split(',');
                        for (var i = 0, len = connectors.length; i < len; i++) {
                            var connector = $.trim(connectors[i]);
                            // do nothing for empty connectors
                            if(connector) {
                                connectors[i] = connector;
                            }
                        }
                    }

                    if (modName && Tc.Module[modName]) {
                        modules.push(self.registerModule($this, modName, skins, connectors));
                    }
                }
            });

            return modules;
        },

        /**
         * Unregisters the modules given by the module instances.
         *
         * @method unregisterModule
         * @param {Array} modules
         *      A list containting the module instances to unregister
         */
        unregisterModules : function(modules) {
            var connectors = this.connectors;

            modules = modules || this.modules;

            if (modules === this.modules) {
                // Clear everything if the arrays are equal
                this.connectors = [];
                this.modules = [];
            }
            else {
                // Unregister the given modules
                for (var i = 0, len = modules.length; i < len; i++) {
                    var module = modules[i],
                        index;

                    // Delete the references in the connectors
                    for (var connectorId in connectors) {
                        if (connectors.hasOwnProperty(connectorId)) {
                            connectors[connectorId].unregisterComponent(module);
                        }
                    }

                    // Delete the module instance itself
                    index = $.inArray(module, this.modules);
                    if(index > -1) {
                        delete this.modules[index];
                    }
                }
            }
        },

        /**
         * Starts (intializes) the registered modules.
         *
         * @method start
         * @param {Array} modules
         *      A list of the modules to start
         */
        start: function(modules) {
            modules = modules || this.modules;

            // Start the modules
            for (var i = 0, len = modules.length; i < len; i++) {
                modules[i].start();
            }
        },

        /**
         * Stops the registered modules.
         *
         * @method stop
         * @param {Array} modules
         *      A list containting the module instances to stop
         */
        stop: function(modules) {
            modules = modules || this.modules;

            // Stop the modules
            for (var i = 0, len = modules.length; i < len; i++) {
                modules[i].stop();
            }
        },

        /**
         * Registers a module.
         *
         * @method registerModule
         * @param {jQuery} $node
         *      The module node
         * @param {String} modName
         *      The module name. It must match the class name of the module
         * @param {Array} skins
         *      A list of skin names. Each entry must match a class name of a skin
         * @param {Array} connectors
         *      A list of connectors identifiers (e.g. MasterSlave-Navigation)
         *      Schema: {connectorName}-{connectorId}
         * @return {Module}
         *      The reference to the registered module
         */
        registerModule : function($node, modName, skins, connectors) {
            var modules = this.modules;

            modName = modName || undefined;
            skins = skins || [];
            connectors = connectors || [];

            if (modName && Tc.Module[modName]) {
                // Generate a unique ID for every module
                var id = modules.length;
                $.data($node[0], 'id', id);

                modules[id] = new Tc.Module[modName]($node, this.sandbox, id);

                for (var i = 0, len = skins.length; i < len; i++) {
                    var skinName = skins[i];

                    if (Tc.Module[modName][skinName]) {
                        modules[id] = modules[id].getDecoratedModule(modName, skinName);
                    }
                }

                for (var i = 0, len = connectors.length; i < len; i++) {
                    this.registerConnection(connectors[i], modules[id]);
                }

                return modules[id];
            }

            return null;
        },

        /**
         * Registers a connection between a module and a connector.
         *
         * @method registerConnection
         * @param {String} connector
         *      The full connector name (e.g. MasterSlave-Navigation)
         * @param {Module} component
         *      The module instance
         */
        registerConnection : function(connector, component) {
            connector = $.trim(connector);

            var parts = connector.split('-'),
                connectorType,
                connectorId,
                identifier;

            if(parts.length === 1) {
                // default connector
                identifier = connectorId = parts[0];
            }
            else if(parts.length === 2) {
                // a specific connector type is given
                connectorType = parts[0];
                connectorId = parts[1];
                identifier = connectorType + connectorId;
            }

            if(identifier) {
                var connectors = this.connectors;

                if (!connectors[identifier]) {
                    // Instantiate the appropriate connector if it does not exist yet
                    if (!connectorType) {
                        connectors[identifier] = new Tc.Connector(connectorId);
                    }
                    else if (Tc.Connector[connectorType]) {
                        connectors[identifier] = new Tc.Connector[connectorType](connectorId);
                    }
                }

                if (connectors[identifier]) {
                    /*
                     * The connector observes the component and attaches it as
                     * an observer.
                     */
                    component.attachConnector(connectors[identifier]);

                    /*
                     * The component wants to be informed over state changes.
                     * It registers it as connector member.
                     */
                    connectors[identifier].registerComponent(component);
                }
            }
        },

        /**
         * Unregisters a module from a connector.
         *
         * @method unregisterConnection
         * @param {String} connectorId
         *      The connector channel id (e.g. 2)
         * @param {Module} component
         *      The module instance
         */
        unregisterConnection : function(connectorId, component) {
            var connector =  this.connectors[connectorId];

            // Delete the references in the connector and the module
            if (connector) {
                connector.unregisterComponent(component);
                component.detachConnector(connector);
            }
        }
    });
})(Tc.$);

<commit_msg>code documentation<commit_after>(function($) {
    "use strict";

    /**
     * Responsible for application-wide issues such as the creation of modules and establishing connections between them.
     *
     * @author Remo Brunschwiler
     * @namespace Tc
     * @class Application
     */
    Tc.Application = Class.extend({

        /**
         * Initializes the application.
         *
         * @method init
         * @constructor
         * @param {jQuery} $ctx
         *      The jQuery context
         * @param {Object} config
         *      The configuration
         */
        init: function($ctx, config) {
            /**
             * The configuration.
             *
             * @property config
             * @type Object
             */
            this.config = $.extend(Tc.Config, config);

            /**
             * The jQuery context.
             *
             * @property $ctx
             * @type jQuery
             */
            this.$ctx = $ctx || $('body');

            /**
             * Contains references to all modules on the page. This can, for
             * example, be useful when there are interactions between Flash
             * objects and Javascript.
             *
             * @property modules
             * @type Array
             */
            this.modules = [];

            /**
             * Contains references to all connectors on the page.
             *
             * @property connectors
             * @type Object
             */
            this.connectors = {};

            /**
             * The sandbox to get the resources from
             * This sandbox is shared between all modules.
             *
             * @property sandbox
             * @type Sandbox
             */
            this.sandbox = new Tc.Sandbox(this, this.config);
        },

        /**
         * Register modules withing scope
         * Automatically registers all modules within the scope,
         * as long as the modules use the OOCSS naming conventions.
         *
         * @method registerModules
         * @param {jQuery} $ctx
         *      The jQuery context
         * @return {Array}
         *      A list containing the references of the registered modules
         */
        registerModules : function($ctx) {
            var self = this,
                modules = [],
                stringUtils = Tc.Utils.String;

            $ctx = $ctx || this.$ctx;

            $ctx.find('.mod:not([data-ignore="true"])').each(function() {
                var $this = $(this),
                    classes = $this.attr('class').split(' ');

                /*
                 * A module can have several different classes and data attributes.
                 * See below for possible values.
                 */

                /*
                 * @config .mod
                 *
                 * Indicates that it is a base module, this is the default and
                 * no JavaScript needs to be involved. It must occur excactly
                 * once.
                 */

                /*
                 * @config .mod{moduleName} || .mod-{module-name}
                 *
                 * Indicates that it is a module of type basic, which is
                 * derived from the base module. It can occur at most
                 * once. Example: .modBasic || .mod-basic
                 */

                /*
                 * @config .skin{moduleName}{skinName} || .skin-{module-name}-{skin-name}
                 *
                 * Indicates that the module basic has the submarine skin. It
                 * will be decorated by the skin JS (if it exists). It can occur
                 * arbitrarily. Example: .skinBasicSubmarine || .skin-basic-submarine
                 */

                /*
                 * @config data-connectors
                 *
                 * A module can have a comma-separated list of data connectors.
                 * The list contains the IDs of the connectors in the following
                 * schema: {connectorType}-{connectorId}
                 *
                 * {connectorType} is optional. If only the {connectorId} is given, the
                 * default connector is instantiated.
                 *
                 * The example MasterSlave-Navigation decodes to: type =
                 * MasterSlave, id = Navigation. This instantiates the MasterSlave
                 * connector (as mediator) with the connector id Navigation.
                 * The connector id is used to chain the appropriate (the ones with the same id)
                 * modules together and to improve the reusability of the connector.
                 * It can contain multiple connector ids (e.g. 1,2,MasterSlave-Navigation).
                 */

                if (classes.length > 1) {
                    var modName,
                        skins = [],
                        connectors = [],
                        dataConnectors;

                    for (var i = 0, len = classes.length; i < len; i++) {
                        var part = $.trim(classes[i]);

                        // do nothing for empty parts
                        if(part) {
                            // convert to camel if necessary
                            if (part.indexOf('-') > -1) {
                                part = stringUtils.toCamel(part);
                            }

                            if (part.indexOf('mod') === 0 && part.length > 3) {
                                modName = part.substr(3);
                            }
                            else if (part.indexOf('skin') === 0) {
                                // Remove the mod name part from the skin name
                                skins.push(part.substr(4).replace(modName, ''));
                            }
                        }
                    }

                    /*
                     * This needs to be done via attr() instead of data().
                     * As data() cast a single number-only connector to an integer, the split will fail.
                     */
                    dataConnectors = $this.attr('data-connectors');

                    if (dataConnectors) {
                        connectors = dataConnectors.split(',');
                        for (var i = 0, len = connectors.length; i < len; i++) {
                            var connector = $.trim(connectors[i]);
                            // do nothing for empty connectors
                            if(connector) {
                                connectors[i] = connector;
                            }
                        }
                    }

                    if (modName && Tc.Module[modName]) {
                        modules.push(self.registerModule($this, modName, skins, connectors));
                    }
                }
            });

            return modules;
        },

        /**
         * Unregisters the modules given by the module instances.
         *
         * @method unregisterModule
         * @param {Array} modules
         *      A list containting the module instances to unregister
         */
        unregisterModules : function(modules) {
            var connectors = this.connectors;

            modules = modules || this.modules;

            if (modules === this.modules) {
                // Clear everything if the arrays are equal
                this.connectors = [];
                this.modules = [];
            }
            else {
                // Unregister the given modules
                for (var i = 0, len = modules.length; i < len; i++) {
                    var module = modules[i],
                        index;

                    // Delete the references in the connectors
                    for (var connectorId in connectors) {
                        if (connectors.hasOwnProperty(connectorId)) {
                            connectors[connectorId].unregisterComponent(module);
                        }
                    }

                    // Delete the module instance itself
                    index = $.inArray(module, this.modules);
                    if(index > -1) {
                        delete this.modules[index];
                    }
                }
            }
        },

        /**
         * Starts (intializes) the registered modules.
         *
         * @method start
         * @param {Array} modules
         *      A list of the modules to start
         */
        start: function(modules) {
            modules = modules || this.modules;

            // Start the modules
            for (var i = 0, len = modules.length; i < len; i++) {
                modules[i].start();
            }
        },

        /**
         * Stops the registered modules.
         *
         * @method stop
         * @param {Array} modules
         *      A list containting the module instances to stop
         */
        stop: function(modules) {
            modules = modules || this.modules;

            // Stop the modules
            for (var i = 0, len = modules.length; i < len; i++) {
                modules[i].stop();
            }
        },

        /**
         * Registers a module.
         *
         * @method registerModule
         * @param {jQuery} $node
         *      The module node
         * @param {String} modName
         *      The module name. It must match the class name of the module
         * @param {Array} skins
         *      A list of skin names. Each entry must match a class name of a skin
         * @param {Array} connectors
         *      A list of connectors identifiers (e.g. MasterSlave-Navigation)
         *      Schema: {connectorName}-{connectorId}
         * @return {Module}
         *      The reference to the registered module
         */
        registerModule : function($node, modName, skins, connectors) {
            var modules = this.modules;

            modName = modName || undefined;
            skins = skins || [];
            connectors = connectors || [];

            if (modName && Tc.Module[modName]) {
                // Generate a unique ID for every module
                var id = modules.length;
                $.data($node[0], 'id', id);

                // Instantiate module
                modules[id] = new Tc.Module[modName]($node, this.sandbox, id);

                // Decorate it
                for (var i = 0, len = skins.length; i < len; i++) {
                    var skinName = skins[i];

                    if (Tc.Module[modName][skinName]) {
                        modules[id] = modules[id].getDecoratedModule(modName, skinName);
                    }
                }

                // Register connections
                for (var i = 0, len = connectors.length; i < len; i++) {
                    this.registerConnection(connectors[i], modules[id]);
                }

                return modules[id];
            }

            return null;
        },

        /**
         * Registers a connection between a module and a connector.
         *
         * @method registerConnection
         * @param {String} connector
         *      The full connector name (e.g. MasterSlave-Navigation)
         * @param {Module} component
         *      The module instance
         */
        registerConnection : function(connector, component) {
            connector = $.trim(connector);

            var parts = connector.split('-'),
                connectorType,
                connectorId,
                identifier;

            if(parts.length === 1) {
                // default connector
                identifier = connectorId = parts[0];
            }
            else if(parts.length === 2) {
                // a specific connector type is given
                connectorType = parts[0];
                connectorId = parts[1];
                identifier = connectorType + connectorId;
            }

            if(identifier) {
                var connectors = this.connectors;

                if (!connectors[identifier]) {
                    // Instantiate the appropriate connector if it does not exist yet
                    if (!connectorType) {
                        connectors[identifier] = new Tc.Connector(connectorId);
                    }
                    else if (Tc.Connector[connectorType]) {
                        connectors[identifier] = new Tc.Connector[connectorType](connectorId);
                    }
                }

                if (connectors[identifier]) {
                    /*
                     * The connector observes the component and attaches it as
                     * an observer.
                     */
                    component.attachConnector(connectors[identifier]);

                    /*
                     * The component wants to be informed over state changes.
                     * It registers it as connector member.
                     */
                    connectors[identifier].registerComponent(component);
                }
            }
        },

        /**
         * Unregisters a module from a connector.
         *
         * @method unregisterConnection
         * @param {String} connectorId
         *      The connector channel id (e.g. 2)
         * @param {Module} component
         *      The module instance
         */
        unregisterConnection : function(connectorId, component) {
            var connector =  this.connectors[connectorId];

            // Delete the references in the connector and the module
            if (connector) {
                connector.unregisterComponent(component);
                component.detachConnector(connector);
            }
        }
    });
})(Tc.$);

<|endoftext|>


<commit_before>function require(path, parent){
  var realpath = require.resolve(path, parent),
      m = require.mods[realpath];

  if(!m.init){
    m.factory.call(this, require.local(realpath), m.module, m.module.exports);
    m.init = true;
  }

  return m.module.exports;
}

require.mods = {}

require.local = function( path ){
  var r = function( id ){ return require( id, path ); }
  r.resolve = function( id ){ return require.resolve( id, path ); }
  return r;
}

require.register = function(path, mod, aliases){
  require.mods[path] = {
    factory: mod,
    aliases: aliases,
    module: {exports:{}}
  };
}

require.aliases = ~ALIASES;
require.alias = function(path){
  for(var alias in require.aliases)
    if(path.indexOf(alias) == 0)
      return require.aliases[alias] + path.match(/\/(.+)/)[0];
  return null;
}

require.resolve = function(path, parent){
  var realpath;

  if(parent){
    if(!(realpath = require.mods[parent].aliases[path]))
      realpath = require.alias( path );
  }
  else
    realpath = path;

  if(!require.mods[realpath])
      throw new Error('Module not found: ' + path);

  return realpath;
}

window.require = require;<commit_msg>fixing dynamic requires' lookup<commit_after>function require(path, parent){
  var realpath = require.resolve(path, parent),
      m = require.mods[realpath];

  if(!m.init){
    m.factory.call(this, require.local(realpath), m.module, m.module.exports);
    m.init = true;
  }

  return m.module.exports;
}

require.mods = {}

require.local = function( path ){
  var r = function( id ){ return require( id, path ); }
  r.resolve = function( id ){ return require.resolve( id, path ); }
  return r;
}

require.register = function(path, mod, aliases){
  require.mods[path] = {
    factory: mod,
    aliases: aliases,
    module: {exports:{}}
  };
}

require.aliases = ~ALIASES;
require.alias = function(path){
  for(var alias in require.aliases)
    if(path.indexOf(alias) == 0)
      return require.aliases[alias] + path.match(/\/(.+)/)[0];
  return null;
}

require.resolve = function(path, parent){
  var realpath;

  if(parent)
    if(!(realpath = require.mods[parent].aliases[path]))
      realpath = require.alias( path );

  if(!require.mods[realpath || path])
      throw new Error('Module not found: ' + path);

  return realpath || path;
}

window.require = require;<|endoftext|>


<commit_before>/**
 * Ember's ObjectProxy combined with the PromiseProxyMixin.
 * Acts as an object and proxies all properties to the
 * given promise when it resolves.
 *
 * @class PromiseObject
 * @extends ObjectProxy
 * @uses PromiseProxyMixin
 * @constructor
 */
EG.PromiseObject = Em.ObjectProxy.extend(Em.PromiseProxyMixin);

/**
 * Ember's ArrayProxy combined with the PromiseProxyMixin.
 * Acts as an array and proxies all properties to the
 * given promise when it resolves.
 *
 * @class PromiseArray
 * @extends ArrayProxy
 * @uses PromiseProxyMixin
 * @constructor
 */
EG.PromiseArray = Em.ArrayProxy.extend(Em.PromiseProxyMixin);

/**
 * Acts just like `PromiseObject` only it's able to hold the
 * ID and typeKey of a model before it's resolved completely.
 *
 * ```js
 * var user = EG.ModelPromiseObject.create({
 *     promise: this.store.find('user', '52'),
 *     id: '52',
 *     typeKey: 'user'
 * });
 *
 * user.get('isPending'); // true
 * user.get('id'); // '52'
 * user.get('typeKey'); // 'user'
 * ```
 *
 * @class ModelPromiseObject
 * @extends PromiseObject
 * @constructor
 */
EG.ModelPromiseObject = EG.PromiseObject.extend({
	__modelId: null,
	__modelTypeKey: null,

	id: function(key, value) {
		var content = this.get('content');

		if (arguments.length > 1) {
			if (content && content.set) {
				content.set('id', value);
			} else {
				this.set('__modelId', value);
			}
		}

		if (content && content.get) {
			return content.get('id');
		} else {
			return this.get('__modelId');
		}
	}.property('__modelId', 'content.id'),

	typeKey: function(key, value) {
		var content = this.get('content');

		if (arguments.length > 1) {
			if (content && content.set) {
				content.set('typeKey', value);
			} else {
				this.set('__modelTypeKey', value);
			}
		}

		if (content && content.get) {
			return content.get('typeKey');
		} else {
			return this.get('__modelTypeKey');
		}
	}.property('__modelTypeKey', 'content.typeKey'),

	getModel: function() {
		return this.get('content');
	},

	destroy: function() {
		var model = this.getModel();

		if (model && typeof model.destroy === 'function') {
			return model.destroy();
		} else {
			return Em.RSVP.Promise.reject('Can\'t destroy a record that is still loading.');
		}
	}
});<commit_msg>Update documentation.<commit_after>/**
 * Ember's ObjectProxy combined with the PromiseProxyMixin.
 * Acts as an object and proxies all properties to the
 * given promise when it resolves.
 *
 * @class PromiseObject
 * @extends ObjectProxy
 * @uses PromiseProxyMixin
 * @constructor
 */
EG.PromiseObject = Em.ObjectProxy.extend(Em.PromiseProxyMixin);

/**
 * Ember's ArrayProxy combined with the PromiseProxyMixin.
 * Acts as an array and proxies all properties to the
 * given promise when it resolves.
 *
 * @class PromiseArray
 * @extends ArrayProxy
 * @uses PromiseProxyMixin
 * @constructor
 */
EG.PromiseArray = Em.ArrayProxy.extend(Em.PromiseProxyMixin);

/**
 * Acts just like `PromiseObject` only it's able to hold the
 * ID and typeKey of a model before it's resolved completely.
 *
 * ```js
 * var user = EG.ModelPromiseObject.create({
 *     promise: this.store.find('user', '52'),
 *     id: '52',
 *     typeKey: 'user'
 * });
 *
 * user.get('isPending'); // true
 * user.get('id'); // '52'
 * user.get('typeKey'); // 'user'
 * ```
 *
 * @class ModelPromiseObject
 * @extends PromiseObject
 * @constructor
 */
EG.ModelPromiseObject = EG.PromiseObject.extend({
	__modelId: null,
	__modelTypeKey: null,

	id: function(key, value) {
		var content = this.get('content');

		if (arguments.length > 1) {
			if (content && content.set) {
				content.set('id', value);
			} else {
				this.set('__modelId', value);
			}
		}

		if (content && content.get) {
			return content.get('id');
		} else {
			return this.get('__modelId');
		}
	}.property('__modelId', 'content.id'),

	typeKey: function(key, value) {
		var content = this.get('content');

		if (arguments.length > 1) {
			if (content && content.set) {
				content.set('typeKey', value);
			} else {
				this.set('__modelTypeKey', value);
			}
		}

		if (content && content.get) {
			return content.get('typeKey');
		} else {
			return this.get('__modelTypeKey');
		}
	}.property('__modelTypeKey', 'content.typeKey'),

	/**
	 * Returns the underlying model for this promise. If the promise
	 * isn't resolved yet, the model will be `undefined`.
	 *
	 * @method getModel
	 * @return {Model}
	 */
	getModel: function() {
		return this.get('content');
	},

	/**
	 * Proxies to the underlying model's `destroy` method.
	 * Will return a rejected promise if the promise isn't resolved yet.
	 *
	 * @method destroy
	 * @return {Promise}
	 */
	destroy: function() {
		var model = this.getModel();

		if (model && typeof model.destroy === 'function') {
			return model.destroy();
		} else {
			return Em.RSVP.Promise.reject('Can\'t destroy a record that is still loading.');
		}
	}
});<|endoftext|>


<commit_before>/**
 * @ngdoc directive
 * @name ng.directive:rcSubmit
 *
 * @description
 * Alternative to ngSubmit that verifies the ngFormController is valid before
 * executing the given expression.  Otherwise it cancels the event.
 *
 * @element form
 * @param {expression} rcSubmit {@link guide/expression Expression} to eval.
 */
var rcSubmitDirective = {
  'rcSubmit': ['$parse', '$q', '$timeout', function ($parse, $q, $timeout) {
    return {
      restrict: 'A',
      require: ['rcSubmit', '?form'],
      controller: ['$scope', function ($scope) {

        var formController = null;
        var attemptHandlers = [];
        var submitCompleteHandlers = [];

        this.attempted = false;
        this.submitInProgress = false;

        this.onAttempt = function(handler) {
          attemptHandlers.push(handler);
        };

        this.setAttempted = function() {
          this.attempted = true;

          angular.forEach(attemptHandlers, function (handler) {
            handler();
          });
        };

        this.setFormController = function(controller) {
          formController = controller;
        };

        this.needsAttention = function (fieldModelController) {
          if (!formController) return false;

          if (fieldModelController) {
            return fieldModelController.$invalid &&
                   (fieldModelController.$dirty || this.attempted);
          } else {
            return formController && formController.$invalid &&
                   (formController.$dirty || this.attempted);
          }
        };

        this.onSubmitComplete = function (handler) {

          submitCompleteHandlers.push(handler);
        };

        this.setSubmitComplete = function (success, data) {

          angular.forEach(submitCompleteHandlers, function (handler) {
            handler({ 'success': success, 'data': data });
          });
        };
      }],
      compile: function(cElement, cAttributes, transclude) {
        return {
          pre: function(scope, formElement, attributes, controllers) {

            var submitController = controllers[0];
            var formController = (controllers.length > 1) ? controllers[1] : null;

            submitController.setFormController(formController);

            scope.rc = scope.rc || {};
            scope.rc[attributes.name] = submitController;
          },
          post: function(scope, formElement, attributes, controllers) {

            var submitController = controllers[0];
            var formController = (controllers.length > 1) ? controllers[1] : null;
            var fn = $parse(attributes.rcSubmit);

            formElement.bind('submit', function () {
              submitController.setAttempted();
              if (!scope.$$phase) scope.$apply();

              if (!formController.$valid) return false;

              var doSubmit = function () {

                submitController.submitInProgress = true;
                if (!scope.$$phase) scope.$apply();

                var returnPromise = $q.when(fn(scope, { $event: event }));

                returnPromise.then(function (result) {
                  submitController.submitInProgress = false;
                  if (!scope.$$phase) scope.$apply();

                  // This is a small hack.  We want the submitInProgress
                  // flag to be applied to the scope before we actually
                  // raise the submitComplete event. We do that by
                  // using angular's $timeout service which even without
                  // a timeout value specified will not fire until after
                  // the scope is digested.
                  $timeout(function() {
                    submitController.setSubmitComplete(true, result);
                  });

                }, function (error) {
                  submitController.submitInProgress = false;
                  if (!scope.$$phase) scope.$apply();
                     submitController.setSubmitComplete(false, error);
                });
              };

              if (!scope.$$phase) {
                scope.$apply(doSubmit);
              } else {
                doSubmit();
                if (!scope.$$phase) scope.$apply();
              }
            });
          }
        };
      }
    };
  }]
};<commit_msg>Update rcSubmit.js<commit_after>/**
 * @ngdoc directive
 * @name ng.directive:rcSubmit
 *
 * @description
 * Alternative to ngSubmit that verifies the ngFormController is valid before
 * executing the given expression.  Otherwise it cancels the event.
 *
 * @element form
 * @param {expression} rcSubmit {@link guide/expression Expression} to eval.
 */
var rcSubmitDirective = {
  'rcSubmit': ['$parse', '$q', '$timeout', function ($parse, $q, $timeout) {
    return {
      restrict: 'A',
      require: ['rcSubmit', '?form'],
      controller: ['$scope', function ($scope) {

        var formController = null;
        var attemptHandlers = [];
        var submitCompleteHandlers = [];

        this.attempted = false;
        this.submitInProgress = false;

        this.onAttempt = function(handler) {
          attemptHandlers.push(handler);
        };

        this.setAttempted = function() {
          this.attempted = true;

          angular.forEach(attemptHandlers, function (handler) {
            handler();
          });
        };

        this.setFormController = function(controller) {
          formController = controller;
        };

        this.needsAttention = function (fieldModelController) {
          if (!formController) return false;

          if (fieldModelController) {
            return fieldModelController.$invalid &&
                   (fieldModelController.$dirty || this.attempted);
          } else {
            return formController && formController.$invalid &&
                   (formController.$dirty || this.attempted);
          }
        };

        this.onSubmitComplete = function (handler) {

          submitCompleteHandlers.push(handler);
        };

        this.setSubmitComplete = function (success, data) {

          angular.forEach(submitCompleteHandlers, function (handler) {
            handler({ 'success': success, 'data': data });
          });
        };
      }],
      compile: function(cElement, cAttributes, transclude) {
        return {
          pre: function(scope, formElement, attributes, controllers) {

            var submitController = controllers[0];
            var formController = (controllers.length > 1) ? controllers[1] : null;

            submitController.setFormController(formController);

            scope.rc = scope.rc || {};
            scope.rc[attributes.name] = submitController;
          },
          post: function(scope, formElement, attributes, controllers) {

            var submitController = controllers[0];
            var formController = (controllers.length > 1) ? controllers[1] : null;
            var fn = $parse(attributes.rcSubmit);

            formElement.bind('submit', function (event) {
              submitController.setAttempted();
              if (!scope.$$phase) scope.$apply();

              if (!formController.$valid) return false;

              var doSubmit = function () {

                submitController.submitInProgress = true;
                if (!scope.$$phase) scope.$apply();

                var returnPromise = $q.when(fn(scope, { $event: event }));

                returnPromise.then(function (result) {
                  submitController.submitInProgress = false;
                  if (!scope.$$phase) scope.$apply();

                  // This is a small hack.  We want the submitInProgress
                  // flag to be applied to the scope before we actually
                  // raise the submitComplete event. We do that by
                  // using angular's $timeout service which even without
                  // a timeout value specified will not fire until after
                  // the scope is digested.
                  $timeout(function() {
                    submitController.setSubmitComplete(true, result);
                  });

                }, function (error) {
                  submitController.submitInProgress = false;
                  if (!scope.$$phase) scope.$apply();
                     submitController.setSubmitComplete(false, error);
                });
              };

              if (!scope.$$phase) {
                scope.$apply(doSubmit);
              } else {
                doSubmit();
                if (!scope.$$phase) scope.$apply();
              }
            });
          }
        };
      }
    };
  }]
};
<|endoftext|>


<commit_before>(function(oCanvas, window, document, undefined){

	// Define the class
	var text = function (settings, thecore) {

		// Method for setting a font property. Updates both obj.font and obj.property
		var setFontProperty = function (_this, property, value, objectProperty, thecore) {
			var font = thecore.style.getFont(_this.font);
			font[property] = value;
			_this._.font = thecore.style.getFont(font, "string");
			_this._[objectProperty] = value;
		};

		// Return an object when instantiated
		return oCanvas.extend({
			core: thecore,

			type: "text",
			shapeType: "rectangular",

			// Default properties
			text: "",
			align: "start",
			baseline: "alphabetic",
			_: {
				font: "normal normal normal 16px/1.5 'Helvetica Neue', Arial, Helvetica, sans-serif",
				style: "_normal",
				variant: "_normal",
				weight: "_normal",
				size: 16,
				lineHeight: 1.5,
				family: "'Helvetica Neue', Arial, Helvetica, sans-serif"
			},

			// Setters for font properties
			set font (value) {

				// Convert the value to a correct string if it is not a string
				if (typeof value !== "string") {
					value = this.core.style.getFont(value, "string");
				}

				// Get font object and set styles
				var font = this.core.style.getFont(value);
				value = this.core.style.getFont(font, "string");
				this._.style = font.style;
				this._.variant = font.variant;
				this._.weight = font.weight;
				this._.size = font.size;
				this._.lineHeight = font.lineHeight;
				this._.family = font.family;
				this._.font = value;
			},
			set style (style) {
				setFontProperty(this, "style", style, "style", this.core);
			},
			set variant (variant) {
				setFontProperty(this, "variant", variant, "variant", this.core);
			},
			set weight (weight) {
				setFontProperty(this, "weight", weight, "weight", this.core);
			},
			set size (size) {
				setFontProperty(this, "size", size, "size", this.core);
			},
			set lineHeight (lineHeight) {
				setFontProperty(this, "lineHeight", lineHeight, "lineHeight", this.core);
			},
			set family (family) {
				setFontProperty(this, "family", family, "family", this.core);
			},

			// Getters for font properties
			get font () {
				return this._.font;
			},
			get style () {
				return this._.style;
			},
			get variant () {
				return this._.variant;
			},
			get weight () {
				return this._.weight;
			},
			get size () {
				return this._.size;
			},
			get lineHeight () {
				return this._.lineHeight;
			},
			get family () {
				return this._.family;
			},

			// Method for drawing the object to the canvas
			draw: function (cb) {
				var canvas = this.core.canvas;

				canvas.beginPath();

				canvas.font = this.font;
				canvas.textAlign = this.align;
				canvas.textBaseline = this.baseline;

				// Draw the text as a stroke if a stroke is specified
				if (this.strokeWeight > 0) {
					canvas.lineWidth = this.strokeWeight;
					canvas.strokeStyle = this.strokeColor;
					canvas.strokeText(this.text, this.abs_x, this.abs_y);
					canvas.stroke();
				}

				// Draw the text normally if a fill color is specified
				if (this.fill !== "") {
					canvas.fillStyle = this.fill;
					canvas.fillText(this.text, this.abs_x, this.abs_y);
					canvas.fill();
				}

				canvas.closePath();

				if (cb) {
					cb.call(this);
				}
				return this;
			}

		}, settings);
	};

	// Register the display object
	oCanvas.registerDisplayObject("text", text);

})(oCanvas, window, document);<commit_msg>Added a method in display object text that sets width and height whenever something is changed.<commit_after>(function(oCanvas, window, document, undefined){

	// Define the class
	var text = function (settings, thecore) {

		// Method for setting a font property. Updates both obj.font and obj.property
		var setFontProperty = function (_this, property, value, objectProperty, thecore) {
			var font = thecore.style.getFont(_this.font);
			font[property] = value;
			_this._.font = thecore.style.getFont(font, "string");
			_this._[objectProperty] = value;
		};

		// Return an object when instantiated
		return oCanvas.extend({
			core: thecore,

			type: "text",
			shapeType: "rectangular",

			// Default properties
			align: "start",
			baseline: "alphabetic",
			_: {
				font: "normal normal normal 16px/1.5 'Helvetica Neue', Arial, Helvetica, sans-serif",
				style: "_normal",
				variant: "_normal",
				weight: "_normal",
				size: 16,
				lineHeight: 1.5,
				family: "'Helvetica Neue', Arial, Helvetica, sans-serif",
				text: "",
				width: 0,
				height: 0
			},

			// Setters for font properties
			set font (value) {

				// Convert the value to a correct string if it is not a string
				if (typeof value !== "string") {
					value = this.core.style.getFont(value, "string");
				}

				// Get font object and set styles
				var font = this.core.style.getFont(value);
				value = this.core.style.getFont(font, "string");
				this._.style = font.style;
				this._.variant = font.variant;
				this._.weight = font.weight;
				this._.size = font.size;
				this._.lineHeight = font.lineHeight;
				this._.family = font.family;
				this._.font = value;

				this.setDimensions();
			},
			set style (style) {
				setFontProperty(this, "style", style, "style", this.core);
				this.setDimensions();
			},
			set variant (variant) {
				setFontProperty(this, "variant", variant, "variant", this.core);
				this.setDimensions();
			},
			set weight (weight) {
				setFontProperty(this, "weight", weight, "weight", this.core);
				this.setDimensions();
			},
			set size (size) {
				setFontProperty(this, "size", size, "size", this.core);
				this.setDimensions();
			},
			set lineHeight (lineHeight) {
				setFontProperty(this, "lineHeight", lineHeight, "lineHeight", this.core);
				this.setDimensions();
			},
			set family (family) {
				setFontProperty(this, "family", family, "family", this.core);
				this.setDimensions();
			},
			set text (text) {
				this._.text = text;
				this.setDimensions();
			},
			set width (value) {
				return;
			},
			set height (value) {
				return;
			},

			// Getters for font properties
			get font () {
				return this._.font;
			},
			get style () {
				return this._.style;
			},
			get variant () {
				return this._.variant;
			},
			get weight () {
				return this._.weight;
			},
			get size () {
				return this._.size;
			},
			get lineHeight () {
				return this._.lineHeight;
			},
			get family () {
				return this._.family;
			},
			get text () {
				return this._.text;
			},
			get width () {
				return this._.width;
			},
			get height () {
				return this._.height;
			},

			// Method for initializing the text and get dimensions
			init: function () {
				this._.initialized = true;
				this.setDimensions();
			},

			// Method for setting width/height when something has changed
			setDimensions: function () {
				if (this.initialized) {
					var canvas = this.core.canvas,
						metrics;

					// Measure the text
					canvas.fillStyle = this.fill;
					canvas.font = this.font;
					metrics = canvas.measureText(this.text);

					// Set new dimensions
					this._.width = metrics.width;
					this._.height = this.size;
				}
			},

			// Method for drawing the object to the canvas
			draw: function (cb) {
				var canvas = this.core.canvas;

				canvas.beginPath();

				canvas.font = this.font;
				canvas.textAlign = this.align;
				canvas.textBaseline = this.baseline;

				// Draw the text as a stroke if a stroke is specified
				if (this.strokeWeight > 0) {
					canvas.lineWidth = this.strokeWeight;
					canvas.strokeStyle = this.strokeColor;
					canvas.strokeText(this.text, this.abs_x, this.abs_y);
					canvas.stroke();
				}

				// Draw the text normally if a fill color is specified
				if (this.fill !== "") {
					canvas.fillStyle = this.fill;
					canvas.fillText(this.text, this.abs_x, this.abs_y);
					canvas.fill();
				}

				canvas.closePath();

				if (cb) {
					cb.call(this);
				}
				return this;
			}

		}, settings);
	};

	// Register the display object
	oCanvas.registerDisplayObject("text", text, "init");

})(oCanvas, window, document);<|endoftext|>


<commit_before>/**
 * Common DOM Element unit methods
 *
 * Credits:
 *   Most of the naming system in the module inspired by
 *     - Prototype (http://prototypejs.org)   Copyright (C) Sam Stephenson
 *
 * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
 */
Element.addMethods({
  /**
   * sets the element attributes
   *
   * @param String attr name or Object attributes hash
   * @param mixed attribute value
   * @return Element self
   */
  set: function(hash, value) {
    if (typeof(hash) === 'string') { var val = {}; val[hash] = value; hash = val; }

    for (var key in hash) {
      // some attributes are not available as properties
      if (this[key] === undefined) {
        this.setAttribute(key, ''+hash[key]);
      }
      this[key] = hash[key];
    }

    return this;
  },

  /**
   * returns the attribute value for the name
   *
   * @param String attr name
   * @return mixed value
   */
  get: function(name) {
    var value = this[name] || this.getAttribute(name);
    return value === '' ? null : value;
  },

  /**
   * checks if the element has that attribute
   *
   * @param String attr name
   * @return Boolean check result
   */
  has: function(name) {
    return this.get(name) !== null;
  },

  /**
   * erases the given attribute of the element
   *
   * @param String attr name
   * @return Element self
   */
  erase: function(name) {
    this.removeAttribute(name);
    return this;
  },

  /**
   * checks if the elemnt is hidden
   *
   * NOTE: will check css level computed styles too
   *
   * @return boolean check result
   */
  hidden: function() {
    return this.getStyle('display') == 'none';
  },

  /**
   * checks if the element is visible
   *
   * @return boolean check result
   */
  visible: function() {
    return !this.hidden();
  },

  /**
   * hides the element
   *
   * @param String optional effect name
   * @param Object the optional effect options
   * @return Element self
   */
  hide: function(effect, options) {
    this._$pd = this.getStyle('display');
    this.style.display = 'none';
    return this;
  },

  /**
   * shows the element
   *
   * @param String optional effect name
   * @param Object the optional effect options
   * @return Element self
   */
  show: function(effect, options) {
    // setting 'block' for the divs and 'inline' for the other elements hidden on the css-level
    var value = this.tagName == 'DIV' ? 'block' : 'inline';
    this.style.display = this._$pd == 'none' ? value : this._$pd || value;
    return this;
  },

  /**
   * toggles the visibility state of the element
   *
   * @param String optional effect name
   * @param Object the optional effect options
   * @return Element self
   */
  toggle: function(effect, options) {
    return this[this.hidden() ? 'show' : 'hide'](effect, options);
  },

  /**
   * shows the element and hides all the sibligns
   *
   * @param String optional effect name
   * @param Object the optional effect options
   * @return Element self
   */
  radio: function(effect, options) {
    this.siblings().each('hide', effect, options);
    return this.show();
  }
});<commit_msg>#158 Element#show fix<commit_after>/**
 * Common DOM Element unit methods
 *
 * Credits:
 *   Most of the naming system in the module inspired by
 *     - Prototype (http://prototypejs.org)   Copyright (C) Sam Stephenson
 *
 * Copyright (C) 2008 Nikolay V. Nemshilov aka St. <nemshilov#gma-il>
 */
Element.addMethods({
  /**
   * sets the element attributes
   *
   * @param String attr name or Object attributes hash
   * @param mixed attribute value
   * @return Element self
   */
  set: function(hash, value) {
    if (typeof(hash) === 'string') { var val = {}; val[hash] = value; hash = val; }

    for (var key in hash) {
      // some attributes are not available as properties
      if (this[key] === undefined) {
        this.setAttribute(key, ''+hash[key]);
      }
      this[key] = hash[key];
    }

    return this;
  },

  /**
   * returns the attribute value for the name
   *
   * @param String attr name
   * @return mixed value
   */
  get: function(name) {
    var value = this[name] || this.getAttribute(name);
    return value === '' ? null : value;
  },

  /**
   * checks if the element has that attribute
   *
   * @param String attr name
   * @return Boolean check result
   */
  has: function(name) {
    return this.get(name) !== null;
  },

  /**
   * erases the given attribute of the element
   *
   * @param String attr name
   * @return Element self
   */
  erase: function(name) {
    this.removeAttribute(name);
    return this;
  },

  /**
   * checks if the elemnt is hidden
   *
   * NOTE: will check css level computed styles too
   *
   * @return boolean check result
   */
  hidden: function() {
    return this.getStyle('display') == 'none';
  },

  /**
   * checks if the element is visible
   *
   * @return boolean check result
   */
  visible: function() {
    return !this.hidden();
  },

  /**
   * hides the element
   *
   * @param String optional effect name
   * @param Object the optional effect options
   * @return Element self
   */
  hide: function(effect, options) {
    this._$pd = this.getStyle('display');
    this.style.display = 'none';
    return this;
  },

  /**
   * shows the element
   *
   * @param String optional effect name
   * @param Object the optional effect options
   * @return Element self
   */
  show: function(effect, options) {
    if (this.getStyle('display') == 'none') {
      // setting 'block' for the divs and 'inline' for the other elements hidden on the css-level
      var value = this.tagName == 'DIV' ? 'block' : 'inline';
      this.style.display = this._$pd == 'none' ? value : this._$pd || value;
    }
    return this;
  },

  /**
   * toggles the visibility state of the element
   *
   * @param String optional effect name
   * @param Object the optional effect options
   * @return Element self
   */
  toggle: function(effect, options) {
    return this[this.hidden() ? 'show' : 'hide'](effect, options);
  },

  /**
   * shows the element and hides all the sibligns
   *
   * @param String optional effect name
   * @param Object the optional effect options
   * @return Element self
   */
  radio: function(effect, options) {
    this.siblings().each('hide', effect, options);
    return this.show();
  }
});<|endoftext|>