/**
* A FORGE.Scene is an object that represents a scene of a {@link FORGE.Story}.
*
* @constructor FORGE.Scene
* @param {FORGE.Viewer} viewer {@link FORGE.Viewer} reference.
* @extends {FORGE.BaseObject}
*/
FORGE.Scene = function(viewer)
{
/**
* The viewer reference.
* @name FORGE.Scene#_viewer
* @type {FORGE.Viewer}
* @private
*/
this._viewer = viewer;
/**
* The scene config object.
* @name FORGE.Scene#_sceneConfig
* @type {?SceneConfig}
* @private
*/
this._config = null;
/**
* The internationalizable name of the scene.
* @name FORGE.Scene#_name
* @type {?FORGE.LocaleString}
* @private
*/
this._name = null;
/**
* The internationalizable slug name of the scene.
* @name FORGE.Scene#_slug
* @type {?FORGE.LocaleString}
* @private
*/
this._slug = null;
/**
* The internationalizable description of the scene.
* @name FORGE.Scene#_description
* @type {?FORGE.LocaleString}
* @private
*/
this._description = null;
/**
* The array of scene uids to be sync with
* @name FORGE.Scene#_sync
* @type {Array<string>}
* @private
*/
this._sync = [];
/**
* The number of times this has been viewed.
* @name FORGE.Scene#_viewCount
* @type {number}
* @private
*/
this._viewCount = 0;
/**
* Array of group uids this scene belongs to. aka "parents".
* @name FORGE.Scene#_groups
* @type {?Array<string>}
* @private
*/
this._groups = null;
/**
* Is booted flag.
* @name FORGE.Scene#_booted
* @type {boolean}
* @private
*/
this._booted = false;
/**
* Use external config file flag.
* @name FORGE.Scene#_useExternalConfig
* @type {boolean}
* @private
*/
this._useExternalConfig = false;
/**
* Scene events from the json configuration
* @name FORGE.Story#_events
* @type {Object<FORGE.ActionEventDispatcher>}
* @private
*/
this._events = {};
/**
* The media of the scene
* @name FORGE.Scene#_media
* @type {FORGE.Media}
* @private
*/
this._media = null;
/**
* Load request event dispatcher.
* @name FORGE.Scene#_onLoadRequest
* @type {FORGE.EventDispatcher}
* @private
*/
this._onLoadRequest = null;
/**
* Load start event dispatcher.
* @name FORGE.Scene#_onLoadStart
* @type {FORGE.EventDispatcher}
* @private
*/
this._onLoadStart = null;
/**
* Load complete event dispatcher.
* @name FORGE.Scene#_onLoadComplete
* @type {FORGE.EventDispatcher}
* @private
*/
this._onLoadComplete = null;
/**
* Unload start event dispatcher.
* @name FORGE.Scene#_onUnloadStart
* @type {FORGE.EventDispatcher}
* @private
*/
this._onUnloadStart = null;
/**
* Unload complete event dispatcher.
* @name FORGE.Scene#_onUnloadComplete
* @type {FORGE.EventDispatcher}
* @private
*/
this._onUnloadComplete = null;
/**
* Load complete event dispatcher for scene configuration file.
* @name FORGE.Scene#_onConfigLoadComplete
* @type {FORGE.EventDispatcher}
* @private
*/
this._onConfigLoadComplete = null;
/**
* media create event dispatcher.
* @name FORGE.Scene#_onMediaCreate
* @type {FORGE.EventDispatcher}
* @private
*/
this._onMediaCreate = null;
FORGE.BaseObject.call(this, "Scene");
};
FORGE.Scene.prototype = Object.create(FORGE.BaseObject.prototype);
FORGE.Scene.prototype.constructor = FORGE.Scene;
/**
* Parse scene configuration.
* @method FORGE.Scene#_parseConfig
* @private
* @param {SceneConfig} config - The configuration object to parse.
*/
FORGE.Scene.prototype._parseConfig = function(config)
{
this._config = config;
this._uid = config.uid;
this._tags = config.tags;
this._register();
this._name = new FORGE.LocaleString(this._viewer, this._config.name);
this._slug = new FORGE.LocaleString(this._viewer, this._config.slug);
this._description = new FORGE.LocaleString(this._viewer, this._config.description);
this._sync = (FORGE.Utils.isArrayOf(this._config.sync, "string") === true) ? this._config.sync : [];
if(typeof config.events === "object" && config.events !== null)
{
this._createEvents(config.events);
}
if (this._booted === false && typeof config.url === "string" && config.url !== "")
{
//use an external config json file
this._useExternalConfig = true;
this._viewer.load.json(this._uid, config.url, this._configLoadComplete, this);
}
else
{
this._booted = true;
}
};
/**
* Event handler for the load of the scene config json file.
* @method FORGE.Scene#_configLoadComplete
* @param {FORGE.File} file - The file data.
* @private
*
* @todo the "story.config" cache file is not updated in this case, a cache entry is added for each scene UID.
*/
FORGE.Scene.prototype._configLoadComplete = function(file)
{
this.log("config load complete");
this._booted = true;
//extend the config
if (typeof file.data === "string")
{
file.data = /** @type {Object} */ (JSON.parse(file.data));
}
// extend init config
this._config = /** @type {SceneConfig} */ (FORGE.Utils.extendSimpleObject(this._config, file.data));
this._viewer.story.notifySceneConfigLoadComplete(this);
if (this._onConfigLoadComplete !== null)
{
this._onConfigLoadComplete.dispatch();
}
};
/**
* Create events dispatchers.
* @method FORGE.Scene#_createEvents
* @private
* @param {SceneEventsConfig} events - The events config of the scene.
*/
FORGE.Scene.prototype._createEvents = function(events)
{
this.log("create events");
var event;
for(var e in events)
{
event = new FORGE.ActionEventDispatcher(this._viewer, e);
event.addActions(events[e]);
this._events[e] = event;
}
};
/**
* Clear all events.
* @method FORGE.Scene#_clearEvents
* @private
*/
FORGE.Scene.prototype._clearEvents = function()
{
this.log("clear events");
for(var e in this._events)
{
this._events[e].destroy();
this._events[e] = null;
}
};
/**
* Create the scene media
* @param {SceneMediaConfig} media - media configuration
* @private
*/
FORGE.Scene.prototype._createMedia = function(media)
{
this.log("create media");
if(this._media === null)
{
this._media = new FORGE.Media(this._viewer, media);
if(this._onMediaCreate !== null)
{
this._onMediaCreate.dispatch({ media: this._media });
}
}
};
/**
* Add a scene configuration object.
* @method FORGE.Scene#addConfig
* @param {SceneConfig} config - The scene configuration object to add.
*/
FORGE.Scene.prototype.addConfig = function(config)
{
this._parseConfig(config);
};
/**
* Load just emmit a load request. The story will trigger the loadStart.
* @method FORGE.Scene#load
*/
FORGE.Scene.prototype.load = function()
{
this.log("load");
if (this._viewer.story.scene === this)
{
return;
}
if (this._onLoadRequest !== null)
{
this._onLoadRequest.dispatch();
}
if(FORGE.Utils.isTypeOf(this._events.onLoadRequest, "ActionEventDispatcher") === true)
{
this._events.onLoadRequest.dispatch();
}
};
/**
* Create the media and start to load.
* @method FORGE.Scene#loadStart
* @param {number} time - The time of the media (if video)
*/
FORGE.Scene.prototype.loadStart = function(time)
{
if(typeof time === "number" && isNaN(time) === false && typeof this._config.media !== "undefined")
{
if(typeof this._config.media.options === "undefined")
{
this._config.media.options = {};
}
this._config.media.options.startTime = time;
}
this._createMedia(this._config.media);
if (this._onLoadStart !== null)
{
this._onLoadStart.dispatch();
}
if(FORGE.Utils.isTypeOf(this._events.onLoadStart, "ActionEventDispatcher") === true)
{
this._events.onLoadStart.dispatch();
}
this._viewCount++;
if (this._onLoadComplete !== null)
{
this._onLoadComplete.dispatch();
}
if(FORGE.Utils.isTypeOf(this._events.onLoadComplete, "ActionEventDispatcher") === true)
{
this._events.onLoadComplete.dispatch();
}
};
/**
* Unload the scene.
* @method FORGE.Scene#unload
* @todo cleanup if useless
*/
FORGE.Scene.prototype.unload = function()
{
this.log("unload");
if (this._onUnloadStart !== null)
{
this._onUnloadStart.dispatch();
}
if(FORGE.Utils.isTypeOf(this._events.onUnloadStart, "ActionEventDispatcher") === true)
{
this._events.onUnloadStart.dispatch();
}
this._media.destroy();
this._media = null;
if (this._onUnloadComplete !== null)
{
this._onUnloadComplete.dispatch();
}
if(FORGE.Utils.isTypeOf(this._events.onUnloadComplete, "ActionEventDispatcher") === true)
{
this._events.onUnloadComplete.dispatch();
}
};
/**
* Know if a {@link FORGE.Group} is related to this scene?
* @method FORGE.Scene#hasGroup
* @param {(FORGE.Group|string)} value - Either the {@link FORGE.Group} itself or its index or its uid.
* @return {boolean} Returns true if the {@link FORGE.Group} is related to this scene, false if not.
*/
FORGE.Scene.prototype.hasGroup = function(value)
{
if (typeof value === "string" && FORGE.UID.isTypeOf(value, "Group") === true)
{
return FORGE.UID.get( /** @type {string} */ (value)).hasScene(this);
}
else if (FORGE.Utils.isTypeOf(value, "Group") === true)
{
return value.hasScene(this);
}
return false;
};
/**
* Know if this scene is related to any {@link FORGE.Group}.
* @method FORGE.Scene#hasGroups
* @return {boolean} Returns true if this scene is related to at least a {@link FORGE.Group}, false if not.
*/
FORGE.Scene.prototype.hasGroups = function()
{
var groups = this._viewer.story.groups;
var group;
for (var i = 0, ii = groups.length; i < ii; i++)
{
group = groups[i];
if (group.hasScene(this) === true)
{
return true;
}
}
return false;
};
/**
* Know if the scene has sound source?
* @method FORGE.Scene#hasSoundSource
* @return {boolean} Returns true if the scene has a sound source, false if not.
*/
FORGE.Scene.prototype.hasSoundSource = function()
{
if (typeof this._config.sound !== "undefined" && typeof this._config.sound.source !== "undefined" && ((typeof this._config.sound.source.url !== "undefined" && this._config.sound.source.url !== "") || (typeof this._config.sound.source.target !== "undefined" && this._config.sound.source.target !== "")))
{
return true;
}
return false;
};
/**
* Know if the scene has sound target as source?
* @method FORGE.Scene#hasSoundTarget
* @param {string} uid - The target source UID to verify.
* @return {boolean} Returns true if the scene has a sound source target, false if not.
*/
FORGE.Scene.prototype.hasSoundTarget = function(uid)
{
if (typeof this._config.sound !== "undefined" && typeof this._config.sound.source !== "undefined" && typeof this._config.sound.source.target !== "undefined" && this._config.sound.source.target !== "" && this._config.sound.source.target === uid)
{
return true;
}
return false;
};
/**
* Know if an ambisonic sound is attached to the scene?
* @method FORGE.Scene#isAmbisonic
* @return {boolean} Returns true if the scene has an ambisonic sound source, false if not.
*/
FORGE.Scene.prototype.isAmbisonic = function()
{
//@todo real check of the UID target object rather then the isAmbisonic method of the FORGE.Scene
if (this.hasSoundSource() === true && this._config.sound.type === FORGE.SoundType.AMBISONIC)
{
return true;
}
return false;
};
/**
* Destroy method
* @method FORGE.Scene#destroy
*/
FORGE.Scene.prototype.destroy = function()
{
this._viewer = null;
this._name.destroy();
this._name = null;
this._slug.destroy();
this._slug = null;
this._description.destroy();
this._description = null;
if (this._media !== null)
{
this._media.destroy();
this._media = null;
}
if (this._onLoadStart !== null)
{
this._onLoadStart.destroy();
this._onLoadStart = null;
}
if (this._onLoadComplete !== null)
{
this._onLoadComplete.destroy();
this._onLoadComplete = null;
}
if (this._onUnloadStart !== null)
{
this._onUnloadStart.destroy();
this._onUnloadStart = null;
}
if (this._onUnloadComplete !== null)
{
this._onUnloadComplete.destroy();
this._onUnloadComplete = null;
}
this._clearEvents();
this._events = null;
FORGE.BaseObject.prototype.destroy.call(this);
};
/**
* Get the booted status of the scene.
* @name FORGE.Scene#booted
* @type {boolean}
* @readonly
*/
Object.defineProperty(FORGE.Scene.prototype, "booted",
{
/** @this {FORGE.Scene} */
get: function()
{
return this._booted;
}
});
/**
* Get the group config object.
* @name FORGE.Scene#config
* @readonly
* @type {SceneConfig}
*/
Object.defineProperty(FORGE.Scene.prototype, "config",
{
/** @this {FORGE.Scene} */
get: function()
{
return this._config;
}
});
/**
* Get the count of how many times this group has been viewed.
* @name FORGE.Scene#viewCount
* @readonly
* @type {number}
*/
Object.defineProperty(FORGE.Scene.prototype, "viewCount",
{
/** @this {FORGE.Scene} */
get: function()
{
return this._viewCount;
}
});
/**
* Know if this scene has been viewed at least one time.
* @name FORGE.Scene#viewed
* @readonly
* @type {boolean}
*/
Object.defineProperty(FORGE.Scene.prototype, "viewed",
{
/** @this {FORGE.Scene} */
get: function()
{
return this._viewCount !== 0;
}
});
/**
* Get the name of this scene.
* @name FORGE.Scene#name
* @readonly
* @type {string}
*/
Object.defineProperty(FORGE.Scene.prototype, "name",
{
/** @this {FORGE.Scene} */
get: function()
{
return this._name.value;
}
});
/**
* Get the slug name of this scene.
* @name FORGE.Scene#slug
* @readonly
* @type {string}
*/
Object.defineProperty(FORGE.Scene.prototype, "slug",
{
/** @this {FORGE.Scene} */
get: function()
{
return this._slug.value;
}
});
/**
* Get the description of this scene.
* @name FORGE.Scene#description
* @readonly
* @type {string}
*/
Object.defineProperty(FORGE.Scene.prototype, "description",
{
/** @this {FORGE.Scene} */
get: function()
{
return this._description.value;
}
});
/**
* Get the sync array.
* @name FORGE.Scene#sync
* @readonly
* @type {Array<string>}
*/
Object.defineProperty(FORGE.Scene.prototype, "sync",
{
/** @this {FORGE.Scene} */
get: function()
{
return this._sync;
}
});
/**
* Get the Array of groups uids to which this scene belongs to.
* @name FORGE.Scene#groupsUid
* @readonly
* @type {?Array<FORGE.Group>}
*/
Object.defineProperty(FORGE.Scene.prototype, "groupsUid",
{
/** @this {FORGE.Scene} */
get: function()
{
var groups = this._viewer.story.groups;
var group;
var result = [];
for (var i = 0, ii = groups.length; i < ii; i++)
{
group = groups[i];
if (group.hasScene(this) === true)
{
result.push(group.uid);
}
}
return result;
}
});
/**
* Get the Array of {@link FORGE.Group} to which this scene belongs to.
* @name FORGE.Scene#groups
* @readonly
* @type {?Array<FORGE.Group>}
*/
Object.defineProperty(FORGE.Scene.prototype, "groups",
{
/** @this {FORGE.Scene} */
get: function()
{
return FORGE.UID.get(this.groupsUid);
}
});
/**
* Get the thumbnails Array.
* @name FORGE.Scene#thumbnails
* @readonly
* @type {Array}
*
* @todo Define what is a thumbnail array, maybe with a thumbnail object descriptor
*/
Object.defineProperty(FORGE.Scene.prototype, "thumbnails",
{
/** @this {FORGE.Scene} */
get: function()
{
return this._config.thumbnails;
}
});
/**
* Get the scene media.
* @name FORGE.Scene#media
* @readonly
* @type {FORGE.Media}
*/
Object.defineProperty(FORGE.Scene.prototype, "media",
{
/** @this {FORGE.Scene} */
get: function()
{
return this._media;
}
});
/**
* Get the background of the scene.
* @name FORGE.Scene#background
* @readonly
* @type {string}
*/
Object.defineProperty(FORGE.Scene.prototype, "background",
{
/** @this {FORGE.Scene} */
get: function()
{
return (typeof this._config.background !== "undefined") ? this._config.background : this._viewer.config.background;
}
});
/**
* Get the onLoadRequest {@link FORGE.EventDispatcher}.
* @name FORGE.Scene#onLoadRequest
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.Scene.prototype, "onLoadRequest",
{
/** @this {FORGE.Scene} */
get: function()
{
if (this._onLoadRequest === null)
{
this._onLoadRequest = new FORGE.EventDispatcher(this);
}
return this._onLoadRequest;
}
});
/**
* Get the onLoadStart {@link FORGE.EventDispatcher}.
* @name FORGE.Scene#onLoadStart
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.Scene.prototype, "onLoadStart",
{
/** @this {FORGE.Scene} */
get: function()
{
if (this._onLoadStart === null)
{
this._onLoadStart = new FORGE.EventDispatcher(this);
}
return this._onLoadStart;
}
});
/**
* Get the onLoadComplete {@link FORGE.EventDispatcher}.
* @name FORGE.Scene#onLoadComplete
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.Scene.prototype, "onLoadComplete",
{
/** @this {FORGE.Scene} */
get: function()
{
if (this._onLoadComplete === null)
{
this._onLoadComplete = new FORGE.EventDispatcher(this);
}
return this._onLoadComplete;
}
});
/**
* Get the onUnloadStart {@link FORGE.EventDispatcher}.
* @name FORGE.Scene#onUnloadStart
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.Scene.prototype, "onUnloadStart",
{
/** @this {FORGE.Scene} */
get: function()
{
if (this._onUnloadStart === null)
{
this._onUnloadStart = new FORGE.EventDispatcher(this);
}
return this._onUnloadStart;
}
});
/**
* Get the onUnloadComplete {@link FORGE.EventDispatcher}.
* @name FORGE.Scene#onUnloadComplete
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.Scene.prototype, "onUnloadComplete",
{
/** @this {FORGE.Scene} */
get: function()
{
if (this._onUnloadComplete === null)
{
this._onUnloadComplete = new FORGE.EventDispatcher(this);
}
return this._onUnloadComplete;
}
});
/**
* Get the onConfigLoadComplete {@link FORGE.EventDispatcher}.
* @name FORGE.Scene#onConfigLoadComplete
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.Scene.prototype, "onConfigLoadComplete",
{
/** @this {FORGE.Scene} */
get: function()
{
if (this._onConfigLoadComplete === null)
{
this._onConfigLoadComplete = new FORGE.EventDispatcher(this);
}
return this._onConfigLoadComplete;
}
});
/**
* Get the onMediaCreate {@link FORGE.EventDispatcher}.
* @name FORGE.Scene#onMediaCreate
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.Scene.prototype, "onMediaCreate",
{
/** @this {FORGE.Scene} */
get: function()
{
if (this._onMediaCreate === null)
{
this._onMediaCreate = new FORGE.EventDispatcher(this);
}
return this._onMediaCreate;
}
});