Proposal for AMD support in Elgg 1.9

Hey, folks,

Elgg's javascript has historically been a messy jumble of spaghetti. It was only in 1.8 that we introduced some semblance of standards and organization, and we'd like to continue that trend in 1.9 by supporting the AMD format for javascript modules, and using requirejs as our script loader. However, my sense is that tolerance for backwards incompatibility in the Elgg community is at an all-time low, so we'll want to come up with a solution that minimizes breakage but also lets people start coding in AMD format today, taking advantage of the organization and performance benefits. For those who aren't familiar with AMD, he's some background: http://requirejs.org/docs/whyamd.html.
 
For starters, I think specifying "location" and "priority" for our JS is aggravating and tedious, and the problem will only get worse as devs continue to do more and more complex things in js with Elgg. Devs should just be specifying the dependencies and let Elgg take care of the rest:
 
```
elgg_register_js('jquery/ui', array(
    'src' => '//ajax.googlecdn.com/jqueryui.js',
    'deps' => array('jquery'),
));
```
 
But of course this is still a bit of a hassle, since the dependency declaration is in some location very distant from the module/script itself. Fortunately, AMD solves that problem, since AMD scripts already define their dependencies as part of the format:
 
```
define(function(require) {
    var $ = require('jquery');
    
    // module definition...
});
```
 
For the sake of backwards compatibility, any registered javascript that doesn't explicitly specify the dependencies should be assumed to depend on at least jquery, jquery-ui, and elgg, since those are the three libraries that are loaded by default on every page right now. So these two registration methods would be identical:
 
```
elgg_register_js('elgg.foobar', elgg_get_simplecache_url('js', 'elgg/foobar'));
 
elgg_register_js('elgg.foobar', array(
  'src' => elgg_get_simplecache_url('js', 'elgg/foobar'),
  'deps' => array('jquery', 'jquery-ui', 'elgg'),
  'exports' => 'elgg.foobar',
));
```
 
From that point, there are two directions we can go: 
  1. Optimistic: load everything asynchronously with requirejs and trust that the configured deps will ensure minimal breakage
  2. Conservative: continue loading scripts synchronously unless we know they are AMD, in which case we load them async
If we can do #2 in core but make #1 easily implemented with a plugin, that seems ideal for now. I want to remain ultra-sensitive to backwards compatibility concerns for 1.9, since 1.8 was so painful for people. That said, loading all external scripts asynchronously makes for absolutely blazing fast initial load time.
 
