Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

How to create a YUI 3 module: Difference between revisions

From MoodleDocs
(Obsolete this code - it has too many best practices and is poorly named. I'm moving it to YUI/Modules and will set up a redirect once complete.)
(21 intermediate revisions by 9 users not shown)
Line 1: Line 1:
{{obsolete}} Please note that much of this documentation is obsolete. A replacement is currently being written in [[YUI/Modules]].
{{Moodle 2.0}}This document explains how to create a YUI 3 module in Moodle.
{{Moodle 2.0}}This document explains how to create a YUI 3 module in Moodle.


= The two ways to write a YUI 3 module =
= The two ways to write a YUI 3 module =
There is two ways to write a YUI 3 module. Following an explanation by Sam Hemelryk.
There is two ways to write a YUI 3 module. The two following paragraphs are sourced from a chat with Sam Hemelryk.
==The static module==
==The static module==
The static module is the simpler of the two, in this case you simply put your code into a module.js file within your plugin. Into that file you create a namespace and define any functions or objects you want to use.
The static module is the simpler of the two, in this case you simply put your code into a module.js file within your plugin. Into that file you create a namespace and define any functions or objects you want to use.
Line 18: Line 20:
This method is certainly more complex than the previous method however it is much more flexible and when you have learnt and understand what you are doing it is much quicker to write and more versatile as well. These modules can also easily include other YUI3 Moodle modules, and can choose to include module specific CSS as well.
This method is certainly more complex than the previous method however it is much more flexible and when you have learnt and understand what you are doing it is much quicker to write and more versatile as well. These modules can also easily include other YUI3 Moodle modules, and can choose to include module specific CSS as well.


In this method you create a '''yui''' directory within you plugin directory, and then sub directories for each module you wish to write, you name them the same as you want your module named.
In this method you create a '''yui''' directory within your plugin directory, and then sub directories for each module you wish to write.  You name the subdirectories the same as you want your module named.
Into the subdirectory you create a JavaScript file with the same name that you gave the subdirectory, e.g.'''/local/myplugin/yui/mymodule/mymodule.js'''
In each subdirectory you create a JavaScript file with the same name that you gave the subdirectory, e.g.'''/local/myplugin/yui/mymodule/mymodule.js'''
The JS that you then put into this file should then be written in the same way you would write a YUI module, before you ask it certainly requires a good understanding of YUI.
The JS that you then put into this file should then be written in the same way you would write a YUI module.  Before you ask it certainly requires a good understanding of YUI.


I would recommend this method of writing a module providing you either have an understanding of how to write a YUI module, or you are happy to learn. It normally results in cleaner easier to read code, and if desired can easily be written in such as way as to make it VERY easy to re-use in other bits of code.... particularly helpful if you have several JavaScript objects in play at a time.
I would recommend this method of writing a module providing you either have an understanding of how to write a YUI module, or you are happy to learn. It normally results in cleaner easier to read code, and if desired can easily be written in such as way as to make it VERY easy to re-use in other bits of code.... particularly helpful if you have several JavaScript objects in play at a time.
Line 27: Line 29:
1. Create a module.js file somewhere  
1. Create a module.js file somewhere  


2. The two way Moodle can load this file are:
2. The two ways Moodle can load this file are:


a- the file is in core: declare the file into /lib/outputrequirementslib.php/find_module($component)
a- the file is in core: declare the file into /lib/outputrequirementslib.php/find_module($component)
Line 54: Line 56:
   }
   }
</code>
</code>
4. Call this module. It can not be in a php renderer script. However you could call it where you call the renderer.
4. Call this module. The best place to do this is in your renderer class (since, if a theme overrides the renderer to change the HTML, they may also need to use different JavaScript.
<code php>
<code php>
     $PAGE->requires->js_init_call('M.local_hub.init');  
     $this->page->requires->js_init_call('M.local_hub.init');  
You should now have loaded and called your first YUI 3 module in Moodle.
You should now have loaded and called your first YUI 3 module in Moodle.
</code>
</code>
Line 65: Line 67:
                 'fullpath' => '/local/hub/module.js',
                 'fullpath' => '/local/hub/module.js',
                 'requires' => array("overlay", "anim", "plugin")); //on this line you are loading three other YUI modules
                 'requires' => array("overlay", "anim", "plugin")); //on this line you are loading three other YUI modules
    $PAGE->requires->js_init_call('M.local_hub.init',
  $this->page->requires->js_init_call('M.local_hub.init',
                 null, false, $jsmodule);
                 null, false, $jsmodule);
