Permalink
Browse files

ENHANCEMENT Using native jQuery.getScript() in customized jQuery.onde…

…mand plugin. Moved processDemandHeaders() into jQuery namespace, and adjusted customized prototype.js library

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@92537 467b73ca-7a2a-4603-9d3b-597d59a354a9
  • Loading branch information...
1 parent b661b40 commit f60e94b1ca502bfde78db0d92823c8dbea8fd7a9 @chillu chillu committed Nov 21, 2009
Showing with 92 additions and 163 deletions.
  1. +90 −161 javascript/jquery-ondemand/jquery.ondemand.js
  2. +2 −2 thirdparty/prototype/prototype.js
@@ -1,34 +1,18 @@
/**
* On-demand JavaScript handler
* Based on http://plugins.jquery.com/files/issues/jquery.ondemand.js_.txt
- * and modified to integrate with SilverStripe and prototype.js.
+ * and heavily modified to integrate with SilverStripe and prototype.js.
* Adds capabilities for custom X-Include-CSS and X-Include-JS HTTP headers
* to request loading of externals alongside an ajax response.
*
- * Modified 2009-08-03 by Ingo Schommer, SilverStripe Ltd. - Renamed property "queue" to "ondemand_queue"
- * to make compatible with jQuery 1.3
+ * IMPORTANT: This plugin monkeypatches the jQuery.ajax() method.
*/
(function($){
- function isExternalScript(url){
- re = new RegExp('(http|https)://');
- return re.test(url);
- };
-
$.extend({
- requireConfig : {
- // empty default paths give more flexibility and user has
- routeJs : '',
- // choice of using this config or full path in scriptUrl argument
- // previously were useless for users which don't use '_js/' and '_css/' folders. (by PGA)
- routeCss : ''
- },
-
- ondemand_queue : [],
- pending : null,
// loaded files list - to protect against loading existed file again (by PGA)
- loaded_list : null,
+ _ondemand_loaded_list : null,
// Added by SRM: Initialise the loaded_list with the scripts included on first load
initialiseItemLoadedList : function() {
@@ -38,7 +22,7 @@
$('script').each(function() {
if($(this).attr('src')) $this.loaded_list[ $(this).attr('src') ] = 1;
});
- $('link[@rel="stylesheet"]').each(function() {
+ $('link[rel="stylesheet"]').each(function() {
if($(this).attr('href')) $this.loaded_list[ $(this).attr('href') ] = 1;
});
}
@@ -48,194 +32,139 @@
* Returns true if the given CSS or JS script has already been loaded
*/
isItemLoaded : function(scriptUrl) {
- this.initialiseItemLoadedList();
- return this.loaded_list[scriptUrl] != undefined;
- },
-
- requireJs : function(scriptUrl, callback, opts, obj, scope) {
-
- if(opts != undefined || opts == null){
- $.extend($.requireConfig, opts);
- }
-
- var _request = {
- url : scriptUrl,
- callback : callback,
- opts : opts,
- obj : obj,
- scope : scope
- }
-
- if(this.pending) {
- this.ondemand_queue.push(_request);
- return;
- }
-
- this.pending = _request;
+ var self = this;
- this.initialiseItemLoadedList();
-
- // if required file exists (by PGA)
- if (this.loaded_list[this.pending.url] != undefined) {
- // => request complete
- this.requestComplete();
- return;
- }
-
- var _this = this;
- var _url = (isExternalScript(scriptUrl)) ? scriptUrl : $.requireConfig.routeJs + scriptUrl;
- var _head = document.getElementsByTagName('head')[0];
- var _scriptTag = document.createElement('script');
-
- // Firefox, Opera
- $(_scriptTag).bind('load', function(){
- _this.requestComplete();
+ if(this._ondemand_loaded_list == null) {
+ this._ondemand_loaded_list = {};
+ $('script').each(function() {
+ if($(this).attr('src')) self._ondemand_loaded_list[ $(this).attr('src') ] = 1;
+ });
+ $('link[rel="stylesheet"]').each(function() {
+ if($(this).attr('href')) self._ondemand_loaded_list[ $(this).attr('href') ] = 1;
});
-
- // IE
- _scriptTag.onreadystatechange = function(){
- if(this.readyState === 'loaded' || this.readyState === 'complete'){
- _this.requestComplete();
- }
- }
-
- _scriptTag.type = "text/javascript";
- _scriptTag.src = _url;
-
- _head.appendChild(_scriptTag);
- },
-
- requestComplete : function() {
-
- if(this.pending.callback){
- if(this.pending.obj){
- if(this.pending.scope){
- this.pending.callback.call(this.pending.obj);
- } else {
- this.pending.callback.call(window, this.pending.obj);
- }
- } else {
- this.pending.callback.call();
- }
}
- // adding loaded file to loaded list (by PGA)
- this.loaded_list[this.pending.url] = 1;
- this.pending = null;
-
- if(this.ondemand_queue.length > 0) {
- var request = this.ondemand_queue.shift();
- this.requireJs(request.url, request.callback, request.opts, request.obj, request.scope);
- }
+ return (this._ondemand_loaded_list[scriptUrl] != undefined);
},
requireCss : function(styleUrl, media){
if(media == null) media = 'all';
// Don't double up on loading scripts
- if(this.isItemLoaded(styleUrl)) return;
+ if($.isItemLoaded(styleUrl)) return;
if(document.createStyleSheet){
- var ss = document.createStyleSheet($.requireConfig.routeCss + styleUrl);
+ var ss = document.createStyleSheet(styleUrl);
ss.media = media;
} else {
var styleTag = document.createElement('link');
$(styleTag).attr({
- href : $.requireConfig.routeCss + styleUrl,
+ href : styleUrl,
type : 'text/css',
media : media,
rel : 'stylesheet'
}).appendTo($('head').get(0));
}
- this.loaded_list[styleUrl] = 1;
+ this._ondemand_loaded_list[styleUrl] = 1;
+
+ },
+
+ /**
+ * Process the X-Include-CSS and X-Include-JS headers provided by the Requirements class
+ */
+ processOnDemandHeaders: function(xml, status, _ondemandComplete) {
+ var self = this;
+
+ // CSS
+ if(xml.getResponseHeader('X-Include-CSS')) {
+ var cssIncludes = xml.getResponseHeader('X-Include-CSS').split(',');
+ for(var i=0;i<cssIncludes.length;i++) {
+ // Syntax: "URL:##:media"
+ if(cssIncludes[i].match(/^(.*):##:(.*)$/)) {
+ $.requireCss(RegExp.$1, RegExp.$2);
+ // Syntax: "URL"
+ } else {
+ $.requireCss(cssIncludes[i]);
+ }
+ }
+ }
+ // JavaScript
+ var newJsIncludes = [];
+ if(xml.getResponseHeader('X-Include-JS')) {
+ var jsIncludes = xml.getResponseHeader('X-Include-JS').split(',');
+ for(var i=0;i<jsIncludes.length;i++) {
+ if(!$.isItemLoaded(jsIncludes[i])) {
+ newJsIncludes.push(jsIncludes[i]);
+ }
+ }
+ }
+
+ // We make an array of the includes that are actually new, and attach the callback to the last one
+ // They are placed in a queue and will be included in order. This means that the callback will
+ // be able to execute script in the new includes (such as a livequery update)
+ var getScriptQueue = function() {
+ if(newJsIncludes.length) {
+ var newJsInclude = newJsIncludes.shift();
+ // emulates getScript() with addtl. setting
+ $.ajax({
+ dataType: 'script',
+ url: newJsInclude,
+ success: function() {
+ self._ondemand_loaded_list[newJsInclude] = 1;
+ getScriptQueue();
+ },
+ cache: false,
+ // jQuery seems to override the XHR objects if used in async mode
+ async: false
+ });
+ } else {
+ _ondemandComplete(xml, status);
+ }
+ }
+
+ if(newJsIncludes.length) {
+ getScriptQueue();
+ } else {
+ // If there aren't any new includes, then we can just call the callbacks ourselves
+ _ondemandComplete(xml, status);
+ }
}
- })
+ });
/**
- * Sapphire extensions
* Ajax requests are amended to look for X-Include-JS and X-Include-CSS headers
*/
_originalAjax = $.ajax;
$.ajax = function(s) {
+ // Avoid recursion in ajax callbacks caused by getScript(), by not parsing
+ // ondemand headers for 'script' datatypes
+ if(s.dataType == 'script') return _originalAjax(s);
+
var _complete = s.complete;
var _success = s.success;
var _dataType = s.dataType;
// This replaces the usual ajax success & complete handlers. They are called after any on demand JS is loaded.
- var _ondemandComplete = function(xml) {
- var status = jQuery.httpSuccess(xml) ? 'success' : 'error';
+ var _ondemandComplete = function(xml, status) {
+ var status = $.httpSuccess(xml) ? 'success' : 'error';
if(status == 'success') {
- data = jQuery.httpData(xml, _dataType);
- if(_success) _success(data, status, xml);
+ var data = jQuery.httpData(xml, _dataType);
+ if(_success) _success(data, status);
}
if(_complete) _complete(xml, status);
}
// We remove the success handler and take care of calling it outselves within _ondemandComplete
s.success = null;
s.complete = function(xml, status) {
- processOnDemandHeaders(xml, _ondemandComplete);
+ $.processOnDemandHeaders(xml, status, _ondemandComplete);
}
return _originalAjax(s);
}
-})(jQuery);
-
-/**
- * This is the on-demand handler used by our patched version of prototype.
- * once we get rid of all uses of prototype, we can remove this
- */
-function prototypeOnDemandHandler(xml, callback) {
- processOnDemandHeaders(xml, callback);
-}
-
-
-/**
- * Process the X-Include-CSS and X-Include-JS headers provided by the Requirements class
- */
-function processOnDemandHeaders(xml, _ondemandComplete) {
- var i;
- // CSS
- if(xml.getResponseHeader('X-Include-CSS')) {
- var cssIncludes = xml.getResponseHeader('X-Include-CSS').split(',');
- for(i=0;i<cssIncludes.length;i++) {
- // Syntax 1: "URL:##:media"
- if(cssIncludes[i].match(/^(.*):##:(.*)$/)) {
- jQuery.requireCss(RegExp.$1, RegExp.$2);
-
- // Syntax 2: "URL"
- } else {
- jQuery.requireCss(cssIncludes[i]);
- }
- }
- }
-
- // JavaScript
- var newIncludes = [];
- if(xml.getResponseHeader('X-Include-JS')) {
- var jsIncludes = xml.getResponseHeader('X-Include-JS').split(',');
- for(i=0;i<jsIncludes.length;i++) {
- if(!jQuery.isItemLoaded(jsIncludes[i])) {
- newIncludes.push(jsIncludes[i]);
- }
- }
- }
-
- // We make an array of the includes that are actually new, and attach the callback to the last one
- // They are placed in a queue and will be included in order. This means that the callback will
- // be able to execute script in the new includes (such as a livequery update)
- if(newIncludes.length > 0) {
- for(i=0;i<jsIncludes.length;i++) {
- jQuery.requireJs(jsIncludes[i], (i == jsIncludes.length-1) ? function() { _ondemandComplete(xml); } : null);
- }
-
- // If there aren't any new includes, then we can just call the callbacks ourselves
- } else {
- _ondemandComplete(xml, status);
- }
-}
+})(jQuery);
@@ -852,8 +852,8 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
}
// jquery ondemand integration patch
- if(typeof prototypeOnDemandHandler != 'undefined') {
- prototypeOnDemandHandler(this.transport, completeHandler);
+ if(typeof jQuery != 'undefined' && typeof jQuery.processOnDemandHeaders != 'undefined') {
+ jQuery.processOnDemandHeaders(this.transport, prototypeAjax.transport.status, completeHandler);
} else {
completeHandler();
}

0 comments on commit f60e94b

Please sign in to comment.