Any thoughts, questions, concerns?
  • would there be any language constructs (as per: elgg style coding thus far) that can be analyzed, parsed, identified to enable better search & identify for coercing forward-compatibility for more legacy code ? if you can identify such language constructs as used in elgg and (good|bad) plugins - i can build the productions for mini-grammars to build a auto-converter | 'standards forcer' on the js code & calls. btw-- amd does have pros *and cons ;)

  • Evan, thanks for posting this! I think your idea is great. We were searching for a similar solution. We started using it today in one of our projects, I can give you some more feedback probably in one week.

  • Any news on atlast a beta release of Elgg 1.9? I think something good is coming and if that's not to far away would be awesome to build my site straight with the new version.

  • I am for #2.  You should never be optimistic expecting everything will work when you are deploying software to tons of different servers. That only leads to trouble. You can be conservatively optimistic, which is how I would describe #2. 

  • I'm not an expert on client-side scripting but based on this and a couple of articles on AMD and require.js, I think this seems like a good idea.

  • Evan, thanks for sharing this.  Regarding the two suggested directions:

    #1. Optimistic: load everything asynchronously with requirejs and trust that the configured deps will ensure minimal breakage

    I think this should work fine for new Elgg Web apps when developers define the dependencies. For older plugins there will be surely breakages! They don't necessarily depend only on jquery-ui and elgg. Definitely, I agree on putting this in a plugin that can be activated when developers follow good AMD practices in order to gain best client-side javascript loading performance (= best scenario).

    #2. Conservative: continue loading scripts synchronously unless we know they are AMD, in which case we load them async

    Yes, this is a realistic approach for backwards compatibility.


    Now, as you said, the proposed solution is to implement #2 in core and to provide #1 as a plugin for modular javascript and speed (AMD best practices).  This sounds like a great plan!

    However, I have some thoughts here.

    Regarding #1:
    When following AMD pattern, new Elgg Web apps can benefit form better organizing javascript code (e.g. like server-side node.js) and gain performance through loading js files asynchronously. Still there is that issue with reducing HTTP requests. As Elgg jquery/javascript modules will be defined in many separate files, it might be lots of http requests which can lead to overload on servers and low performance. This is where requirejs optimizer should be used, and I think 2 things should be done:

    - Elgg should automatically include these files in requirejs optimizer.

    - requirejs optimizer must be used via a function call (exposed in node module) instead of a command line tool.


    Regarding #2:
    I somehow doubt about gaining much more performance with this approach as javascript will be loaded synchronously and asynchronously on the same page, i.e. there will be browser blocking parts anyway. But these figures should be first measured and tested if it is worth the efforts.


    Additional thought
    Why not ELgg would only provide #1 as a plugin? I am not a big fan of semi-AMD solutions :-)


    Concern
    Why not better put these efforts in HTML5/CSS3? 

    One thing is sure, if Elgg does't catch the HTML5/CSS3 train soon, we will, all Elgg lovers, end up regretting it.

    To my opinion, Elgg should have a new separate version built on top of the same Elgg core (further maintained for 1.9 and some later versions for backwards compatibility), but built with HTML5 and CSS3 for developing a new UI that should be responsive regarding fluid layouts and images.

    With Elgg current software stack, we can't develop a single web for different devices. Social networking is not about the desktop anymore! We should now develop first for mobiles, tablets and then desktop!

    I believe that more developers will prefer to develop with this "responsive Elgg" version because

    1. HTML5 is powerful, cleaner and faster.
    2. CSS3 can do great things regarding capabilities and performance (media queries, background images, gradients, web fonts etc.)
    3. Businesses and developers can't ignore the huge number of mobile traffic and users anymore! 

    Later, 1.9, 2.x versions can be dropped in favor of this responsive Elgg version.

  • Thanks for the feedback everyone.

    @cartonic, Elgg 1.9 is a little ways out yet. We will do what we can to make sure the upgrade is not painful. Building a site with 1.8 right now is a great choice.

    @SocialPlus+, we're switching to HTML5 for 1.9. HTML5-related pull requests are being accepted for 1.9.

    I've asked gillie to start a discussion about Elgg responsiveness. His crew has done great work in that space + with mobile. We could benefit a lot from integrating their work.

    I'm going to get a production website running AMD stuff so it can get some real world exposure and then I'll hopefully carve out some time to report back my findings.

  • @evan regarding the Elgg responsiveness and others : Whats the idea of bringing in bootstrap into Elgg?

    Regarding a/synchronous loading of javascripts, how will it affect when Elgg pages are ajaxified? Recently we had some troubles while ajaxification of Elgg, because of the context based loading of js and css by elgg_load_js/css() for the friendpicker, lightbox etc.. 

  • @Evan Winslow:

    If y'all core guys really want this 'responsive' stuff techniques in the core badly enough -> make the accommodation easier => get rid of that clunky smarty 'ElggMenu' thingummyjigs & take 'menus' back to being just simply 'views' within proper contexts of MVC - then only will reponsiveness & theming & the general declaratiive-ness (html, css.. are after all markups and declarations (not language) -- become as it has always meant to be -> strictly within the paradigm of views.Can y'all handle this ? be doing yourselves a big favor if you can and o ;)

  • Can the dojo toolkit support AMD , Ajax and many other requirements?

Feedback and Planning

Feedback and Planning

Discussions about the past, present, and future of Elgg and this community site.