</code>
</code>
Line 72: Line 74:
PHP:
PHP:
<code php>
<code php>
     $PAGE->requires->js_init_call('M.local_hub.init',
     $this->page->requires->js_init_call('M.local_hub.init',
                     array('this is the param1 value'), false, $jsmodule);
                     array('this is the param1 value'), false, $jsmodule);
</code>
</code>
Line 93: Line 95:
     var MODULENAME = function() {
     var MODULENAME = function() {
         MODULENAME.superclass.constructor.apply(this, arguments);
         MODULENAME.superclass.constructor.apply(this, arguments);
     }
     };
     Y.extend(MODULENAME, Y.Base, {
     Y.extend(MODULENAME, Y.Base, {
         initializer : function(config) { //'config' contains the parameter values
         initializer : function(config) { // 'config' contains the parameter values
             alert('I am in initializer');
             alert('I am in initializer');
         }
         }
     }, {
     }, {
         NAME : ModulenameNAME, //module name is something mandatory.  
         NAME : ModulenameNAME, //module name is something mandatory.  
                                 //It should be in lower case without space  
                                 // It should be in lower case without space  
                                 //as YUI use it for name space sometimes.
                                 // as YUI use it for name space sometimes.
         ATTRS : {
         ATTRS : {
                 aparam : {}
                 aparam : {}
         } // Attributs are the parameters sent when the $PAGE->requires->yui_module calls the module.  
         } // Attributes are the parameters sent when the $PAGE->requires->yui_module calls the module.  
           // Here you can declare default values or run functions on the parameter.  
           // Here you can declare default values or run functions on the parameter.  
           // The param names must be the same as the ones declared  
           // The param names must be the same as the ones declared  
           // in the $PAGE->requires->yui_module call.
           // in the $PAGE->requires->yui_module call.
     });
     });
     M.local_pluginname = M.local_pluginname || {}; //this line use existing name path if it exists, ortherwise create a new one.  
     M.local_pluginname = M.local_pluginname || {}; // This line use existing name path if it exists, otherwise create a new one.  
                                                 //This is to avoid to overwrite previously loaded module with same name.
                                                 // This is to avoid to overwrite previously loaded module with same name.
     M.local_pluginname.init_modulename = function(config) { //'config' contains the parameter values
     M.local_pluginname.init_modulename = function(config) { // 'config' contains the parameter values
         alert('I am in the javascript module, Yeah!');
         alert('I am in the javascript module, Yeah!');
         return new MODULENAME(config); //'config' contains the parameter values
         return new MODULENAME(config); // 'config' contains the parameter values
     }
     };
   }, '@VERSION@', {
   }, '@VERSION@', {
       requires:['base','another_required_YUI_module', 'a_moodle_YUI_module']
       requires:['base','another_required_YUI_module', 'a_moodle_YUI_module']
   });
   });
</code>
</code>
3. Call the javascript in PHP (if you copied the previous javascript, 2 javascript alert should appear.)
3. Call the javascript in PHP. Again, it is best to do this in a renderer class. (if you copied the previous javascript, 2 javascript alert should appear.)
<code php>
<code php>
   $PAGE->requires->yui_module('moodle-local_pluginname-modulename', 'M.local_pluginname.init_modulename',
   $this->page->requires->yui_module('moodle-local_pluginname-modulename', 'M.local_pluginname.init_modulename',
                 array(array('aparam'=>'paramvalue')));
                 array(array('aparam'=>'paramvalue')));
