''NOTE: This page is still Work In Progress'''

=WebEditor Framework=

The WebEditor framework (WEF) is a framework that consists of a a few core components and UI widgets. Together they allow plugins to be easily developed and added to the WebEditor. 
When deployed, it loads all specified resources and creates a ribbon widget which plugins can add their functionality to. Typically, this means adding a toolbar and its buttons and perhaps a help link.
The plugin-able nature of the framework is supplied by the support of hooks around core methods which fire events before and after when each method is called. Other methods can also be specified so thatbefore and after events are fired for those methods. See the [[#Events_and_Hooks]] section

==Core WEF Components==

A high level description of the core components are described below. All javascript files are fully documented with jsdoc and additional info may be found there.

===WEF===

The WEF object is responsible for bootstrap the WebEditor framework onto the page by loading all required modules and starting the application when all the resources are loaded. It has a config which looks likes this:

<pre>
WEF.init(
{
   /**
    * Context path of application
    * 
    * @type String
    */
   contextPath: "/awe",
   
   /**
    * Debug mode
    * 
    * @type Boolean
    */
   debugMode: false,

   /**
    * constants
    * @type Object 
    */
   constants: {},

   /**
    * Object literal of applications to render.
    * 
    * @type Object 
    */ 
   applications: {},
   
   /**
    * Configuration for loader
    *  
    */
   loaderConfig: 
   {
      [See Loader for information about the loader config]
   }
});
</pre>

===Loader===

The loader is responsible for setting up resources for modules (and any of their dependencies) to be loaded by the browser. A resource can be specified as one that should be loaded as follow:

<pre>
   WEF.addResource({
      name: "com.yahoo.bubbling", 
      type: "js",
      path: "\/awe/res/wef/bubbling.v2.1.js",
      requires: ["utilities"],
      varName: "YAHOO.Bubbling"
   }
);
</pre>

The loader can be configured and has the following options:

<pre>
   {
      /**
       * Server port of awe app
       *
       * @type String
       */
      serverPort: window.location.protocol + "//" + window.location.host,
      
      /**
       * Context path of awe app
       *
       * @type String
       */
      urlContext: window.location.protocol + "//" + window.location.host + "/awe" + "/res",
      
      /**
       * Use sandbox to load files
       *
       * @type Boolean
       */
      useSandboxLoader: false,
      
      /**
       * Path to yuiloader. This is loaded via script tags so can be absolute or relative
       * 
       * @type String 
       */
      yuiloaderPath: "/yui/yuiloader/yuiloader-debug.js",
      
      /**
       * Base path to yui files. Use empty string to use YDN 
       * 
       * @type string
       */
      yuibase: "/awe/res/yui/",
      
      /**
       * A filter to apply to loader.
       * 3rd party plugins need to be available in -[filter].js versions too
       * Defaults to min if not supplied.
       *
       * @type String 
       */
      filter: "min",
      
      /**
       * Flag for yui loader to determine whether to load extra optional resources as well
       *
       * @type Boolean 
       */
      loadOptional: true,
      
      /**
       * Skin overrides for YUI
       * 
       * @type Object 
       */
      skin: 
      {
         base: "/assets/skins/",
         defaultSkin: "sam" 
      }               
   }

</pre>

===ConfigRegistry===

The ConfigRegistry object is responsible for maintaining a registry of configuration objects for a specified plugin. It has two methods

====registerConfig()====

This registers a config for a specified plugin using the id of the plugin. 
   
   WEF.ConfigRegistry.registerConfig('org.springframework.extensions.webeditor.ui.ribbon',{ position: "top" });

====getConfig()====

getConfig returns the config for the specified plugin

   WEF.ConfigRegistry.getConfig('org.springframework.extensions.webeditor.ui.ribbon');

===PluginRegistry===

The PluginRegistry maintains a registry of plugins and has the following methods:

====getPlugin()====

The getPlugin method returns a reference to the plugin class (not instance). 

   WEF.PluginRegistry.getPlugin('my.plugin'));

====registerInstance()====

This is used to register an instance of a plugin.

   WEF.PluginRegistry.registerInstance(config.id, pluginInstance));
   
====getInstance()====

getInstance returns a instance of the specified plugin

   WEF.PluginRegistry.getInstance(pluginId)

===Base===

The WEF.Base is the core class that all wef components are based on. It provides init() and destroy() methods and also i18n functionality via setMessages() and getMessage().  

===Plugin===

The Plugin component builds on Base and every plugin is expected to be one that extends Plugin or one of its subclasses. It provides activate() and deactivate() methods allowing plugins to be custom logic to be added when a plugin is (de)activated. In addition, it also adds a render() method, which is used to add elements to the page. Each method also fires before and after events that can be used to notify others that the plugin is being activated/deactivated/rendered.

====Widget====

A widget is a UI element that can be interacted with. A dialog is a good example of a widget. In addition to methods supplied by Plugin and Base, widgets adds show() and hide() methods. The show() and hide() methods add/remove the css class names of wef-show and wef-hide which show and hide the plugin's main element respectively.

====App====

An App is just a plugin and add no additional methods of its own. 

==Core WEF Widgets==

The Core WEF widgets are components that the WEF use and plugin authors can either use directly or indirectly.

===Ribbon===

The Ribbon component is responsible for displaying the ribbon at the top of the page and also for managing and/or registered toolbars and plugins. It has the following methods available:

====addToolbar()====

This method adds a toolbar to the Ribbon. Normally this is the method used to add new tabs to the ribbon which in turn contain the toolbar(s).
   
====addButtons()====

This method adds the specified buttons to the specified toolbar.
   
====getToolbar()====

This returns a reference to the specified toolbar
   
===Toolbar===

The toolbar component is a simple component that manages a collection of buttons. It has the following methods available:

====addButtons()====

This method adds the specified buttons to the toolbar.
   
====getButtonById()====

This method returns a reference to the specified button.

=====Button Handling=====

A note of button handling. Any toolbar button when interacted with fires a namespaced event. That is, an event with a name that consists of the plugin id + button id + event type eg "helloworldPlugin--hello-worldClick" would be an name for a click event for a button with an id of hello-world belonging to a plugin called helloworldPlugin. Any handler should listen to that event name in order to respond to it.

===Tabbed Toolbar===

The Tabbed Toolbar manages a collection of toolbars that are rendered in different tabs. This allows plugins to add their own functionality into either existing tabs or into their own tabs.

====addToolbar()====

This method adds a new tab with an Toolbar as the content.

====addButtons()====

This method adds the specified buttons to the specified toolbar.
   
====getToolbar()====

This returns a reference to the specified toolbar

==Developing a plugin==

This section describes how to add a sample plugin for the WebEditor. In most instances, this means that the plugin adds some functionality to the WebEditor ribbon which the user can then interact with. In this sample, a plugin adds a new tab to the ribbon and a button that when clicked shows an alert dialog displaying 'Hello World!'. In addition, it will also show how to change the help link (at the top of the ribbon) so it is context sensitive to the current plugin.

===An Hello World plugin===

A useful starting stub for your plugin is as follows:
<pre>
(function() 
{
   var Dom =YAHOO.util.Dom,
       Event = YAHOO.util.Event,
       KeyListener = YAHOO.util.KeyListener,
       Selector = YAHOO.util.Selector,
       Bubbling = YAHOO.Bubbling,
       Cookie = YAHOO.util.Cookie,
       WebEditor = YAHOO.org.springframework.extensions.webeditor;

   YAHOO.namespace('wef.pluginname');

   /**
    * Sample plugin constructor
    * @constructor
    * @class Sample
    * @namespace YAHOO.wef.pluginname
    * @extends WEF.App
    */
   YAHOO.wef.pluginname = function(config)
   {
      config.setUpCustomEvents = (['customMethod']).concat(config.setUpCustomEvents || []);
      YAHOO.wef.pluginname.superclass.constructor.apply(this, Array.prototype.slice.call(arguments));
   };

   YAHOO.extend(YAHOO.wef.pluginname, WEF.App,
   {
      //plugin implementation goes here
   });

})();

WEF.register("wef.pluginname", YAHOO.wef.pluginname, {version: "1.0.1", build: "1"});
</pre>

The first name in the constructor is purely optional and highlights how we would add automatic firing of before and after events for a method called customMethod, by using the setUpCustomEvents property. This can be specified in the constructor, as here, or via the plugin config.

After that there are three methods that should be implemented. These are init(), render(), and onHelp(). Init() should contain any logic to initialise the plugin. For our example, which is a very simple one, we register an event handler for when a button is clicked. If the plugin was more complicated, it might be useful to use managed attributes. Managed attributes are attributes which fire events when the value of the attribute changes. If so, then the initAttributes method should be called.

<pre>
      init: function SamplePlugin_init()
      {
         // call the superclass init function
         YAHOO.wef.plugin.sample.superclass.init.apply(this);
         
         // register click handler for the toolbar button
         Bubbling.on(this.config.name + WebEditor.SEPARATOR + 'hello-worldClick', this.helloWorldClick, this, true);

         // if using managed events
         //this.initAttributes(this.config)
         return this;
      },
</pre>

Our sample plugin needs to add a toolbar and a button. We can do this in the render() method.

<pre>
      render: function SamplePlugin_render()
      {
         // get the current context path
         var contextPath = WEF.get("contextPath");
         
         // add a primary toolbar
         var tb  = WebEditor.module.Ribbon.addToolbar('WEF-'+WebEditor.ui.Ribbon.PRIMARY_TOOLBAR+'-root',
         {
            id: 'WEF-'+WebEditor.ui.Ribbon.PRIMARY_TOOLBAR+'-root',
            name: 'WEF-'+WebEditor.ui.Ribbon.PRIMARY_TOOLBAR+'-root',
            label: '<img src="' + contextPath + '/res/sample/images/toolbar-icon.png" alt="'+ this.getMessage('hello-world-tab') +'" />',
            title: this.getMessage('wef.plugin.sample.hello-world-tab'),
            content: '',
            active: true,
            pluginOwner:this
         }, WebEditor.ui.Toolbar);

         // add a button to the primary toolbar
         tb.addButtons(
         [
            {
               type: 'push',
               label: '<img src="' + contextPath + '/res/sample/images/toolbar-button.gif" alt="'+ this.getMessage('hello-world-button') +'" />',
               title: this.getMessage('wef.plugin.sample.hello-world-button'),
               value: this.config.namespace + WebEditor.SEPARATOR + 'hello-world',
               id: this.config.name + WebEditor.SEPARATOR + 'hello-world',
               icon: true
            }
         ]);
      },   
</pre>

First, the addToolbar method adds a toolbar with an image for the tab and a title for a tooltip. The pluginOwner attribute is required so the framework can track which is the active plugin. (See [[#PluginActivation section]]). Next, a button is added to the newly created toolbar.

Finally, if the plugin has any help content somewhere then we add an onHelp handler which simply opens a new browser window to the help page.

<pre>   
   onHelp: function SamplePlugin_onHelp()
   {
      window.open('http://www.springsurf.org', 'samplehelp');
   }
</pre>

If more complicated help logic is required, then this can be added here instead. For example, inline help within a dialog rather than an external link.

====PluginActivation====
If your plugin is more complicated than our example, it's possible that you need some custom logic that should be run everytime your plugin is activated or deactivated. A plugin is activated or deactivated everytime the active tab is changed (in the ribbon) by the user clicking on a non-active tab. Add your logic to the activate() and deactivate() method. It's also best practice to add a destroy() method which should cleanup after the plugin (unsubscribe event handlers etc). A plugin's destroy method is called when a page is unloaded (currently not implemented).


==Developing a plugin to an existing plugin==

The [[#Developing_a_plugin]] section described how to add a plugin to the ribbon. The section describes how to add an plugin to a plugin that has already been added.
In particular we'll change the hello world plugin so it's a plugin to the Alfresco WebEditor (AWE) but still renders it own toolbar.

First, we subscribe to the afterRender of the AWE plugin, see below:
<pre>
      init: function SamplePlugin_init()
      {
         // call the superclass init function
         YAHOO.wef.plugin.sample.superclass.init.apply(this);
         
         // register click handler for the toolbar button
         Bubbling.on(this.config.name + WebEditor.SEPARATOR + 'hello-worldClick', this.helloWorldClick, this, true);
         
         // if using managed events
         //this.initAttributes(this.config)
         
         //render our plugin after the AWE is rendered
         Bubbling.on('awe'+WebEditor.SEPARATOR+'afterRender', this.render, this, true);
         return this;
      },
</pre>

But as outlined in the [[#Bootstrap_process]], every loaded plugin has its render() method called explicitly. This means that it's possible that our render method is called before the AWE plugin is rendered. So a small change is required to the render() method.

<pre>
      render: function SamplePlugin_render()
      {
         if (arguments.length===0)
         {
            return;
         }
         // get the current context path
         var contextPath = WEF.get("contextPath");
         
         // add a primary toolbar
         var tb  = WebEditor.module.Ribbon.addToolbar('WEF-'+WebEditor.ui.Ribbon.PRIMARY_TOOLBAR+'-root',
         {
            id: 'WEF-'+WebEditor.ui.Ribbon.PRIMARY_TOOLBAR+'-root',
            name: 'WEF-'+WebEditor.ui.Ribbon.PRIMARY_TOOLBAR+'-root',
            label: '<img src="' + contextPath + '/res/sample/images/toolbar-icon.png" alt="'+ this.getMessage('hello-world-tab') +'" />',
            title: this.getMessage('wef.plugin.sample.hello-world-tab'),
            content: '',
            active: true,
            pluginOwner:this
         }, WebEditor.ui.Toolbar);

         // add a button to the primary toolbar
         tb.addButtons(
         [
            {
               type: 'push',
               label: '<img src="' + contextPath + '/res/sample/images/toolbar-button.gif" alt="'+ this.getMessage('hello-world-button') +'" />',
               title: this.getMessage('wef.plugin.sample.hello-world-button'),
               value: this.config.namespace + WebEditor.SEPARATOR + 'hello-world',
               id: this.config.name + WebEditor.SEPARATOR + 'hello-world',
               icon: true
            }
         ]);
      },   
</pre>

We only want the render() method to be called in response to an event, so simple logic is added so nothing happens if there are no arguments passed in, since we know that an event is passed in but nothing is passed in when it is called directly by the framework.

==Events and Hooks==

The web editor is an event driven framework. This means that in order for plugins to work, they need to listen to certain events and fire certain events. Every core method of a plugin fires a before and an after event for that method. For instance, a WEF.Base has an init and a destroy method. Each time the init method is called, for instance, a before event is fired, then the init method is executed and finally an after event is fired. (Note, the events are fired *automatically* for the core methods and any developer specified methods). So if you need to do something after a plugin is initialised, you can subscribe to the afterInit method of that plugin and when the init method has been executed, your event handler will then be run. Each component in the WEF tree has their own core methods and each of these also fire their own before and after events. WEF.Plugin has activate() and deactivate() and WEF.Widget has render(), show() and hide() methods. If you require before and after methods to be fired for one or more custom methods of your own plugin, then these can be specified in the plugin config. Add a property called setUpCustomEvents of Array type containing the names of the methods you wish to have before and after events fired for and it will be set up. Note: the actual event names are namespaced.  This means that in order to subscribe to the events you have to know the name of the originating plugin (as specified in the plugin's config.name property). This is because the name of the event will be in this format config.name--eventName eg for a plugin with a name of simplePlugin the event name for its before init event will be 'samplePlugin--beforeInit'.

===Bootstrap process===

The section describes what happens when the framework bootstraps itself and the firing order of the events.

When WEF loads all specified resources, it fires a WEF--afterInit event. It does this after creating the Ribbon and running the main app. The main app subscribes a handler to the WEF-Ribbon--afterRender event and, when this event is fired, this handler creates an instance of every registered resource that is loaded and of a plugin type and calls its init method. When all plugins have been instantiated and initialised, only then are the plugins rendered. After the main app is run, WEF then calls its render method which ultimately fires a WEF--afterRender event. That event is subscribed to by WEF-Ribbon which renders its own elements and then fires off its own WEF-Ribbon-afterRender event. Remember, it is only now, after the ribbon has rendered, that the plugins and instantiated and initialised (as described above).

So, in summary  the actual order is
   WEF--afterInit event is fired
      WEF.render()
   WEF--afterRender event is fired
   Ribbon is subscribed to WEF--afterRender to run its render() method
      Ribbon.render()
   Ribbon fires WEF-Ribbon--afterRender event
   Any plugins which subscribe to WEF-Ribbon--afterRender are now instantiated and their init() method is called.
      plugin1()
      plugin1.init();
      plugin2()
      plugin2.init();
  Finally each plugin has its render() method called
      plugin1.render()
      plugin2.render();

Perhaps it might be better described in an image:

[[File:WEF_Loading_sequence.png|500px]]


note over WEF,WEF: has loaded all resources successfully
WEF->App: calls App()
note over App,App: 
 subscribes to 
 WEF-Ribbon-afterRender event
end note
WEF->Ribbon:
Ribbon->Ribbon: init()
WEF->Ribbon: fires WEF-afterInit event
WEF->WEF: render()
WEF->Ribbon: fires WEF-afterRender event
Ribbon->Ribbon: render()
Ribbon->App: fires WEF-Ribbon--afterRender event
note left of App: All plugins are instantiated
note over App: Plugin1 is instantiated
App->Plugin1: init()
note over App: Plugin2 is instantiated
App->Plugin2: init()
note left of App: All plugins are rendered
App->Plugin1: render()
App->Plugin2: render()

      