Javascript aaaargh

Javascript aaaargh

by Howard Miller -
Number of replies: 25
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

I don't use javascript in Moodle very often as it make me want to kill small animals, but once again I am totally confused...

Would someone kindly explain the difference between using $PAGE->requires->js() and $PAGE->requires->js_init_cal().

The latter appears to require the js to be much more structured (M.mod_something) whereas the former seems to be for much less stuctured code. I don't really understand why you would use one over the other. For example, the quiz module has a mixture of both.

This article - http://docs.moodle.org/dev/YUI - is very recent but misleading. I actually have no idea what it is talking about. The sort of YUI.add() or YUI().use() wrappers it talks about are not used anywhere in Moodle outside of the library APIs (granted at a quick glance).

EDIT:Here's another one - $PAGE->requires->yui_module - not used much but does turn up in Moodle 2.4

I seem to remember that this page - http://docs.moodle.org/dev/JavaScript_guidelines - used to be the one that explained how to use js_init_call() but is now much longer and explains using the yui_module / YUI.add() stuff - which isn't actually used anywhere. Huh?

Average of ratings: -
In reply to Howard Miller

Re: Javascript aaaargh

by Davo Smith -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

$PAGE->requires->js - this just loads some javascript from the location, but does no initialisation, so either the javacript code has got to include some inline initialisation code (i.e. outside of functions), OR you are going to have to add some function calls into your page (e.g. 'onclick' parameters on icons or a <script> tag somewhere in your page), OR you are just loading some library code that will be used by the code later initialised by js_init_call.

$PAGE->requires->js_init_call - this loads the javascript file, calls the specified function (passing in the specified parameters), optionally loads some YUI modules and optionally includes some Moodle language strings

$PAGE->requires->yui_module - this can be used if you have arranged your javascript code into the layout that is supported by YUI modules and, hence, can load it as a YUI module.

The first is used if you want to load a non-YUI library (e.g. raphael, or jquery pre-Moodle 2.5) or if you are using badly designed javascript with the function calls inlined in your page.

The second is what I use almost all the time, as it is a clean way to load and initialise your javascript (including passing relevant parameters into it).

The third is very new and something I still need to learn how to do, so I can't really comment too much on it (but it seems like a neat idea).

Average of ratings: Useful (1)
In reply to Davo Smith

Re: Javascript aaaargh

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Thanks - appreciated.