</code>
</code>
== About 'extend' ==
The example code above shows how to create a class that extends from the YUI 'base' class. If you only want to write code that is on its own and does not extend a YUI class, you do not need to use this format. For example, here is how you could include the same code from the 'static example' above but in the YUI module structure:
<code javascript>
YUI.add('moodle-local_pluginname-yuimodulename', function(Y) {
    M.local_pluginname = {
        init : function() {
            alert('pluginname yuimodulename initialisation');
        },
      }
}, '@VERSION@', {
    requires:['node', 'another_required_YUI_module', 'a_moodle_YUI_module']
});
</code>
This is the same code as in the simple static example above except that:
* The YUI.add function is called, giving a unique YUI 3 module name for this code.
* The Y parameter does not need to be stored inside M.local_hub because it is already available from being inside the outer function.
* Required YUI 3 modules are explicitly listed in the code.
== Full simple example==
== Full simple example==
Once you understand and run the above quick start, here is simple YUI 3 example. It shows how to use another YUI 3 module in your module, and to use some events.
=== It includes===
=== It includes===
* display a parameter value in two javascript alert boxes => demonstrate how to pass a param to the initialize function in two different ways
* display a parameter value in two javascript alert boxes => demonstrate how to pass a param to the initialize function in two different ways
Line 276: Line 302:
});
});
</code>
</code>
==Where are my methods in the IDE?==
Some (all?) IDEs will fail to parse the above structure correctly and will not show you your methods in the structure/navigator pane. If this happens, use JSDoc format to mark the Y.extend() line as being a particular class:
<code javascript>   
/**
* @class M.local_hub.init_comments
*/
Y.extend(COMMENTS, Y.Base, {
</code>
This depends to a certain extent on how you choose to instantiate your objects. The above init_widget() method can be replaced with a more class based method like a lot of the YUI library uses.
= What else to read =
= What else to read =
2 pages are important to start with YUI 3  
Two pages are important to start with when you discover YUI 3
* you should read the [http://developer.yahoo.com/yui/3/base/ base YUI doc]. It is very useful to understand the YUI 3 Moodle module.
* you should read the [http://developer.yahoo.com/yui/3/base/ base YUI doc]. It is very useful to understand the YUI 3 Moodle module.
* you want to have a look to the [http://developer.yahoo.com/yui/3/examples/ YUI 3 examples]. Always open the examples in a window to have a look to the complete example codes.
* you want to have a look to the [http://developer.yahoo.com/yui/3/examples/ YUI 3 examples]. Always open the examples in a window to have a look to the complete example codes.
[[Category:AJAX]]
[[Category:Javascript]]
== See Also ==
* [[Javascript and YUI 3 FAQ]]
* [[JavaScript FAQ]]

Revision as of 01:01, 25 March 2013

Warning: This page is no longer in use. The information contained on the page should NOT be seen as relevant or reliable.
Please note that much of this documentation is obsolete. A replacement is currently being written in YUI/Modules.

Moodle 2.0

This document explains how to create a YUI 3 module in Moodle.

The two ways to write a YUI 3 module

There is two ways to write a YUI 3 module. The two following paragraphs are sourced from a chat with Sam Hemelryk.

The static module

The static module is the simpler of the two, in this case you simply put your code into a module.js file within your plugin. Into that file you create a namespace and define any functions or objects you want to use.

Within PHP static modules are also pretty easy to use. The first step if to create a module definition which tells moodle what your module is called, what it requires, and can optionally give it strings.

Once the module is defined you can then use any of the several JS methods for including JS in your page and pass your module definition along.

Whilst this method is simpler to write and understand especially when you are first starting out with JavaScript it isn't as easily flexible and has some quirks that you need will need to overcome, especially with more complex JavaScript.

This method of writing a module should be used if you are just starting out with JavaScript or if the JS solution you are creating is going to be relatively simple in nature. If you are writing something more complex I would strongly suggest looking at the other method as it will result in cleaner, easier to read code at the end of the day. However it will require you to learn your way around writing a Moodle module.

The YUI3 Moodle Module

This method is certainly more complex than the previous method however it is much more flexible and when you have learnt and understand what you are doing it is much quicker to write and more versatile as well. These modules can also easily include other YUI3 Moodle modules, and can choose to include module specific CSS as well.

In this method you create a yui directory within your plugin directory, and then sub directories for each module you wish to write. You name the subdirectories the same as you want your module named. In each subdirectory you create a JavaScript file with the same name that you gave the subdirectory, e.g./local/myplugin/yui/mymodule/mymodule.js The JS that you then put into this file should then be written in the same way you would write a YUI module. Before you ask it certainly requires a good understanding of YUI.

I would recommend this method of writing a module providing you either have an understanding of how to write a YUI module, or you are happy to learn. It normally results in cleaner easier to read code, and if desired can easily be written in such as way as to make it VERY easy to re-use in other bits of code.... particularly helpful if you have several JavaScript objects in play at a time.

Static module quick start

1. Create a module.js file somewhere

2. The two ways Moodle can load this file are:

a- the file is in core: declare the file into /lib/outputrequirementslib.php/find_module($component)

         //example:
         case 'core_rating':
                   $module = array('name'     => 'core_rating',
                                   'fullpath' => '/rating/module.js',
                                   'requires' => array('node', 'event', 'overlay', 'io', 'json'));

b- the file is in a local plugin: the first line of your module should be

M.local_xxx = {

xxx being your plugin directory name.

3. Write the module.js

 M.local_hub={
   Y : null,
   transaction : [],
   init : function(Y){
       alert('hub module initialisation');
       this.Y = Y;
   },
 }

4. Call this module. The best place to do this is in your renderer class (since, if a theme overrides the renderer to change the HTML, they may also need to use different JavaScript.

   $this->page->requires->js_init_call('M.local_hub.init'); 

You should now have loaded and called your first YUI 3 module in Moodle. 5. If you want to load your module with some other YUI modules

   $jsmodule = array(
               'name' => 'local_hub',
               'fullpath' => '/local/hub/module.js',
               'requires' => array("overlay", "anim", "plugin")); //on this line you are loading three other YUI modules
  $this->page->requires->js_init_call('M.local_hub.init',
                null, false, $jsmodule);

6. If you want to pass parameter to the init function

PHP:

   $this->page->requires->js_init_call('M.local_hub.init',
                   array('this is the param1 value'), false, $jsmodule);

JS:

   init : function(Y, params){
       alert(params);
       ....
   }

7. Then you should be able to fill the module with your own YUI javascript from YUI examples.

YUI 3 Moodle Module quick start

1. Create the module_name.js file in /local/pluginname/yui/module_name/modulename.js or in /mod/modname/yui/modulename/modulename.js

2. The basic module_name.js structure is:

 YUI.add('moodle-local_pluginname-modulename', function(Y) {
   var ModulenameNAME = 'this_is_a_module_name';
   var MODULENAME = function() {
       MODULENAME.superclass.constructor.apply(this, arguments);
   };
   Y.extend(MODULENAME, Y.Base, {
       initializer : function(config) { // 'config' contains the parameter values
           alert('I am in initializer');
       }
   }, {
       NAME : ModulenameNAME, //module name is something mandatory. 
                               // It should be in lower case without space 
                               // as YUI use it for name space sometimes.
       ATTRS : {
                aparam : {}
       } // Attributes are the parameters sent when the $PAGE->requires->yui_module calls the module. 
         // Here you can declare default values or run functions on the parameter. 
         // The param names must be the same as the ones declared 
         // in the $PAGE->requires->yui_module call.
   });
   M.local_pluginname = M.local_pluginname || {}; // This line use existing name path if it exists, otherwise create a new one. 
                                                // This is to avoid to overwrite previously loaded module with same name.
   M.local_pluginname.init_modulename = function(config) { // 'config' contains the parameter values
       alert('I am in the javascript module, Yeah!');
       return new MODULENAME(config); // 'config' contains the parameter values
   };
 }, '@VERSION@', {
     requires:['base','another_required_YUI_module', 'a_moodle_YUI_module']
 });

3. Call the javascript in PHP. Again, it is best to do this in a renderer class. (if you copied the previous javascript, 2 javascript alert should appear.)

 $this->page->requires->yui_module('moodle-local_pluginname-modulename', 'M.local_pluginname.init_modulename',
               array(array('aparam'=>'paramvalue')));

About 'extend'

The example code above shows how to create a class that extends from the YUI 'base' class. If you only want to write code that is on its own and does not extend a YUI class, you do not need to use this format. For example, here is how you could include the same code from the 'static example' above but in the YUI module structure:

YUI.add('moodle-local_pluginname-yuimodulename', function(Y) {

   M.local_pluginname = {
       init : function() {
           alert('pluginname yuimodulename initialisation');
       },
     }

}, '@VERSION@', {

   requires:['node', 'another_required_YUI_module', 'a_moodle_YUI_module']

});

This is the same code as in the simple static example above except that:

  • The YUI.add function is called, giving a unique YUI 3 module name for this code.
  • The Y parameter does not need to be stored inside M.local_hub because it is already available from being inside the outer function.
  • Required YUI 3 modules are explicitly listed in the code.

Full simple example

Once you understand and run the above quick start, here is simple YUI 3 example. It shows how to use another YUI 3 module in your module, and to use some events.

It includes

  • display a parameter value in two javascript alert boxes => demonstrate how to pass a param to the initialize function in two different ways
  • display an overlay when you click on an image
  • hide the overlay when you click on the body
  • some commented code to see how Moodle YUI exception works

PHP

Create a basic local plugin (in this example it is named hub) in /local/hub/ and add this code into one of the plugin pages:

  $PAGE->requires->yui_module('moodle-local_hub-comments', 'M.local_hub.init_comments',
               array(array('commentids' => '1 , 23 ,45')));

HTML

The plugin page should generate this HTML code:

<img src='http://moodle.org/theme/moodle2/pix/moodle-logo.gif' alt='the image to click on'/>
Comment
this is a comment
this is another comment

CSS

The plugin style.css should contain: /* Hide overlay markup while loading, if js is enabled */ .yui3-js-enabled .yui3-overlay-loading {

   top:-1000em;
   left:-1000em;
   position:absolute;

}

/* Overlay Look/Feel */ .yui3-overlay-content {

   padding:3px;
   border:1px solid #000;
   background-color:#aaa;

}

.yui3-overlay-content .yui3-widget-hd {

   padding:5px;
   border:2px solid #aa0000;
   background-color:#fff;

}

.yui3-overlay-content .yui3-widget-bd {

   padding:5px;
   border:2px solid #0000aa;
   background-color:#fff;

}

JS

In /local/hub/yui/comments/comments.js YUI.add('moodle-local_hub-comments', function(Y) {

   var COMMENTSNAME = 'hub_comments';
   //we declare the overlay here so it's accessible to all the extended class function'
   var overlay = new Y.Overlay({
               srcNode:"#commentoverlay", //the main div with id = commentoverlay
               width:"10em",
               height:"10em",
               xy:[ 0, 0],
               visible: false //by default it is not displayed
           });
   var COMMENTS = function() {
       COMMENTS.superclass.constructor.apply(this, arguments);
   }
   Y.extend(COMMENTS, Y.Base, {
       event:null,
       initializer : function(params) {
           //Example how to retrieve the parameter,
           //see what happens if you don't pass the parameter in the call
           alert(this.get('commentids')); // You usually use an attribute because
                                          //a attribut can have default value.
                                          //use the ATTRS default value if not passed as parameter
           alert(params.commentids); // undefined if not passed as parameter
           //render the overlay
           overlay.render();
           // position the overlay in the middle of the web browser window
           var WidgetPositionAlign = Y.WidgetPositionAlign;
           overlay.set("align", {
               node:"", //empty => viewport
               points:[WidgetPositionAlign.CC, WidgetPositionAlign.CC]
           });
           //attach a show event on the div with id = comments
           //we also change the div style to display the click zone
           Y.one('#comments').setStyle("border", "1px solid red").on('click', this.show, this);
           //uncomment the try content to see how works exception in Moodle
           //this exception has been specially created for Moodle
           try {
             //surely_not_existing_js_function();
           } catch (exception) {
               new M.core.exception(exception);
           }
       },
       show : function (e) {
           overlay.show(); //show the overlay
          
           e.halt(); // we are going to attach a new 'hide overlay' event to the body,
                     // because javascript always propagate event to parent tag,
                     // we need to tell Yahoo to stop to call the event on parent tag
                     // otherwise the hide event will be call right away.
           //we add a new event on the body in order to hide the overlay for the next click
           this.event = Y.one(document.body).on('click',this.hide,this);
       },
       hide : function () {
           overlay.hide(); //hide the overlay
           this.event.detach(); //we need to detach the hide event
                                //Note: it would work without but create js warning everytime
                                //we click on the body
       }
   }, {
       NAME : COMMENTSNAME,
       ATTRS : {
           commentids: {value : 450} //default value (has no purpose in this code except to show you
                                     // how to pass/use a param value)
       }
   });
   M.local_hub = M.local_hub || {};
   M.local_hub.init_comments = function(params) {
       return new COMMENTS(params);
   }

}, '@VERSION@', {

   requires:['base','overlay', 'moodle-enrol-notification']
   //Note: 'moodle-enrol-notification' contains Moodle YUI exception

});

Where are my methods in the IDE?

Some (all?) IDEs will fail to parse the above structure correctly and will not show you your methods in the structure/navigator pane. If this happens, use JSDoc format to mark the Y.extend() line as being a particular class: /**

* @class M.local_hub.init_comments
*/

Y.extend(COMMENTS, Y.Base, { This depends to a certain extent on how you choose to instantiate your objects. The above init_widget() method can be replaced with a more class based method like a lot of the YUI library uses.

What else to read

Two pages are important to start with when you discover YUI 3

  • you should read the base YUI doc. It is very useful to understand the YUI 3 Moodle module.
  • you want to have a look to the YUI 3 examples. Always open the examples in a window to have a look to the complete example codes.

See Also