/**
* @copyright 2015 Tridium, Inc. All Rights Reserved.
* @author Gareth Johnson
*/
/**
* Defines {@link baja.ComponentSpace}.
* @module baja/comp/ComponentSpace
*/
define([
"bajaScript/nav",
"bajaScript/baja/comp/compUtil",
"bajaScript/baja/comm/Callback",
"bajaPromises" ], function (
baja,
compUtil,
Callback,
bajaPromises) {
"use strict";
var subclass = baja.subclass,
callSuper = baja.callSuper,
bajaDef = baja.def,
bajaError = baja.error,
strictArg = baja.strictArg,
objectify = baja.objectify,
unlease = compUtil.unlease;
/**
* Represents a `baja:ComponentSpace` in BajaScript.
*
* @class
* @alias baja.ComponentSpace
* @extends baja.NavContainer
* @implements baja.comm.ServerHandlerProxy
*
* @param {String} name
* @param {String} ordInHost
* @param host
*/
var ComponentSpace = function ComponentSpace(name, ordInHost, host) {
var that = this;
callSuper(ComponentSpace, that, [ { navName: name } ]);
that.$map = {};
that.$root = null;
that.$callbacks = null;
that.$host = bajaDef(host, null);
that.$ordInHost = baja.Ord.make(ordInHost || '');
that.$isReadonly = false;
};
subclass(ComponentSpace, baja.NavContainer);
/**
* Called to initialize the Component Space.
*
* @private
*
* @returns {Promise} Resolved once the space has initialized.
*/
ComponentSpace.prototype.init = function () {
return bajaPromises.resolve();
};
/**
* Return true if the entire ComponentSpace is readonly.
*
* @return {Boolean} true if readonly.
*/
ComponentSpace.prototype.isSpaceReadonly = function () {
return this.$isReadonly;
};
/**
* Mount a `Component` in the Component Space.
*
* This is a private internal method to mount `Component`s into a Component Space.
*
* @ignore
*/
function mount(space, comp) {
var h = comp.getHandle(),
cursor;
if (h === null) {
// TODO: What about automatically generating a handle for a non Proxy Component Space?
// TODO: Generate error if not a handle?
return;
}
if (space.$map.hasOwnProperty(h)) {
throw new Error("Fatal error: handle already used in Component Space: " + h);
}
// Set up the Space and Handle reference
comp.$space = space;
space.$map[h] = comp;
// Mount any child Components
cursor = comp.getSlots().properties();
while (cursor.next()) {
const slotValue = comp.get(cursor.get());
if (slotValue.getType().isComponent()) {
mount(space, slotValue);
}
}
}
/**
* Unmount a `Component` in the Component Space.
*
* This is a private internal method to unmount `Component`s into a Component Space.
*
* @ignore
*/
function unmount(space, comp) {
var h = comp.getHandle(),
prevSub,
subs,
cursor,
i;
if (h === null) {
// TODO: Generate error if not a handle?
return;
}
if (!space.$map.hasOwnProperty(h)) {
throw new Error("Fatal error: handle not mapped into Space: " + h);
}
prevSub = comp.isSubscribed();
// delete the Space and Handle reference (after this isMounted() will return false)
comp.$space = null;
delete space.$map[h];
// Unsubscribe Component from all Subscribers
subs = comp.$subs.slice();
for (i = 0; i < subs.length; ++i) {
subs[i].unsubscribe({
"comps": comp
});
}
// Make sure we're not leased
unlease(comp);
// If this was subscribed before, make sure this callback is made
if (prevSub && typeof comp.unsubscribed === "function") {
try {
comp.unsubscribed();
} catch (err) {
bajaError(err);
}
}
// TODO: What about nullifying the handle reference on the Component?
// Unmount any child Components
cursor = comp.getSlots().properties();
while (cursor.next()) {
const slotValue = comp.get(cursor.get());
if (slotValue.getType().isComponent()) {
unmount(space, slotValue);
}
}
}
/**
* Private framework handler for a `Component` Space.
*
* This is a private internal method for framework developers.
*
* @private
*/
ComponentSpace.prototype.$fw = function (x, a, b, c) {
if (x === "mount") {
// Mount this Component
mount(this, /*Component*/a);
} else if (x === "unmount") {
// Unmount this Component
unmount(this, /*Component*/a);
}
};
/**
* Return the root `Component` of the Component Space.
*
* @returns {baja.Component} the root Component for the Space.
*/
ComponentSpace.prototype.getRootComponent = function () {
return this.$root;
};
/**
* Return the ORD in Session for the Component Space.
*
* @returns {baja.Ord}
*/
ComponentSpace.prototype.getOrdInSession = function () {
return this.$ordInHost.relativizeToSession();
};
/**
* Return absolute ORD for the Component Space. May be session-aware.
*
* @param {object} [params]
* @param {boolean} [params.sessionAware]
* @returns {baja.Ord}
* @see baja.NavContainer#getNavOrd
*/
ComponentSpace.prototype.getAbsoluteOrd = function (params) {
if (!this.$host) {
return baja.Ord.DEFAULT;
}
var sessionAware = baja.objectify(params).sessionAware;
return baja.Ord.make({
base: this.$host.getAbsoluteOrd(params),
child: sessionAware ? this.$ordInHost : this.getOrdInSession()
});
};
/**
* @private
* @override
* @returns {String} the server handler ID to use when making server calls.
*/
ComponentSpace.prototype.getServerHandlerId = function () {
return this.getAbsoluteOrd().relativizeToSession().toString();
};
/**
* Find the `Component` via its handle (null if not found).
*
* This method does not result in any network calls.
*
* @private
*
* @param {String} handle the Component's handle.
*
* @returns {baja.Component} the Component via its handle (null if not found).
*/
ComponentSpace.prototype.findByHandle = function (handle) {
strictArg(handle, String);
return bajaDef(this.$map[handle], null);
};
/**
* Find the `Component` via its handle (null if not found).
*
* This method may result in an **asynchronous** network call if
* the `Component` can't be found locally and the Space is a Proxy.
*
* @private
*
* @param {Object} [obj] the object literal for the method's arguments.
* @param {Function} [obj.ok] (Deprecated: use Promise) the ok callback.
* Called if the Component is resolved. The Component instance will be passed
* to this function.
* @param {Function} [obj.fail] (Deprecated: use Promise) the fail callback.
* Called if there's an error or the Component can't be resolved.
* @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be
* batched into this object.
* @returns {Promise.<baja.Component>} a promise that will be resolved once
* the component has been resolved.
*/
ComponentSpace.prototype.resolveByHandle = function (obj) {
obj = objectify(obj);
var handle = obj.handle,
cb = new Callback(obj.ok, obj.fail),
comp;
try {
comp = this.findByHandle(handle);
if (comp !== null) {
cb.ok(comp);
} else {
throw new Error("Could not find Component from Handle");
}
} catch (err) {
cb.fail(err);
}
return cb.promise();
};
/**
* Resolve to a list of enabled mix-in Types for the Component Space.
*
* An Object Literal is used for the method's arguments.
*
* @name baja.ComponentSpace#toEnabledMixIns
* @function
*
* @param {Object} [obj] the Object Literal for the method's arguments.
* @param {Function} [obj.ok] (Deprecated: use Promise) Callback handler
* invoked once the enabled mix-in Types have been resolved.
* @param {Function} [obj.fail] (Deprecated: use Promise) the fail callback.
* @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be
* batched into this object.
* @returns {Promise.<Array.<Object>>} a promise that will be resolved once
* the mixin information has been retrieved.
*/
ComponentSpace.prototype.toEnabledMixIns = function (obj) {
obj = baja.objectify(obj);
var cb = new Callback(obj.ok, obj.fail, obj.batch);
cb.ok([]);
return cb.promise();
};
/**
* Return true if this Component Space has Space callbacks.
*
* Space callbacks are normally used to make network calls.
*
* @private
*
* @returns {Boolean}
*/
ComponentSpace.prototype.hasCallbacks = function () {
return this.$callbacks !== null;
};
/**
* Return the Space Callbacks.
*
* @private
*
* @returns Space Callbacks
*/
ComponentSpace.prototype.getCallbacks = function () {
return this.$callbacks;
};
/**
* Sync the Component Space.
*
* If the Space is a Proxy, this method will result in an
* **asynchronous** network call to sync the master Space with this one.
*
* @param {Object} [obj] the object literal for the method's arguments.
* @param {Function} [obj.ok] (Deprecated: use Promise) the ok callback.
* Called once the Component Space has been successfully synchronized with the
* Server.
* @param {Function} [obj.fail] (Deprecated: use Promise) the fail callback.
* Called If the Component Space can't be synchronized.
* @param {baja.comm.Batch} [obj.batch] if defined, any network calls will be
* batched into this object.
* @returns {Promise} a promise that will be resolved once the space has been
* synced.
*/
ComponentSpace.prototype.sync = function (obj) {
obj = objectify(obj, "ok");
var cb = new Callback(obj.ok, obj.fail);
cb.ok();
return cb.promise();
};
/**
* Return the Nav ORD of the Root Component.
*
* @private
*
* @returns {baja.Ord}
*/
ComponentSpace.prototype.getNavOrd = function () {
return this.$root === null ? null : this.$root.getNavOrd();
};
/**
* Access the Nav Children.
*
* @see baja.NavContainer#getNavChildren
*
* @returns promise a promise that will be resolved once the callbacks have been invoked.
*/
ComponentSpace.prototype.getNavChildren = function (obj) {
obj = objectify(obj, "ok");
if (this.$root) {
return this.$root.getNavChildren(obj);
}
var cb = new Callback(obj.ok);
cb.ok([]);
return cb.promise();
};
/**
* Return the Nav Icon.
*
* @returns {baja.Icon}
*/
ComponentSpace.prototype.getNavIcon = function () {
return this.$root ? this.$root.getNavIcon() : callSuper("getNavIcon", ComponentSpace, this);
};
return ComponentSpace;
});