milo

Component

declaration
 Component 

Option name Type Description
scope Scope

scope to which component will belong. It is usually a top level scope object returned by milo.binder or scope property of Container facet.

element Element

DOM element that component is attached to

name String

component name, should be unique in the scope of component

componentInfo ComponentInfo

instance of ComponentInfo class that can be used to create a copy of component TODO try removing it

return Component

milo.Component
Base Component class. Subclass of FacetedObject, but none of this class methods should be directly used with component.
Its constructor passes its parameters, including its scope, DOM element and name to init method.
The constructor of Component class rarely needs to be used directly, as milo.binder creates components when it scans DOM tree.
Component.createComponentClass should be used to create a subclass of Component class with configured facets.

####Component instance properties####

  • el - DOM element that component is attached to. If the second component is attached to the same DOM element, the warning will be logged to console. To get component reference from DOM element use Component.getComponent class method. To inspect component via element in browser check ___milo_component property of element (property name be changed using milo.config).
  • scope - parent scope object, an instance of Scope class. To get parent component use getScopeParent instance method of component. The actual path to get parent of conponent is component.scope._hostObject.owner, where _hostObject refers to Container facet of parent component and owner to the parent itself. The children of component are accessible via the scope of its container facet: component.container.scope. The scope hierarchy can be the same or different as the DOM hierarchy - DOM children of the component will be on the same scope as component if it does not have Container facet and in the scope of its Container facet if it has it. See Scope.
  • name - the name of component, should be unique for the scope where component belongs. To find component in scope the component's name should be used as property of scope object. See Scope.
  • facets - map of references of all component's facets (facet names are lowercase in this map). All facets can be accessed directly as properties of component, this property can be used to iterate facets (it is used in this way in allFacets component's instance method that allows to call method with the same name on all facets).
  • extraFacets - an array of names of facets that are added to component and do not form the part of component's class.
  • _messenger - the reference to component's messenger. Rarely needs to be used directly as all commonly used methods of mesenger are available directly on component.

####Component events####

  • 'childrenbound' - synchronously dispatched when children of DOM element which compnent is connected to are connected to components. The event is dispatched when component is created with milo.binder (as is almost always the case, as all Component class methods that create/copy components use milo.binder internally - component constructor and Component.create methods are not used in framework outside of milo.binder and rarely if ever need to be used in aplication).
  • 'addedtoscope' - synchronously dispatched when component is added to scope.
  • 'stateready' - aynchronously dispatched when component (together with its scope children) is created with Component.createFromState (or createFromDataTransfer) method. Can be dispatched by application if the component's state is set with some other mechanism. This event is not used in milo, it can be used in application in particular subclasses of component.
  • 'getstatestarted' - emitted synchronously just before getState executes so components and facets can clean up their state for serialization.
  • 'getstatecompleted' - emitted asynchronously after getState executes so components and facets can restore their state after serialization.

####Component "lifecycle"####

  1. Component constructor is called. Component's constructor simply calls constructor of FacetedObject that is a superclass of Component. Subclasses of Component should not implement their own constructor, they can optionally implement init method, but most components do not need to do it.
  2. constructors and init methods of all facets are called in sequence. Same as components, facet do not implement their constructors, they can optionally implement init and start methods (see below). Inside init method there should be only general initialization code without any dependency on component itself (it is not ready yet) and other facets (as there is no specific facets creation order). If facet implements init method it MUST call inherited init with ComponentFacet.prototype.init.apply(this, arguments).
  3. init method of component is called. At this point all facets are created but facets still can be not ready as they can have initialization code in start method. If component subclass implements init method it MUST call inherited method with <Superclass>.prototype.init.apply(this, arguments), where is Component or another superclass the component is a subclass of.
  4. check method of all facets is called. This method adds facets that are not part of the component declaration (being part of the class or explicitely listed in bind attribute) but are required by facets that the compnent already has. Subclasses of ComponentFacet do not need to implement this method.
  5. start method of all facets is called. This method is usually implemented by ComponentFacet subclasses and it can have any initialization code that depends on component or on other facets that are the dependencies of a facet. Inherited start method should be called int he same way as written above.
  6. start method of component is called. This component method can be implemented by subclasses if they need to have some initialization code that depends on some facets and requires that these facets are fully inialized. Often such code also depends on component's scope children as well so this code should be inside 'childrenbound' event subscriber.
  7. 'addedtoscope' event is dispatched when component is added to its parent's scope or to top level scope created by milo.binder.
  8. component's children are created (steps 1-6 above are followed for each child).
  9. 'childrenbound' event is dispatched when all component's children are created and added to their scope (see event description below).
  10. 'stateready' event is dispatched for component and all its children when component is create from state (see event description below).
  11. at this point component is in the "interactive" state when it and its facets will only respond to messages/events that they subscribed to during initialization.