I think part of my confusion is that someone (mentioning no names to protect the guilty) has removed the documentation for js_init_call() from the docs and replaced it with the yui_module documentation (this page http://docs.moodle.org/dev/JavaScript_guidelines, got to go back a bit to get the 'old' docs http://docs.moodle.org/dev/index.php?title=JavaScript_guidelines&oldid=38075)

It's hard to tell if this was intentional or otherwise. As you say, the third is new and unused. I'm not even sure I fully understand what a 'YUI Module' actually is.

Anyway, as a more general point we should be wary of removing documentation for features that are heavility used even if they are no longer the "flavour of the month".

This leaves me wondering (it doesn't say) if js_init_call() is about to be deprecated in favour of yui_module()? Dunno!

In reply to Howard Miller

Re: Javascript aaaargh

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi Howard,

I am the guilty one, and this is partially deliberate. I'm trying to improve the general JS documentation for Moodle and reduce the number of options to use JavaScript in Moodle - this will hopefully simplify things in the long run.

In my opinion, a YUI module is far preferable over js_init_call() and js() for several reasons:

  1. YUI modules can define a set of dependencies in the JavaScript rather than PHP. JavaScript dependencies should remain in JS where the loader can handle things properly rather than separating things between disparate locations. This has other benefits when combined with the YUI loader;
  2. YUI modules are sandboxed and use sandboxed versions of their dependencies. This means that if you depend on moodle-core-notification; and some other third-party developer has also done so but they've changed their view of the module, you won't see their changes (this is a good thing). It means that your code is more guaranteed to work;
  3. From 2.5, we support shifted YUI modules - these give us linting, and also consistent and reliable minification of code;
  4. The move to Shifter means that we can more easily write JS unit tests (this is on the JS roadmap);
  5. A YUI module can have it's own CSS dependencies so you can more easily include CSS which is only used if your module is successfully loaded
  6. The old js_init_call has a confusing syntax (e.g. the third option should ideally always be false);
  7. The old js_init_call's fourth option to define an array of module information is less efficient WRT HTTP transactions than getting the YUI loader to do things;
  8. The old js encourages bad habits, or at least makes it easier to make mistakes (like leaving global variables and functions hanging around)

My preference as JS maintainer is to move away from use of js_init_call. It's not easy to do as a big bang approach, and it will take a long time to remove entirely, but by removing the documentation and leaving documentation for a single way of doing things, I hope to encourage people to write YUI modules which benefit from all of the above.

I can look at adding back some documentation for js_init_call, but it would be new documentation, not a direct reinstatement of the old to try and make the reasoning for it's use clear. I'll try and do this in the next week.

Hope that this clears things up a little and thanks for raising this issue,

Andrew

Average of ratings: Useful (2)
In reply to Andrew Lyons

Re: Javascript aaaargh

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Thanks for the update. To be honest I don't understand a lot of this stuff (Shifted, linted et al) but I suppose that's ultimately my problem. I realise these calls have been around forever but, as far as I can tell, there is currently no use at all of YUI modules outside of lib. This makes it very difficult to find relevant examples. 

Anyway, I'll see if I can get my head around modules (which I don't understand at all at the moment). Are you confident that this isn't just increasing the learning curve for "casual" javascript users?  A glance at the docs implies this module stuff is significantly non-trivial. All I want to do is to check some radio boxes. I have neither the time or the motivation to become immersed in YUI. Maybe that's just me though.  There's always jQuery tongueout

In reply to Howard Miller

Re: Javascript aaaargh

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

No problem -hope that some of this helps smile

The linting tries to make sure that you're not doing anything really stupid - e.g. declaring things in the global scope, comparing strings with booleans in ways you *think* will work but may not (type conversion can be sucky in JS), converting strings to Ints without specifying a base (we have bugs with this), etc.

Shifting is just an optional function which makes gives many really really good benefits. It's quite easy to set up, and the learning curve to running the tool it pretty low once you understand the purpose.

As I mentioned before modules are sandboxed, which has various benefits - especially in an environment like Moodle. In reality, most JS frameworks already use these but not necessarily at the general usage level - any framework should (and probably is) using closures for it's Modules, and you're just invoking it's sandboxed/closured modules. Basically, you put your code within a function which means that it's scope is much more limited.

The only major difference that moving things to using a YUI module really makes is to move the JavaScript dependencies and setup to JavaScript, rather than placing them within PHP. With js_init_call, this was actually setting up the JS wrapper in lib/outputrequirements.php, which may be clearer to PHP developers doing some JS, but it isn't really the best thing to do. In the yui_module way, you write a self-contained piece of JS which is just called from the PHP. The biggest change is that: instead of placing your JS in a file called module.js, it's in a file within a series of directories: yui/src/<modulename>/js/<modulename>js (where <modulename> describes the task you're trying to achieve). Once you've set up your metadata which describes that module, things are very similar to jQuery.

This format has been used for quite some time outside of the lib directory - e.g. enrol/yui < 2.3; course/yui >= 2.3.

Andrew

 

Average of ratings: Useful (1)
In reply to Andrew Lyons

Re: Javascript aaaargh

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Ok - that's useful. To be honest, I looked in /mod and /blocks and found nothing. I'm off to try it for a module subplugin which should be as good a test as you can get big grin

I think it might be worth even one line in the docs saying "this is how we do it now" to reduce confusion. There's quite enough conflicting information about use of javascript in the dev docs as it is. 

In reply to Howard Miller

Re: Javascript aaaargh

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

So, how do you debug this stuff?

I had a go at creating some code in what I think is the right place and added the requires->yui_module() call. Nothing happens. The js doesn't turn up in the output and there are no errors generated. I have 'developer' debugging on.

Am I missing something?

In reply to Howard Miller

Re: Javascript aaaargh

by Joseph Rézeau -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers Picture of Translators

Howard "... how do you debug this stuff?"

That's the problem I've always had with Javascript, I find it quite difficult to debug, unlike PHP. Contrary opinions and debugging advice welcome.wink

Joseph

 

In reply to Joseph Rézeau

Re: Javascript aaaargh

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

I just added a few lines to the docs page to remind myself. Basically make sure that javascript caching and combo-loading are turned off (and debugging is on, obviously).

You are then away with your favorite debugger - e.g. firebug

The only thing left that doesn't seem to be handled is not getting the module loaded in the first place. I had made a typo as it happens but it seemed to fail 'silently'. More messing around required wink

However, all seems to be working as advertised. I just need to remember how to do anything at all in JS/YUI!

In reply to Howard Miller

Re: Javascript aaaargh

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Again, another thing on the JS roadmap in my head (and at http://docs.moodle.org/dev/User:Andrew_Nicols) is to correct a few long-standing bugs with the way in which we load YUI modules after you call yui_module. This should improve the alerting in the browser console to point you at things like typos more quickly smile

You didn't say which version of Moodle your'e developing for.

Andrew

In reply to Howard Miller

Re: Javascript aaaargh

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi Howard,

When working with JS in Moodle, I'd advise setting the following option in your config.php:

$CFG->jsrev = -1;

This will ensure that we don't send caching headers with the files, and that the non-minified versions are used which makes it easier to debug.

The yui_module() function doesn't include the file directly in the generated content, but instead adds a couple of lines to the generated content which inform the loader to load your JS file. The module name must match the frankenstyle name of the module you're developing for. So for example, if you're writing JS for a block called notes, and the JS is responsible for handling comments you'd use:

moodle-block_notes-comments as the block name

When you make the call to yui_module you'd use:

yui_module('moodle-block_notes-comments', 'M.block_notes.comments.init');

This will add some JS to the page:

Y.use('moodle-block_notes-comments', function() {

  M.block_notes.comments.init();

});

The Y.use is a way of calling the YUI loader which then goes and passes your module name to theme/yui_combo.php and returns your code.

If you open your developer tools (I highly recommend Chrome for JS dev), then you can use the Sources tab to find where your module has been loaded. The developer tools are very powerful and you can modify the JS in the browser to try things out, set breakpoints, generate stack traces, etc.

Andrew

Average of ratings: Useful (3)
In reply to Andrew Lyons

Re: Javascript aaaargh

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Cool - I did get it all to work (second go) but it just sulked when I had a typo in the frankenstyle path.

It worked with my module subplugin which is really cool (I expected trouble!).

I'll have a go with Chrome and see how I get on.

In reply to Howard Miller

Re: Javascript aaaargh

by Amanda Doughty -
Picture of Core developers Picture of Plugin developers

I feel your pain Howard. I've written and rewritten my first attempt at YUI modules. It's a steep learning curve compared to jQuery and there are far fewer examples on the net. But it's worth the effort I think, and Andrew is a brilliant maintainer. This kind of forum participation helps us all.

The debugging is something I have struggled with, so I'm pleased to see this being discussed and documented too.

Average of ratings: Useful (3)
In reply to Amanda Doughty

Re: Javascript aaaargh

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Yeh - the YUI documentation is deeply confusing.... set(), setData(), setAttribute() huh?

In reply to Howard Miller

Re: Javascript aaaargh

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

They're for setting different things:

  • set() - setting a parameter on the YUI object. These parameters only exist on the object.
  • setData() - this sets data attributes on a DOM node - e.g. the javascript attribute in <div data-javascript="handy">Some Content</div>. To set this you'd use mydiv.setData('javascript', 'handy'); This doesn't necessarily change the DOM itself as further calls should also use getData and setData.
  • setAttribute() - this sets other attributs on a DOM node. Technically you can use setAttribute to set data, but you shoudn't.

Take a look at http://yuilibrary.com/yui/docs/api/classes/Node.html for the full run down, but there are also various other functions like:

  • setHTML
  • setStyle
  • setStyles

Andrew

In reply to Andrew Lyons

Re: Javascript aaaargh

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Thanks - I'm getting there smile

Just an update. The docs worked as advertised. The YUI module loaded and worked and did what I required. Although, what I needed was very simple a few things occurred to me that I MIGHT have wanted to know...

- What can 'M' do for me? I noticed M.util is used as part of getting translated strings but I assume that there must be a range of methods attached to this? Is this documented (even in the code)?

- The sandbox thing... I'm not sure I really understand this so this might not be a sensible question... If I have multiple plugins adding YUI Modules to the page (e.g. a module and a related block) can they 'talk' to each other? Can data from one be, somehow, accessed in the other?

In reply to Howard Miller

Re: Javascript aaaargh

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi Howard,

I'll try and add those to the documentation too.

In essence, M is the namespace that was chosen for Moodle's JavaScript. At present, most of our JS lives on it, but not 

There's a lot on the M.util namespace, and most of it needs documenting. The important ones are:

  • M.util.get_string() - similar to get_string() in Moodle. You do need to export the strings from the PHP to JS still - See $PAGE->requires->string_for_js and strings_for_js
  • M.util.image_url() - Get the correct URL for an image using it's name and component

For the most part, the rest of the code in javascript-static probably isn't useful to you (it's things which should be modules).

If you have multiple plugins, you can get them to talk to one another. It depends on what you're doing, what you're trying to achieve, and what data it is that you wish to share. On the whole, you can use module dependencies to load additional modules into your sandbox and get them to work with one another. If you can provide more information on what you're trying to achieve, I may be able to give a more detailed answer.

Andrew

In reply to Andrew Lyons

Re: Javascript aaaargh

by Amanda Doughty -
Picture of Core developers Picture of Plugin developers

I've got a module in a block broadcasting a global event, that is used by our custom menu to update itself. So the block allows users to add courses to their 'favourites' area on the My Moodle page. The favourites are also in a drop down menu in the header. Changes are broadcast by the block module and caught by the custom menu module.

I've got everything working as I want it to but it would be really useful to have a peer review of the structure of my modules. If I document it and provide a link to an example page, would you be willing to check it out for me Andrew?

In reply to Andrew Lyons

Re: Javascript aaaargh

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Thanks Andrew... no, it was just a random thought that popped into my head. If I'm sandboxed how do I see 'other' stuff. Where 'other' is undefined wink

In reply to Howard Miller

Re: Javascript aaaargh

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Because you make it clear what 'other' stuff you need in your module's metadata, or via Y.use() calls. Then the YUI loader makes sure that other stuff is added to the Y object.

In reply to Howard Miller

Re: Javascript aaaargh

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

I've got to run off now, but just to say that the yui_module stuff is used quite a bit in Moodle. All of the new JS is using it and anything new should be using it...