|
|
Other Module Systems
|
|
|
====================
|
|
|
Extensions on client-side (JavaScript)
|
|
|
======================================
|
|
|
|
|
|
Ziemlich gut:
|
|
|
http://www.synthtopia.com/content/2012/05/30/audulus-modular-audio-processor-for-mac/
|
|
|
Content
|
|
|
-------
|
|
|
|
|
|
Bestes:
|
|
|
http://alex4d.files.wordpress.com/2008/12/quartz-composer-ui.jpg
|
|
|
- [Introduction](Extensions\_on\_client-side\_(JavaScript)\#Introduction)
|
|
|
- [Extension
|
|
|
Mechanism](Extensions\_on\_client-side\_(JavaScript)\#Extension-Mechanism)
|
|
|
- [Api](Extensions\_on\_client-side\_(JavaScript)\#Api)
|
|
|
- [Example](Extensions\_on\_client-side\_(JavaScript)\#Example)
|
|
|
- [Troubleshooting](Extensions\_on\_client-side\_(JavaScript)\#Troubleshooting)
|
|
|
|
|
|
http://lithosphere.codeflow.org/screenshots/desert.jpg
|
|
|
Introduction
|
|
|
------------
|
|
|
|
|
|
http://www.mapzoneeditor.com/?PAGE=DOCUMENTATION.IvyLeafKr
|
|
|
TODO
|
|
|
|
|
|
http://commons.wikimedia.org/wiki/File:CompositeNode-Settings\_withText\_BlenderMistTutorial.jpg
|
|
|
Extension Mechanism
|
|
|
-------------------
|
|
|
|
|
|
http://images-l3.native-instruments.com/typo3temp/pics/3\_87d27267c9.png?1314198777
|
|
|
Extensions basically consist of three components - the extension
|
|
|
**Main** object, a **Config** object and a **Content** object.
|
|
|
|
|
|
http://www.bmc.de/ckfinder/userfiles/images/Glossar/DASYLab/Schaltbildansicht.jpg
|
|
|
The **Main** object of an extension contains basic information such as
|
|
|
`name`, `mainMenuEntries` or (and most important) the `content`.
|
|
|
`content` stores references to **Config** objects as key-value pairs:
|
|
|
|
|
|
http://synthmaker.co.uk/images/components%20L.png
|
|
|
<code class="javascript">
|
|
|
this.content = {
|
|
|
tabA: new TestTabA(),
|
|
|
frameB: new TestFrameA(),
|
|
|
...
|
|
|
};
|
|
|
</code>
|
|
|
|
|
|
|
|
|
**Config** objects are instanced at the time, the `content` is set
|
|
|
(unlike **Content** objects). The example above demonstrates that a
|
|
|
**Main** object can have multiple **Config** objects which contain
|
|
|
further information describing the behavior and appearance of content,
|
|
|
e.g. `stlye`, `label` or `maxInstances`. The most important parameters
|
|
|
are `behavior` and `constructor`. `behavior` decides whether the content
|
|
|
is displayed in a *tab* or as a *frame*. `constructor` stores a
|
|
|
reference the the content constructor; the **Content** object. Instances
|
|
|
of this class/object are created dynamically on (user) demand. Its
|
|
|
function `getContent()` is supposed to deliver arbitrary content which
|
|
|
is then used as the visible content.
|
|
|
|
|
|
The hierarchical structuring into these three objects allows one single
|
|
|
extension (**Main**) to have multiple ways (**Config**) of displaying
|
|
|
arbitrary, parallel content (**Content**). The resulting two-step *1:n*
|
|
|
relationship is capable of hosting various types of extensions with
|
|
|
basic or even more sophisticated purposes.
|
|
|
|
|
|
#### Data access inside your Extension
|
|
|
|
|
|
TODO
|
|
|
|
|
|
Api
|
|
|
---
|
|
|
|
|
|
Each of the three objects has two types of members. Members with a
|
|
|
leading *underscore* have a *private* state. They are set by the
|
|
|
extension manager but can be accessed in your extension. Members without
|
|
|
the *underscore* are treated as normal variables.
|
|
|
|
|
|
#### Class `Extension`
|
|
|
|
|
|
- **`name`**
|
|
|
>MANDATORY! The unique name of the extension. Supposed to be the
|
|
|
same name as used as the folder name in the `extensions` folder or
|
|
|
as returned by `getName()` in your `extensions.py`. Case sensitive!
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`fileExtensions`**
|
|
|
>File extensions that can be handled by your extension passed as
|
|
|
a map with callbacks:
|
|
|
<code class="javascript">
|
|
|
this.fileExtensions = {
|
|
|
txt: function(path){/* foo */},
|
|
|
xml: function(path){/* bar */},
|
|
|
defaults: {txt: 1, xml: 9}
|
|
|
};
|
|
|
</code>
|
|
|
|
|
|
|
|
|
>The `defaults` entry registers the corresponding file extension
|
|
|
with its callback at a given preference to the extension manager.
|
|
|
LIFO in case of same preference.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`urlCallbacks`**
|
|
|
>URL channels that the extension listens to in a map with
|
|
|
callbacks:
|
|
|
<code class="javascript">
|
|
|
this.urlCallbacks = {
|
|
|
channelABC: function(path){/* foo */},
|
|
|
channelXYZ: function(path){/* bar */},
|
|
|
generators: ["channelABC", ...]
|
|
|
...
|
|
|
};
|
|
|
</code>
|
|
|
|
|
|
|
|
|
>A call to `vispadomain.tld/?channelABC=somestring` will cause
|
|
|
the function `function(path){/* foo */}` to be fired. The
|
|
|
`generators` entry defines channels for which a URL can be generated
|
|
|
dynamically (e.g. from within the `Analysis Designer`). (Also see
|
|
|
`ExtensionContent.getUrlChannelValue()`)
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`mainMenuEntries`**
|
|
|
>The main menu entries as an array of `dijit.MenuItem`’s. Please
|
|
|
use `onClick` events. They are automatically converted to `Tap`
|
|
|
events by the platform.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`mainMenuPreference`**
|
|
|
>This preference value (integer) steers the position of main
|
|
|
menu entries in the associated menu. No preference means
|
|
|
“sort-by-extension-name”. Same preferences are handled as LIFO.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`content`**
|
|
|
>The config classes passed with a key, e.g.:
|
|
|
<code class="javascript">
|
|
|
this.content = {
|
|
|
tabA: new TestTabA(),
|
|
|
frameB: new TestFrameA(),
|
|
|
...
|
|
|
};
|
|
|
</code>
|
|
|
|
|
|
#### Class `ExtensionConfig`
|
|
|
|
|
|
- **`_ext`**
|
|
|
>Private. Set by the extension manager. Stores a reference to
|
|
|
the extension main object for data access reasons.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`_key`**
|
|
|
>Private. Set by the extension manager. Stores the key with
|
|
|
which this config object is stored in the `content` object of the
|
|
|
extension main class.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`_lastId`**
|
|
|
>Private. Set by the extension manager. Stores the iterator that
|
|
|
is used to generate continuously running ids.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`_instances`**
|
|
|
>Private. Set by the extension manager. Stores the instances of
|
|
|
the referenced content constructor.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`_create()`**
|
|
|
>Private. Set by default. Creates new content instances.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`maxInstances`**
|
|
|
>The maximum number of parallel instances. 0 means infinit.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`showEmptyTab`**
|
|
|
>‘tab’ BEHAVIOR ONLY. Decides whether empty tabs are shown.
|
|
|
Overrides the global behavior when set.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`label`**
|
|
|
>‘tab’ behavior: used as the extension tab title.
|
|
|
>‘frame’ behavior: used as the frame title. Can be overridden by
|
|
|
the content function `getLabel()`.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`badgeIcon`**
|
|
|
>‘tab’ BEHAVIOR ONLY. The icon that is used in the badge when a
|
|
|
content instance is passive. Can be overridden by the content
|
|
|
function `getBadgeIcon()`. (HxW 45X45)
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`menuIcon`**
|
|
|
>The menu icon of the extension menu.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`style`**
|
|
|
>Additional css styles for the content passed as a string.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`behavior`**
|
|
|
>Supposed to be ‘tab’ (default) or ‘frame’, dependent on the
|
|
|
manner of representation of the extension content.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`constructor`**
|
|
|
>A reference to the class/the constructor that creates a new
|
|
|
content instance.
|
|
|
|
|
|
#### Class `ExtensionContent`
|
|
|
|
|
|
- **`_config`**
|
|
|
>Private. Set by the extension manager. Stores a reference to
|
|
|
the config objects that owns this instance.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`_id`**
|
|
|
>Private. Set by the extension manager. Stores a unique id for
|
|
|
this instance.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`_close()`**
|
|
|
>Private. Set by default. Closes this instance and handles all
|
|
|
following steps.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`getLabel()`**
|
|
|
>‘tab’ behavior: used as the title of the badge when this
|
|
|
instance is passive.
|
|
|
>‘frame’ behavior: overrides `label` of the config. Used as the
|
|
|
frame title.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`getBadgeIcon()`**
|
|
|
>‘tab’ BEHAVIOR ONLY. Overrides `badgeIcon` of the config. Can
|
|
|
be used to dynamically set the badge icon. (HxW 45X45)
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`extMenuEntries`**
|
|
|
>‘tab’ BEHAVIOR ONLY. Same as `mainMenuEntries` of the main
|
|
|
object but used to define the menu entries of the extension menu.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`getUrlChannelValue(channel)`**
|
|
|
>Returns a value for a URL-`channel` during dynamic URL
|
|
|
generation. (Also see `ExtensionConfig.urlChannels`)
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`getContent()`**
|
|
|
>**Used to deliver the content**. Can be a string, a dom node or
|
|
|
even a dojo object.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`beforeFirst()`**
|
|
|
>What happens before the first instance is created? Return false
|
|
|
to prevent the action.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`beforeOpen()`**
|
|
|
>What happens before the instance is opened? Return false to
|
|
|
prevent the action.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`afterOpen`**
|
|
|
>What happens after the instance is opened?
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`beforeClose()`**
|
|
|
>What happens before the instance is closed? Return false to
|
|
|
prevent the action.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`afterClose()`**
|
|
|
>What happens after the instance is closed?
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`beforeShow()`**
|
|
|
>‘tab’ BEHAVIOR ONLY. What happens before the instance is shown?
|
|
|
Return false to prevent the action.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`afterShow()`**
|
|
|
>‘tab’ BEHAVIOR ONLY. What happens after the instance is shown?
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`beforeHide()`**
|
|
|
>‘tab’ BEHAVIOR ONLY. What happens before the instance is
|
|
|
hidden? Return false to prevent the action.
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`afterHide()`**
|
|
|
>‘tab’ BEHAVIOR ONLY. What happens after the instance is hidden?
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`onResize()`**
|
|
|
>‘tab’ BEHAVIOR ONLY. What happens when the window is resized?
|
|
|
|
|
|
<!-- -->
|
|
|
|
|
|
- **`ready()`**
|
|
|
>**What happens when the content of the instance is loaded?**
|
|
|
This function should be used to render content, to bind events or to
|
|
|
execute other functions that have to be called only once at the very
|
|
|
beginning. Don’t mix with events that should be called in
|
|
|
‘afterShow’ or ‘afterOpen’ which can be fired multiple times!
|
|
|
|
|
|
Example
|
|
|
-------
|
|
|
|
|
|
This minimal example summarizes the basic usage of Extensions on
|
|
|
client-side.
|
|
|
It simply demonstrates the way to setup your Extension and thus, has no
|
|
|
further functionality.
|
|
|
All available options that implement individual behavior are listed in
|
|
|
the [Api](Extensions\_on\_client-side\_(JavaScript)\#Api).
|
|
|
|
|
|
### The Main Config Class
|
|
|
|
|
|
<code class="javascript">
|
|
|
function ExampleExtension ()
|
|
|
{
|
|
|
//inheritance
|
|
|
this.__proto__ = new Extension(this);
|
|
|
|
|
|
//lexical scope
|
|
|
var self = this;
|
|
|
|
|
|
//the name (must be set)
|
|
|
this.name = "example";
|
|
|
|
|
|
//menu entries for the main menu
|
|
|
//passed as a list of dijit.MenuItem's
|
|
|
this.mainMenuEntries = [
|
|
|
new dijit.MenuItem({label: "new Example Tab ...", iconClass: "vispa_icon vispa_icon_plus", onClick: function ()
|
|
|
{
|
|
|
//create a new tabA instance
|
|
|
self.content.tabA._create();
|
|
|
}}),
|
|
|
new dijit.MenuItem({label: "new Example Frame ...", iconClass: "vispa_icon vispa_icon_frame", onClick: function()
|
|
|
{
|
|
|
//create a new frameB instance
|
|
|
self.content.frameB._create();
|
|
|
}})
|
|
|
];
|
|
|
|
|
|
//the content
|
|
|
this.content = {
|
|
|
tabA: new ExampleExtensionTabA(),
|
|
|
frameB: new ExampleExtensionFrameB()
|
|
|
};
|
|
|
}
|
|
|
</code>
|
|
|
|
|
|
### The Config Classes
|
|
|
|
|
|
<code class="javascript">
|
|
|
function ExampleExtensionTabA ()
|
|
|
{
|
|
|
//inheritance
|
|
|
this.__proto__ = new ExtensionConfig(this);
|
|
|
|
|
|
//the reference to the Content Class (no instance!)
|
|
|
this.constructor = ExampleExtensionTabAContent;
|
|
|
}
|
|
|
|
|
|
function ExampleExtensionFrameB ()
|
|
|
{
|
|
|
//inheritance
|
|
|
this.__proto__ = new ExtensionConfig(this);
|
|
|
|
|
|
//the reference to the Content Class (no instance!)
|
|
|
this.constructor = ExampleExtensionFrameBContent;
|
|
|
|
|
|
//'behavior' defaults to 'tab', but this one will be a 'frame', so set it
|
|
|
this.behavior = "frame";
|
|
|
}
|
|
|
</code>
|
|
|
|
|
|
### The Content Classes
|
|
|
|
|
|
<code class="javascript">
|
|
|
function ExampleExtensionTabAContent ()
|
|
|
{
|
|
|
//inheritance
|
|
|
this.__proto__ = new ExtensionContent(this);
|
|
|
|
|
|
//the content
|
|
|
this.getContent = function ()
|
|
|
{
|
|
|
return "TabA Content";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function ExampleExtensionFrameBContent ()
|
|
|
{
|
|
|
//inheritance
|
|
|
this.__proto__ = new ExtensionContent(this);
|
|
|
|
|
|
//the content
|
|
|
this.getContent = function ()
|
|
|
{
|
|
|
return "FrameB Content";
|
|
|
}
|
|
|
}
|
|
|
</code>
|
|
|
|
|
|
### The Registration
|
|
|
|
|
|
<code class="javascript">
|
|
|
//Proceed when dojo is loaded
|
|
|
dojo.ready(function ()
|
|
|
{
|
|
|
//register the Extension
|
|
|
vispa.ExtensionManager.registerExtension(new ExampleExtension());
|
|
|
});
|
|
|
</code>
|
|
|
|
|
|
The final call `vispa.ExtensionManager.registerExtension()` can also be
|
|
|
replaced by a call to the channel `registerExtension`:
|
|
|
|
|
|
<code class="javascript">
|
|
|
dojo.publish("registerExtension", [new ExampleExtension()]);
|
|
|
</code>
|
|
|
|
|
|
|
|
|
For further details on dojo’s publish/subscribe event mechanism, [click
|
|
|
here](http://livedocs.dojotoolkit.org/dojo/publish) .
|
|
|
|
|
|
Troubleshooting
|
|
|
---------------
|
|
|
|
|
|
- JavaScript’s `this` is (in some cases) tricky. For a distinct usage
|
|
|
of class variables use a lexical scope and define
|
|
|
<code class="javascript">
|
|
|
var self = this;
|
|
|
</code>
|
|
|
|
|
|
|
|
|
at the beginning of a class.
|
|
|
|