var Component = _.createSubclass(FacetedObject, 'Component', true);

module.exports = Component;

_registerWithDomStorage('Component');
_.extend(Component, {
    createComponentClass: Component$$createComponentClass,
    create: Component$$create,
    copy: Component$$copy,
    createOnElement: Component$$createOnElement,
    isComponent: componentUtils.isComponent,
    getComponent: componentUtils.getComponent,
    getContainingComponent: componentUtils.getContainingComponent,
    createFromState: Component$$createFromState,
    createFromDataTransfer: Component$$createFromDataTransfer
});
delete Component.createFacetedClass;

####Component instance methods####

#####Messenger methods available on component#####

_.extendProto(Component, {
    init: Component$init,
    start: Component$start,
    createElement: Component$createElement,
    hasFacet: Component$hasFacet,
    addFacet: Component$addFacet,
    allFacets: Component$allFacets,
    rename: Component$rename,
    remove: Component$remove,
    insertInto: Component$insertInto,

    getState: Component$getState,
    getTransferState: Component$getTransferState,
    _getState: Component$_getState,
    setState: Component$setState,
    
    getScopeParent: Component$getScopeParent,
    getTopScopeParent: Component$getTopScopeParent,
    getScopeParentWithClass: Component$getScopeParentWithClass,
    getTopScopeParentWithClass: Component$getTopScopeParentWithClass,

    setScopeParentFromDOM: Component$setScopeParentFromDOM,

    walkScopeTree: Component$walkScopeTree,

    treePathOf: Component$treePathOf,
    getComponentAtTreePath: Component$getComponentAtTreePath,
    insertAtTreePath: Component$insertAtTreePath,

    broadcast: Component$broadcast,
    destroy: Component$destroy,
    isDestroyed: Component$isDestroyed
});

MESSENGER_PROPERTY

declaration
 MESSENGER_PROPERTY 

Expose Messenger methods on Component prototype

var MESSENGER_PROPERTY = '_messenger';
Messenger.useWith(Component, MESSENGER_PROPERTY, Messenger.defaultMethods);


var COMPONENT_DATA_TYPE_PREFIX = 'x-application/milo-component';
var COMPONENT_DATA_TYPE_REGEX = /x-application\/milo-component\/([a-z_$][0-9a-z_$]*)(?:\/())/i;

Component$$createComponentClass

function
 Component$$createComponentClass() 

Option name Type Description
name String

class name

facetsConfig Object.<Object>, Array.<String>

map of facets configuration. If some facet does not require configuration, undefined should be passed as the configuration for the facet.
If no facet requires configuration, the array of facets names can be passed.

return Subclass.<Component>

Component class method
Creates a subclass of component from the map of configured facets.
This method wraps and replaces createFacetedClass class method of FacetedObject.
Unlike createFacetedClass, this method take facet classes from registry by their name, so only map of facets configuration needs to be passed. All facets classes should be subclasses of ComponentFacet

function Component$$createComponentClass(name, facetsConfig) {
    // convert array of facet names to map of empty facets configurations
    if (Array.isArray(facetsConfig)) {
        var configMap = {};
        facetsConfig.forEach(function(fct) {
            var fctName = _.firstLowerCase(fct);
            configMap[fctName] = {};
        });
        facetsConfig = configMap;
    }

    // construct map of facets classes from facetRegistry
    var facetsClasses;
    if (typeof facetsConfig == 'object' && _.keys(facetsConfig).length) {
        facetsClasses = {};
        _.eachKey(facetsConfig, function(fctConfig, fct) {
            var fctName = _.firstLowerCase(fct);
            var fctClassName = _.firstUpperCase(fct);
            facetsClasses[fctName] = facetsRegistry.get(fctClassName);
        });
    }

    // create subclass of Component using method of FacetedObject
    var ComponentClass = FacetedObject.createFacetedClass.call(this, name, facetsClasses, facetsConfig);
    
    _registerWithDomStorage(name);

    return ComponentClass;
}


