silverstripe cms javascript refactoring
DESCRIPTION
Outlines the refactoring effort of the SilverStripe CMS codebase, which was an internal project at SilverStripe Ltd. mid 2009. The main objective was to simplify CMS extensions for intermediate frontend/backend developers. In practice this meant a lot of housekeeping, and the consistent usage of jQuery and jQuery UI over PrototypeJS.TRANSCRIPT
SILVERSTRIPE CMS JAVASCRIPT REFACTORING
The jsparty is over!
Friday, 28 August 2009
SCOPE
• JavaScript library upgrade, no more forks
• CMS JavaScript cleanupRewrite in jQuery where feasible
• CMS PHP Controller simplification
• More solid UI components with jQuery UI and plugins
• CSS Architecture for easier customization
• Not a large-scale CMS redesign
Friday, 28 August 2009
LIBRARIES
• Prototype 1.4rc3 to jQuery 1.3
• Random UI code and ancient libs to jQuery UI 1.7
• Custom behaviour.js to jQuery.concrete
• External libraries managed by Piston (piston.rubyforge.org/) instead of svn:externals
Friday, 28 August 2009
GOODBYE JSPARTY
• Merged external librariesto cms/thirdparty and sapphire/thirdparty
Friday, 28 August 2009
THE DARK AGES
function hover_over() { Element.addClassName(this, 'over');}function hover_out() { Element.removeClassName(this, 'over');}
hover_behaviour = { onmouseover : hover_over, onmouseout : hover_out}jsparty/hover.js
Friday, 28 August 2009
THE DARK AGESclass LeftAndMain {
// ... public function returnItemToUser($p) { // ... $response = <<<JS var tree = $('sitetree'); var newNode = tree.createTreeNode("$id", "$treeTitle", "{$p->class}{$hasChildren} {$singleInstanceCSSClass}"); node = tree.getTreeNodeByIdx($parentID); if(!node) { node = tree.getTreeNodeByIdx(0); } node.open(); node.appendTreeNode(newNode); newNode.selectTreeNode(); JS; FormResponse::add($response); FormResponse::add($this->hideSingleInstanceOnlyFromCreateFieldJS($p)); return FormResponse::respond(); }}
cms/code/LeftAndMain.php (excerpt)
Friday, 28 August 2009
BEST PRACTICES
• Don’t claim global properties
• Assume element collections
• Encapsulate: jQuery.concrete, jQuery plugin, jQueryUI widget (in this order)
Friday, 28 August 2009
ENCAPSULATE: EXAMPLE
// create closure(function($) { // plugin definition $.fn.hilight = function(options) { // build main options before element iteration var opts = $.extend({}, $.fn.hilight.defaults, options); // iterate and reformat each matched element return this.each(function() { $this = $(this); // build element specific options var o = $.meta ? $.extend({}, opts, $this.data()) : opts; // update element styles $this.css({backgroundColor: o.background,color: o.foreground}); }); }; // plugin defaults $.fn.hilight.defaults = {foreground: "red",background: "yellow"};// end of closure})(jQuery);
Simple Highlight jQuery Plugin
Friday, 28 August 2009
BEST PRACTICES
• Use plain HTML, jQuery.data() and jQuery.metadata to encode initial state and (some) configuration
• Better than building “object cathedrals” in most cases
Friday, 28 August 2009
STATE: EXAMPLE
$('form :input').bind('change', function(e) { $(this.form).addClass('isChanged');});$('form').bind('submit', function(e) { if($(this).hasClass('isChanged')) return false;});
Simple Form Changetracking
$('form :input').bind('change', function(e) { $(this.form).data('isChanged', true);});$('form').bind('submit', function(e) { if($(this).data('isChanged')) return false;});
State in CSS properties
State in DOM(through jQuery.data())
Friday, 28 August 2009
BEST PRACTICES
• Ajax responses should default to HTMLMakes it easier to write unobtrusive markup and use SilverStripe templating
• Use HTTP metadata to transport state and additional dataExample: 404 HTTP status code to return “Not Found”Example: Custom “X-Status” header for more detailed UI status
• Return JSON if HTML is not feasibleExample: Update several tree nodes after CMS batch actionsAlternative: Inspect the returned DOM for more structured data like id attributes
• For AJAX and parameter transmission, <form> is your friend
Friday, 28 August 2009
AJAX: EXAMPLE
<form action"#"> <div class="autocomplete {url:'MyController/autocomplete'}"> <input type="text" name="title" /> <div class="results" style="display: none;"> </div> <input type="submit" value="action_autocomplete" /></form>
<ul><% control Results %> <li id="Result-$ID">$Title</li><% end_control %></ul>
MyController.ss
AutoComplete.ss
Simple Serverside Autocomplete for Page Titles
Using jQuery.metadata,but could be a plain SilverStripe Form as well
Friday, 28 August 2009
AJAX: EXAMPLE
class MyController { function autocomplete($request) { $SQL_title = Convert::raw2sql($request->getVar('title')); $results = DataObject::get("Page", "Title LIKE '%$SQL_title%'"); if(!$results) return new HTTPResponse("Not found", 404); // Use HTTPResponse to pass custom status messages $this->response->setStatusCode(200, "Found " . $results->Count() . " elements"); // render all results with a custom template $vd = new ViewableData(); return $vd->customise(array( "Results" => $results ))->renderWith('AutoComplete'); }}
MyController.php
Friday, 28 August 2009
AJAX: EXAMPLE
$('.autocomplete input').live('change', function() { var resultsEl = $(this).siblings('.results'); resultsEl.load( // get form action, using the jQuery.metadata plugin $(this).parent().metadata().url, // submit all form values $(this.form).serialize(), // callback after data is loaded function(data, status) { resultsEl.show(); // optional: get all record IDs from the new HTML var ids = jQuery('.results').find('li').map(function() { return $(this).attr('id').replace(/Record\-/,''); }); } );});
MyController.js
Friday, 28 August 2009
BEST PRACTICES
• Use events and observation for component communication
• Use composition for synchronous, two-way communicatoin
• Use callbacks to allow for customization
• Don’t overuse prototypical inheritance and pseudo-classes
• Only expose public APIs where necessary (through jQuery.concrete)
Friday, 28 August 2009
RESOURCES
http://doc.silverstripe.com/doku.php?id=2.4:javascript
http://github.com/chillu/sapphire/tree/jsrewrite
http://github.com/chillu/cms/tree/jsrewrite
Documentation (unreleased)
Source (unreleased, pre 2.4 alpha)
Friday, 28 August 2009