/**
* A plugin object is an instance of a {@link FORGE.PluginEngine} on top of the viewer.<br>
* It can be visual (display a logo) or not (gyroscope, stats ...).
*
* @constructor FORGE.Plugin
* @param {FORGE.Viewer} viewer - {@link FORGE.Viewer} reference.
* @param {FORGE.PluginEngine} engine - The engine used to instantiate tis plugin.
* @param {PluginInstanceConfig} config - The config of the plugin instance.
* @param {number} index - The index of the plugin, use for display order.
* @extends {FORGE.BaseObject}
*
* @todo Scene array upgrade : Have a scene array with inclusive and exclusive uids
* @todo Same filter array for groups ?
*/
FORGE.Plugin = function(viewer, engine, config, index)
{
/**
* The viewer reference.
* @name FORGE.Plugin#_viewer
* @type {FORGE.Viewer}
* @private
*/
this._viewer = viewer;
/**
* The engine this plugin is based on.
* @name FORGE.Plugin#_engine
* @type {FORGE.PluginEngine}
* @private
*/
this._engine = engine;
/**
* The instance config of the plugin.
* @name FORGE.Plugin#_instanceConfig
* @type {?PluginInstanceConfig}
* @private
*/
this._instanceConfig = config;
/**
* The contextual config is the plugin configuration specific to a scene or a group.<br/>
* It doesn't last at scene change.
* @name FORGE.Plugin#_contextualConfig
* @type {?PluginConfigurationConfig}
* @private
*/
this._contextualConfig = null;
/**
* This config will be the final config object that merges the default engine config, the instance config and the contextual config.
* @name FORGE.Plugin#_config
* @type {?PluginInstanceConfig}
* @private
*/
this._config = null;
/**
* The index of the plugin, used if the plugin is a visual one.
* @name FORGE.Plugin#_index
* @type {number}
* @private
*/
this._index = index;
/**
* The options of this Plugin.
* @name FORGE.Plugin#_options
* @type {Object}
* @private
*/
this._options = null;
/**
* The actions of this Plugin.
* @name FORGE.Plugin#_actions
* @type {Object}
* @private
*/
this._actions = null;
/**
* Object that stores {@link FORGE.ActionEventDispatcher}.
* @name FORGE.Plugin#_events
* @type {Object<FORGE.ActionEventDispatcher>}
* @private
*/
this._events = null;
/**
* Instance of the plugin engine constructor.
* @name FORGE.Plugin#_instance
* @type {?PluginStructure}
* @private
*/
this._instance = null;
/**
* Flag to tell if the instance is ready.<br>
* It is the plugin developper that set the ready flag by calling the notifyInstanceReady method from its instance.
* @name FORGE.Plugin#_instanceReady
* @type {boolean}
* @private
*/
this._instanceReady = false;
/**
* A DOM id or a DOM element.<br>
* Used to inject graphical plugins anywhere in the document (outside the viewer container).
* @name FORGE.Plugin#_parent
* @type {Element|HTMLElement|string}
* @private
*/
this._parent = null;
/**
* The display object container for this plugin if it is a graphical one.
* @name FORGE.Plugin#_container
* @type {?FORGE.DisplayObjectContainer}
* @private
*/
this._container = null;
/**
* Reference to the plugin object factory, every plugin have its own factory.
* @name FORGE.Plugin#_create
* @type {FORGE.PluginObjectFactory}
* @private
*/
this._create = null;
/**
* Scenes UID array, this plugin is allow to instantiate only on these scene if not null.
* @name FORGE.Plugin#_scenes
* @type {Array<string>}
* @private
*/
this._scenes = null;
/**
* Object to handle persistent data.
* @name FORGE.Plugin#_persistentData
* @type {Object}
* @private
*/
this._persistentData = null;
/**
* Is this plugin stay at the scene change event?
* @name FORGE.Plugin#_keep
* @type {boolean}
* @private
*/
this._keep = true;
/**
* Is this plugin have to reset between each scene?
* @name FORGE.Plugin#_reset
* @type {boolean}
* @private
*/
this._reset = true;
/**
* Event dispatcher for instance creation
* @name FORGE.Plugin#_onInstanceCreate
* @type {FORGE.EventDispatcher}
* @private
*/
this._onInstanceCreate = null;
/**
* Event dispatcher for instance ready
* @name FORGE.Plugin#_onInstanceReady
* @type {FORGE.EventDispatcher}
* @private
*/
this._onInstanceReady = null;
FORGE.BaseObject.call(this, "Plugin");
this._boot();
};
FORGE.Plugin.prototype = Object.create(FORGE.BaseObject.prototype);
FORGE.Plugin.prototype.constructor = FORGE.Plugin;
/**
* Boot sequence
* @method FORGE.Plugin#_boot
* @private
*/
FORGE.Plugin.prototype._boot = function()
{
//First thing, very important is to register the plugin object!
//We can't parse the rest of the config here, maybe the engine is not loaded yet.
this._uid = this._instanceConfig.uid;
this._tags = this._instanceConfig.tags;
this._register();
this._persistentData = {};
};
/**
* Internal method to parse the config.
* @method FORGE.Plugin#_parseConfig
* @private
* @param {Object} config - The config object to parse.
*/
FORGE.Plugin.prototype._parseConfig = function(config)
{
//If there is a parent string in config, this should be an dom element id where to inject the plugin.
if(typeof config.parent === "string")
{
this._parent = config.parent;
}
this._keep = (typeof config.keep === "boolean") ? config.keep : true;
this._reset = (typeof config.reset === "boolean") ? config.reset : true;
//If there is no scenes array alerady set and if there is a scene array in the instance config.
if(this._scenes === null && FORGE.Utils.isArrayOf(config.scenes, "string") === true)
{
this._scenes = config.scenes;
}
this._mergeConfigurations();
};
/**
* Merge the plugin configurations
* @method FORGE.Plugin#_mergeConfigurations
* @private
*/
FORGE.Plugin.prototype._mergeConfigurations = function()
{
this._config = /** @type {PluginInstanceConfig} */ (FORGE.Utils.extendMultipleObjects(this._engine.defaultConfig, this._instanceConfig, this._contextualConfig));
this._options = this._config.options || {};
this._data = this._config.data || {};
this._actions = this._config.actions || {};
this._clearEvents();
this._createEvents(this._config.events);
};
/**
* Create events dispatchers that the engine needs.
* @method FORGE.Plugin#_createEvents
* @private
* @param {Object=} events - The events config of the plugin engine.
*/
FORGE.Plugin.prototype._createEvents = function(events)
{
this._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 plugin events.
* @method FORGE.Plugin#_clearEvents
* @private
*/
FORGE.Plugin.prototype._clearEvents = function()
{
for(var e in this._events)
{
this._events[e].destroy();
this._events[e] = null;
}
};
/**
* Instantiate the plugin engine if it is loaded, if not listen to the engine load complete to retry to instantiate.
* @method FORGE.Plugin#instantiate
*/
FORGE.Plugin.prototype.instantiate = function()
{
if(this._engine.loaded === true)
{
this._instantiatePlugin();
}
else
{
this._engine.onLoadComplete.addOnce(this._engineLoadComplete, this);
}
};
/**
* Handler for engine load complete.
* @method FORGE.Plugin#_engineLoadComplete
* @private
*/
FORGE.Plugin.prototype._engineLoadComplete = function()
{
this._instantiatePlugin();
};
/**
* Parse the config then instantiate the plugin.
* @method FORGE.Plugin#_instantiatePlugin
* @private
*/
FORGE.Plugin.prototype._instantiatePlugin = function()
{
this.log("Plugin._instantiatePlugin(); "+this._uid);
this._parseConfig(this._instanceConfig);
this._instance = this._engine.getNewInstance(this._viewer, this);
this._instance.boot.call(this._instance);
if(this._onInstanceCreate !== null)
{
this._onInstanceCreate.dispatch();
}
};
/**
* Instance can notify with this method that the instance is ready
* @method FORGE.Plugin.notifyInstanceReady
*/
FORGE.Plugin.prototype.notifyInstanceReady = function()
{
this._instanceReady = true;
if(this._onInstanceReady !== null)
{
this._onInstanceReady.dispatch();
}
};
/**
* Update method called by the plugin manager.
* @method FORGE.Plugin#update
*/
FORGE.Plugin.prototype.update = function()
{
if(this._instance !== null && typeof this._instance.update === "function")
{
this._instance.update.call(this._instance);
}
};
/**
* Reset the plugin to a specified configuration.
* @method FORGE.Plugin#reset
*/
FORGE.Plugin.prototype.resetInstance = function()
{
this._mergeConfigurations();
if(this._instance !== null)
{
if(typeof this._instance.reset === "function")
{
this._instance.reset.call(this._instance);
}
else
{
this.log("There is no reset function on plugin "+this._engine.uid);
}
}
};
/**
* Destroy method.
* @method FORGE.Plugin#destroy
*/
FORGE.Plugin.prototype.destroy = function()
{
if(this._alive === false)
{
return;
}
if(this._instance !== null)
{
this._instance.viewer = null;
this._instance.plugin = null;
this._instance.destroy();
this._instance = null;
}
if(this._create !== null)
{
this._create.destroy();
this._create = null;
}
if(this._container !== null)
{
this._container.destroy();
this._container = null;
}
if(this._onInstanceCreate !== null)
{
this._onInstanceCreate.destroy();
this._onInstanceCreate = null;
}
if(this._onInstanceReady !== null)
{
this._onInstanceReady.destroy();
this._onInstanceReady = null;
}
this._clearEvents();
this._events = null;
this._viewer = null;
this._persistentData = null;
FORGE.BaseObject.prototype.destroy.call(this);
};
/**
* Get the plugin full url.
* @name FORGE.Plugin#fullUrl
* @readonly
* @type {string}
*/
Object.defineProperty(FORGE.Plugin.prototype, "fullUrl",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._engine.fullUrl;
}
});
/**
* Get the plugin options.
* @name FORGE.Plugin#options
* @readonly
* @type {Object}
*/
Object.defineProperty(FORGE.Plugin.prototype, "options",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._options;
}
});
/**
* Get the plugin actions.
* @name FORGE.Plugin#actions
* @readonly
* @type {Object}
*/
Object.defineProperty(FORGE.Plugin.prototype, "actions",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._actions;
}
});
/**
* Get the plugin events.
* @name FORGE.Plugin#events
* @readonly
* @type {Object}
*/
Object.defineProperty(FORGE.Plugin.prototype, "events",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._events;
}
});
/**
* Get the scenes array. This plugin will be alive only on these scenes.
* @name FORGE.Plugin#scenes
* @readonly
* @type {Array}
*/
Object.defineProperty(FORGE.Plugin.prototype, "scenes",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._scenes;
}
});
/**
* Get and set pesistent data.
* @name FORGE.Plugin#persistentData
* @type {Object}
*/
Object.defineProperty(FORGE.Plugin.prototype, "persistentData",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._persistentData;
},
/** @this {FORGE.Plugin} */
set: function(value)
{
this._persistentData = value;
}
});
/**
* Get the plugin instance.
* @name FORGE.Plugin#instance
* @readonly
* @type {Object}
*/
Object.defineProperty(FORGE.Plugin.prototype, "instance",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._instance;
}
});
/**
* Get the plugin instance ready flag.
* @name FORGE.Plugin#instanceReady
* @readonly
* @type {boolean}
*/
Object.defineProperty(FORGE.Plugin.prototype, "instanceReady",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._instanceReady;
}
});
/**
* Get the plugin container.
* @name FORGE.Plugin#container
* @readonly
* @type {FORGE.DisplayObjectContainer}
*/
Object.defineProperty(FORGE.Plugin.prototype, "container",
{
/** @this {FORGE.Plugin} */
get: function()
{
if(this._container === null)
{
if(typeof this._parent === "string" && this._parent !== "")
{
this._parent = document.getElementById(this._parent);
if(typeof this._parent === "undefined" || this._parent === null || FORGE.Dom.isHtmlElement(this._parent) === false)
{
throw "FORGE.Plugin.boot : Plugin parent is invalid";
}
this._container = new FORGE.DisplayObjectContainer(this._viewer, null, null, this._parent);
}
else
{
this._container = new FORGE.DisplayObjectContainer(this._viewer);
this._viewer.pluginContainer.addChild(this._container);
this._container.maximize(true);
}
this._container.id = this._uid;
this._container.index = this._index;
}
return this._container;
}
});
/**
* Get the plugin object factory.
* @name FORGE.Plugin#create
* @readonly
* @type {FORGE.PluginObjectFactory}
*/
Object.defineProperty(FORGE.Plugin.prototype, "create",
{
/** @this {FORGE.Plugin} */
get: function()
{
if(this._create === null)
{
this._create = new FORGE.PluginObjectFactory(this._viewer, this);
}
return this._create;
}
});
/**
* Get the plugin keep flag.
* @name FORGE.Plugin#keep
* @readonly
* @type {boolean}
*/
Object.defineProperty(FORGE.Plugin.prototype, "keep",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._keep;
}
});
/**
* Get the plugin reset flag.
* @name FORGE.Plugin#reset
* @readonly
* @type {boolean}
*/
Object.defineProperty(FORGE.Plugin.prototype, "reset",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._reset;
}
});
/**
* Get the contextual config.
* @name FORGE.Plugin#contextualConfig
* @type {Object}
*/
Object.defineProperty(FORGE.Plugin.prototype, "contextualConfig",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._contextualConfig;
},
/** @this {FORGE.Plugin} */
set: function(value)
{
this._contextualConfig = value;
}
});
/**
* Get the instance config.
* @name FORGE.Plugin#instanceConfig.
* @readonly
* @type {Object}
*/
Object.defineProperty(FORGE.Plugin.prototype, "instanceConfig",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._instanceConfig;
}
});
/**
* Get the final merged config.
* @name FORGE.Plugin#config
* @readonly
* @type {Object}
*/
Object.defineProperty(FORGE.Plugin.prototype, "config",
{
/** @this {FORGE.Plugin} */
get: function()
{
return this._config;
}
});
/**
* Get the "onInstanceCreate" {@link FORGE.EventDispatcher} of the Plugin.
* @name FORGE.Plugin#onInstanceCreate
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.Plugin.prototype, "onInstanceCreate",
{
/** @this {FORGE.Plugin} */
get: function()
{
if(this._onInstanceCreate === null)
{
this._onInstanceCreate = new FORGE.EventDispatcher(this, true);
}
return this._onInstanceCreate;
}
});
/**
* Get the "onInstanceReady" {@link FORGE.EventDispatcher} of the Plugin.
* @name FORGE.Plugin#onInstanceReady
* @readonly
* @type {FORGE.EventDispatcher}
*/
Object.defineProperty(FORGE.Plugin.prototype, "onInstanceReady",
{
/** @this {FORGE.Plugin} */
get: function()
{
if(this._onInstanceReady === null)
{
this._onInstanceReady = new FORGE.EventDispatcher(this, true);
}
return this._onInstanceReady;
}
});