function _registerWithDomStorage(className) {
    DOMStorage.registerDataType(className, Component_domStorageSerializer, Component_domStorageParser);
}


function Component_domStorageSerializer(component) {
    var state = component.getState();
    return JSON.stringify(state);   
}


function Component_domStorageParser(compStr, compClassName) {
    var state = _.jsonParse(compStr);
    if (state)
        return Component.createFromState(state);
}

Component$$create

function
 Component$$create() 

Option name Type Description
info ComponentInfo
throwOnErrors Boolean

If set to false, then errors will only be logged to console. True by default.

return Component

Component class method
Creates component from ComponentInfo (used by milo.binder and to copy component)
Component of any registered class (see componentsRegistry) with any additional registered facets (see facetsRegistry) can be created using this method.

function Component$$create(info, throwOnErrors) {
    var ComponentClass = info.ComponentClass;

    if (typeof ComponentClass != 'function') {
        var message = 'create: component class should be function, "' + typeof ComponentClass + '" passed'; 
        if (throwOnErrors === false) {
            logger.error('Component', message, ';using base Component class instead');
            ComponentClass = Component;
        } else
            throw new Error(message);
    }

    var aComponent = new ComponentClass(info.scope, info.el, info.name, info);

    if (info.extraFacetsClasses)
        _.eachKey(info.extraFacetsClasses, function(FacetClass) {
            if (! aComponent.hasFacet(FacetClass))
                aComponent.addFacet(FacetClass, undefined, undefined, throwOnErrors);
        });

    return aComponent;
}

Component$$copy

function
 Component$$copy() 

Option name Type Description
component Component

an instance of Component class or subclass

deepCopy Boolean

optional true to make deep copy of DOM element, otherwise only element without children is copied

return Component

Component class method
Create a copy of component, including a copy of DOM element. Returns a copy of component (of the same class) with new DOM element (not inserted into page).
Component is added to the same scope as the original component.

function Component$$copy(component, deepCopy) {
    check(component, Component);
    check(deepCopy, Match.Optional(Boolean));

    if (deepCopy && !component.container) 
        throw new Error('Cannot deep copy component without container facet');

    // copy DOM element, using Dom facet if it is available
    var newEl = component.dom 
                    ? component.dom.copy(deepCopy)
                    : component.el.cloneNode(deepCopy);

    var ComponentClass = component.constructor;

    // create component of the same class on the element
    var aComponent = ComponentClass.createOnElement(newEl, undefined, component.scope, component.extraFacets);
    var state = component._getState(deepCopy || false);
    aComponent.setState(state);
    _.deferMethod(aComponent, 'broadcast', 'stateready');
    return aComponent;
}

Component$$createOnElement

function
 Component$$createOnElement() 

Option name Type Description
el Element

optional element to attach component to. If element is not passed, it will be created

innerHTML String

optional inner html to insert in element before binding.

rootScope Scope

optional scope to put component in. If not passed, component will be attached to the scope that contains the element. If such scope does not exist, new scope will be created.

extraFacets Array.<String>

list of extra facet to add to component

return Subclass.<Component>

Component class method
Creates an instance of component atached to element. All subclasses of component inherit this method.
Returns the component of the class this method is used with (thecontext of the method call).

function Component$$createOnElement(el, innerHTML, rootScope, extraFacets) {
    check(innerHTML, Match.Optional(String));
    check(rootScope, Match.Optional(Scope));
    check(extraFacets, Match.Optional([String]));

    // "this" refers to the class of component here, as this is a class method
    if (el && innerHTML) el.innerHTML = innerHTML;
    el = el || _createComponentElement.call(this, innerHTML);
    rootScope = rootScope || _findOrCreateComponentRootScope(el);
    var aComponent = _addAttributeAndBindComponent.call(this, el, rootScope, extraFacets);
    aComponent.broadcast('stateready');
    return aComponent;
}

function _createComponentElement(innerHTML) {
    // "this" refers to the class of component here, as this is a class method
    var Dom = facetsRegistry.get('Dom')
        , domFacetConfig = this.getFacetConfig('dom')
        , templateFacetConfig = this.getFacetConfig('template')
        , template = templateFacetConfig && templateFacetConfig.template;

    var elConfig = {
        domConfig: domFacetConfig,
        template: template,
        content: innerHTML
    };

    return Dom.createElement(elConfig);
}

function _findOrCreateComponentRootScope(el) {
    var parent = Component.getContainingComponent(el, false, 'Container');
    return parent ? parent.container.scope : new Scope(el);
}

function _addAttributeAndBindComponent(el, rootScope, extraFacets) {
    // add bind attribute to element
    var attr = new BindAttribute(el);
    // "this" refers to the class of component here, as this is a class method
    attr.compClass = this.name;
    attr.compFacets = extraFacets;
    attr.decorate();

    // should be required here to resolve circular dependency
    var miloBinder = require('../binder');
    miloBinder(el, rootScope);

    return rootScope[attr.compName];
}

Component$$createFromState

function
 Component$$createFromState() 

Option name Type Description
state Object

state from which component will be created

rootScope Scope

scope to which component will be added

newUniqueName Boolean

optional true to create component with the name different from the original one. False by default.

throwOnErrors Boolean

If set to false, then errors will only be logged to console. True by default.

return Component

component

Component class method
Creates component from component state, that includes information about its class, extra facets, facets data and all scope children.
This is used to save/load, copy/paste and drag/drop component

function Component$$createFromState(state, rootScope, newUniqueName, throwOnErrors) {
    check(state, Match.ObjectIncluding({
        compName: Match.Optional(String),
        compClass: Match.Optional(String),
        extraFacets: Match.Optional([String]),
        facetsStates: Match.Optional(Object),
        outerHTML: String
    }));

    var miloBinder = require('../binder');

    // create wrapper element optionally renaming component
    var wrapEl = _createComponentWrapElement(state, newUniqueName);

    // instantiate all components from HTML
    var scope = miloBinder(wrapEl, undefined, undefined, throwOnErrors);

    // as there should only be one component, call to _any will return it
    var component = scope._any();

    // set component's scope
    if (rootScope) {
        component.scope = rootScope;
        rootScope._add(component);
    }

    // restore component state
    component.setState(state);
    _.deferMethod(component, 'broadcast', 'stateready');

    return component;   
}


// used by Component$$createFromState
function _createComponentWrapElement(state, newUniqueName) {
    var wrapEl = document.createElement('div');
    wrapEl.innerHTML = state.outerHTML;

    var children = domUtils.children(wrapEl);
    if (children.length != 1)
        throw new Error('cannot create component: incorrect HTML, elements number: ' + children.length + ' (should be 1)');
    var compEl = children[0];
    var attr = new BindAttribute(compEl);
    attr.compName = newUniqueName ? miloComponentName() : state.compName;
    attr.compClass = state.compClass;
    attr.compFacets = state.extraFacets;
    attr.decorate();

    return wrapEl;
}

Component$$createFromDataTransfer

function
 Component$$createFromDataTransfer() 

Option name Type Description
dataTransfer DataTransfer

Data transfer

Creates a component from a DataTransfer object (if possible)

function Component$$createFromDataTransfer(dataTransfer) {
    var dataType = _.find(dataTransfer.types, function (type) {
        return COMPONENT_DATA_TYPE_REGEX.test(type);
    });
    if (!dataType) return;

    var state = _.jsonParse(dataTransfer.getData(dataType));
    if (!state) return;

    return Component.createFromState(state, undefined, true);
}

Component$init

function
 Component$init() 

Option name Type Description
scope Scope

scope to which component will belong. It is usually a top level scope object returned by milo.binder or scope property of Container facet.

element Element

DOM element that component is attached to

name String

component name, should be unique in the scope of component

componentInfo ComponentInfo

instance of ComponentInfo class that can be used to create a copy of component TODO try removing it

Component instance method.
Initializes component. Automatically called by inherited constructor of FacetedObject.
Subclasses should call inherited init methods:

Component.prototype.init.apply(this, arguments)
function Component$init(scope, element, name, componentInfo) {
    // create DOM element if it wasn't passed to Constructor
    this.el = element || this.createElement();

    // store reference to component on DOM element
    if (this.el) {
        // check that element does not have a component already atached
        var elComp = this.el[config.componentRef];
        if (elComp)
            logger.warn('component ' + name + ' attached to element that already has component ' + elComp.name);

        this.el[config.componentRef] = this;
    }

    _.defineProperties(this, {
        componentInfo: componentInfo,
        extraFacets: []
    }, _.ENUM);

    this.name = name;
    this.scope = scope;

    // create component messenger
    var messenger = new Messenger(this);
    _.defineProperty(this, MESSENGER_PROPERTY, messenger);

    // check all facets dependencies (required facets)
    this.allFacets('check');

    // start all facets
    this.allFacets('start');

    // call start method if it's defined in subclass
    if (this.start) this.start();
}

Component$start

function
 Component$start() 

This is a stub to avoid confusion whether the method of superclass should be called in subclasses
The start method of subclass instance is called once all the facets are created, initialized and started (see above)

function Component$start() {}

Component$createElement

function
 Component$createElement() 

Component instance method.
Initializes the element which this component is bound to

This method is called when a component is instantiated outside the DOM and
will generate a new element for the component.

function Component$createElement() {
    if (typeof document == 'undefined')
        return;

    this.el = this.dom
                ? this.dom.createElement()
                : document.createElement('DIV');

    return this.el;
}

Component$hasFacet

function
 Component$hasFacet() 

Option name Type Description
facetNameOrClass Function, String
return Boolean

Component instance method
Returns true if component has facet

function Component$hasFacet(facetNameOrClass) {
    var facetName = _.firstLowerCase(typeof facetNameOrClass == 'function'
                                        ? facetNameOrClass.name
                                        : facetNameOrClass);

    var facet = this[facetName];
    if (! facet instanceof ComponentFacet)
        logger.warn('expected facet', facetName, 'but this property name is used for something else');

    return !! facet;
}

Component$addFacet

function
 Component$addFacet() 

Option name Type Description
facetNameOrClass String, Subclass.<Component>

name of facet class or the class itself. If name is passed, the class will be retireved from facetsRegistry

facetConfig Object

optional facet configuration

facetName String

optional facet name. Allows to add facet under a name different from the class name supplied.

throwOnErrors Boolean

If set to false, then errors will only be logged to console. True by default.

Component instance method.
Adds facet with given name or class to the instance of Component (or its subclass).

function Component$addFacet(facetNameOrClass, facetConfig, facetName, throwOnErrors) {
    check(facetNameOrClass, Match.OneOf(String, Match.Subclass(ComponentFacet)));
    check(facetConfig, Match.Optional(Object));
    check(facetName, Match.Optional(String));

    var FacetClass;
    // if only name passed, retrieve facet class from registry
    if (typeof facetNameOrClass == 'string') {
        var facetClassName = _.firstUpperCase(facetNameOrClass);
        FacetClass = facetsRegistry.get(facetClassName);
    } else 
        FacetClass = facetNameOrClass;

    if (!facetName)
        facetName = _.firstLowerCase(FacetClass.name);

    this.extraFacets.push(facetName);

    // add facet using method of FacetedObject
    var newFacet = FacetedObject.prototype.addFacet.call(this, FacetClass, facetConfig, facetName, throwOnErrors);

    // check depenedencies and start facet
    if (newFacet.check) newFacet.check();
    if (newFacet.start) newFacet.start();
}

Component$allFacets

function
 Component$allFacets() 

Option name Type Description
method String

method name to envoke on the facet

return Object

Component instance method.
Envoke given method with optional parameters on all facets.
Returns the map of values returned by all facets. If the facet doesn't have the method it is simply not called and the value in the map will be undefined.

function Component$allFacets(method) { // ,... arguments
    var args = _.slice(arguments, 1);

    return _.mapKeys(this.facets, function(facet, fctName) {
        if (facet && typeof facet[method] == 'function')
            return facet[method].apply(facet, args);
    });
}

Component$rename

function
 Component$rename() 

Option name Type Description
[name] String

optional new name of component,

[renameInScope] Boolean

optional false to not rename ComponentInfo object in its scope, true by default

Component instance method.

function Component$rename(name, renameInScope) {
    name = name || miloComponentName();
    this.componentInfo.rename(name, false);
    Scope.rename(this, name, renameInScope);
}

Component$remove

function
 Component$remove() 

Option name Type Description
preserveScopeProperty Boolean

true not to delete scope property of component

quiet Boolean

optional true to suppress the warning message if the component is not in scope

Component instance method.
Removes component from its scope.

function Component$remove(preserveScopeProperty, quiet) {
    if (this.scope) {
        this.scope._remove(this.name, quiet);
        if (! preserveScopeProperty)
            delete this.scope;
    }
}

Component$insertInto

function
 Component$insertInto() 

Option name Type Description
parentEl HTMLElement

The element into which the component should be inserted.

referenceEl HTMLElement

(optional) The reference element it should be inserted before.

Component instance method.
Inserts the component into the DOM and attempts to adjust the scope tree accordingly.

function Component$insertInto(parentEl, referenceEl) {
    parentEl.insertBefore(this.el, referenceEl);
    this.setScopeParentFromDOM();
}

Component$getState

function
 Component$getState() 

Component instance method
Retrieves all component state, including information about its class, extra facets, facets data and all scope children.
This information is used to save/load, copy/paste and drag/drop component
Returns component state

function Component$getState() {
    this.broadcast('getstatestarted', { rootComponent: this }, undefined, true);
    var state = this._getState(true);
    state.outerHTML = this.el.outerHTML;
    _.deferMethod(this, 'broadcast', 'getstatecompleted', { rootComponent: this }, undefined, true);
    return state;
}

Component$getTransferState

function
 Component$getTransferState() 

Option name Type Description
options Object

can be used by subclasses.

return Object

Component instance method
Retrieves all component state, including information about its class, extra facets, facets data and all scope children.
This information is used to save/load, copy/paste and drag/drop component
If component has Transfer facet on it, this method retrieves state from this facet
Returns component state

function Component$getTransferState(options) {
    return this.transfer
            ? this.transfer.getState(options)
            : this.getState(options);
}

Component$getScopeParent

function
 Component$getScopeParent() 

Option name Type Description
conditionOrFacet Function, String

optional condition that component should pass (or facet name it should contain)

return Component

Component instance method.
Returns the scope parent of a component.
If conditionOrFacet parameter is not specified, an immediate parent will be returned, otherwise the closest ancestor with a specified facet or passing condition test.

function Component$getScopeParent(conditionOrFacet) {
    return _callGetScopeParent.call(this, _getScopeParent, conditionOrFacet);
}

function _callGetScopeParent(_getScopeParentFunc, conditionOrFacet) {
    check(conditionOrFacet, Match.Optional(Match.OneOf(Function, String)));
    var conditionFunc = componentUtils._makeComponentConditionFunc(conditionOrFacet);
    return _getScopeParentFunc.call(this, conditionFunc);   
}

function _getScopeParent(conditionFunc) {
    var parent;
    try { parent = this.scope._hostObject.owner; } catch(e) {}

    // Where there is no parent, this function will return undefined
    // The parent component is checked recursively
    if (parent) {
        if (! conditionFunc || conditionFunc(parent) )
            return parent;
        else
            return _getScopeParent.call(parent, conditionFunc);
    }
}

Component$getScopeParentWithClass

function
 Component$getScopeParentWithClass() 

Option name Type Description
[ComponentClass] Function

component class that the parent should have, same class by default

return Component

Component instance method
Returns scope parent with a given class, with same class if not specified

function Component$getScopeParentWithClass(ComponentClass) {
    ComponentClass = ComponentClass || this.constructor;
    return _getScopeParent.call(this, function(comp) {
        return comp instanceof ComponentClass;
    });
}

Component$getTopScopeParent

function
 Component$getTopScopeParent() 

Option name Type Description
conditionOrFacet Function, String

optional condition that component should pass (or facet name it should contain)

return Component

Component instance method.
Returns the topmost scope parent of a component.
If conditionOrFacet parameter is not specified, the topmost scope parent will be returned, otherwise the topmost ancestor with a specified facet or passing condition test.

function Component$getTopScopeParent(conditionOrFacet) {
    return _callGetScopeParent.call(this, _getTopScopeParent, conditionOrFacet);
}

function _getTopScopeParent(conditionFunc) {
    var topParent
        , parent = this;
    do {
        parent = _getScopeParent.call(parent, conditionFunc);
        if (parent)
            topParent = parent;
    } while (parent);

    return topParent;
}

Component$getTopScopeParentWithClass

function
 Component$getTopScopeParentWithClass() 

Option name Type Description
[ComponentClass] Function

component class that the parent should have, same class by default

return Component

Component instance method
Returns scope parent with a given class, with same class if not specified

function Component$getTopScopeParentWithClass(ComponentClass) {
    ComponentClass = ComponentClass || this.constructor;
    return _getTopScopeParent.call(this, function(comp) {
        return comp instanceof ComponentClass;
    });
}

Component$setScopeParentFromDOM

function
 Component$setScopeParentFromDOM() 

Component instance method
Finds scope parent of component using DOM tree (unlike getScopeParent that simply goes up the scope tree).
While getScopeParent is faster it may fail if scope chain is not setup yet (e.g., when component has been just inserted).
The scope property of component will be changed to point to scope object of container facet of that parent.
Returned scope parent of the component will be undefined (as well as component's scope property) if no parent in the DOM tree has container facet.
TODO Method will not bind DOM children correctly if component has no container facet.

function Component$setScopeParentFromDOM() {
    var parentEl = this.el.parentNode;

    var parent, foundParent;
    while (parentEl && ! foundParent) {
        parent = Component.getComponent(parentEl);
        foundParent = parent && parent.container;
        parentEl = parentEl.parentNode;
    }

    this.remove(); // remove component from its current scope (if it is defined)
    if (foundParent) {
        this.rename(undefined, false);
        parent.container.scope._add(this);
        return parent;
    }        
}

Component$walkScopeTree

function
 Component$walkScopeTree() 

Option name Type Description
callback
thisArg

Walks component tree, calling provided callback on each component

function Component$walkScopeTree(callback, thisArg) {
    callback.call(thisArg, this);
    if (!this.container) return;
    this.container.scope._each(function(component) {
        component.walkScopeTree(callback, thisArg);
    });
}


function Component$treePathOf(component) {
    return domUtils.treePathOf(this.el, component.el);
}


function Component$getComponentAtTreePath(treePath, nearest) {
    var node = domUtils.getNodeAtTreePath(this.el, treePath, nearest);
    return Component.getComponent(node);
}


function Component$insertAtTreePath(treePath, component, nearest) {
    var wasInserted = domUtils.insertAtTreePath(this.el, treePath, component.el);
    if (wasInserted) component.setScopeParentFromDOM();
    return wasInserted;
}

Component$broadcast

function
 Component$broadcast() 

Option name Type Description
msg String, RegExp

message to be sent

[data] Any

optional message data

[callback] Function

optional callback

[synchronously] Boolean

if it should use postMessageSync

Broadcast message to component and to all its scope children

function Component$broadcast(msg, data, callback, synchronously) {
    var postMethod = synchronously ? 'postMessageSync' : 'postMessage';
    this.walkScopeTree(function(component) {
        component[postMethod](msg, data, callback);
    });
}

Component$destroy

function
 Component$destroy() 

Destroy component: removes component from DOM, removes it from scope, deletes all references to DOM nodes and unsubscribes from all messages both component and all facets

function Component$destroy(opts) {
    if (typeof opts == 'boolean') opts = { quiet: opts };
    else if (!opts) opts = {};

    if (this._destroyed) {
        if (!opts.quiet) logger.warn('Component destroy: component is already destroyed');
        return;
    }
    this.remove(false, opts.quiet);
    this.allFacets('destroy', opts);
    this[MESSENGER_PROPERTY].destroy();
    if (this.el) {
        domUtils.detachComponent(this.el);
        domUtils.removeElement(this.el);
        delete this.el;
    }
    this.componentInfo.destroy();
    this._destroyed = true;
}

Component$isDestroyed

function
 Component$isDestroyed() 

Returns true if component was destroyed

function Component$isDestroyed() {
    return !!this._destroyed;
}