summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorctrlaltca@gmail.com <>2011-07-16 09:28:55 +0000
committerctrlaltca@gmail.com <>2011-07-16 09:28:55 +0000
commitfedddfd60edb9cfe5bb5a90745ad7d8b963661ac (patch)
tree184efa20737fe468b64c77356612488c6ccab2d7
parent07a205ffd62910c79e09c5fd5247868c4b163ec5 (diff)
applied prado-lazyload-in-callbacks-patch (ticket #348)
-rw-r--r--framework/Web/Javascripts/source/prado/activecontrols/ajax3.js433
-rw-r--r--framework/Web/TAssetManager.php2
-rw-r--r--framework/Web/UI/ActiveControls/TActivePageAdapter.php32
-rw-r--r--framework/Web/UI/TClientScriptManager.php65
-rw-r--r--framework/Web/UI/WebControls/THtmlArea.php4
5 files changed, 517 insertions, 19 deletions
diff --git a/framework/Web/Javascripts/source/prado/activecontrols/ajax3.js b/framework/Web/Javascripts/source/prado/activecontrols/ajax3.js
index d9b5bdfa..b0e4e31e 100644
--- a/framework/Web/Javascripts/source/prado/activecontrols/ajax3.js
+++ b/framework/Web/Javascripts/source/prado/activecontrols/ajax3.js
@@ -26,32 +26,53 @@ Object.extend(Prado.AjaxRequest.prototype,
if (event == 'Complete')
{
- var redirectUrl = this.getBodyContentPart(Prado.CallbackRequest.REDIRECT_HEADER);
- if(redirectUrl)
+ var redirectUrl = this.getBodyContentPart(Prado.CallbackRequest.REDIRECT_HEADER);
+ if (redirectUrl)
document.location.href = redirectUrl;
- if ((this.getHeader('Content-type') || '').match(/^text\/javascript/i))
- {
- try
+ if ((this.getHeader('Content-type') || '').match(/^text\/javascript/i))
+ {
+ try
{
- json = eval('(' + transport.responseText + ')');
- }catch (e)
+ json = eval('(' + transport.responseText + ')');
+ }
+ catch (e)
{
if(typeof(json) == "string")
json = Prado.CallbackRequest.decode(result);
}
- }
+ }
- try
- {
- Prado.CallbackRequest.updatePageState(this,transport);
- Ajax.Responders.dispatch('on' + transport.status, this, transport, json);
- Prado.CallbackRequest.dispatchActions(transport,this.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER));
+ try
+ {
+ Prado.CallbackRequest.updatePageState(this,transport);
+ Prado.CallbackRequest.checkHiddenFields(this,transport);
+ var obj = this;
+ Prado.CallbackRequest.loadAssets(this,transport, function()
- (this.options['on' + this.transport.status]
- || this.options['on' + (this.success() ? 'Success' : 'Failure')]
- || Prototype.emptyFunction)(this, json);
- } catch (e) {
+ {
+ try
+ {
+ Ajax.Responders.dispatch('on' + transport.status, obj, transport, json);
+ Prado.CallbackRequest.dispatchActions(transport,obj.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER));
+
+ (
+ obj.options['on' + obj.transport.status]
+ ||
+ obj.options['on' + (obj.success() ? 'Success' : 'Failure')]
+ ||
+ Prototype.emptyFunction
+ ) (obj, json);
+ }
+ catch (e)
+ {
+ obj.dispatchException(e);
+ }
+ }
+ );
+ }
+ catch (e)
+ {
this.dispatchException(e);
}
}
@@ -150,6 +171,18 @@ Object.extend(Prado.CallbackRequest,
* Page state header name.
*/
PAGESTATE_HEADER : 'X-PRADO-PAGESTATE',
+ /**
+ * Script list header name.
+ */
+ SCRIPTLIST_HEADER : 'X-PRADO-SCRIPTLIST',
+ /**
+ * Stylesheet list header name.
+ */
+ STYLESHEETLIST_HEADER : 'X-PRADO-STYLESHEETLIST',
+ /**
+ * Hidden field list header name.
+ */
+ HIDDENFIELDLIST_HEADER : 'X-PRADO-HIDDENFIELDLIST',
REDIRECT_HEADER : 'X-PRADO-REDIRECT',
@@ -344,6 +377,181 @@ Object.extend(Prado.CallbackRequest,
//Logger.warn('current request ' + self.currentRequest.id);
},
+ /*
+ * Checks which scripts are used by the response and ensures they're loaded
+ */
+ loadScripts : function(request, transport, callback)
+ {
+ var self = Prado.CallbackRequest;
+ var data = request.getBodyContentPart(self.SCRIPTLIST_HEADER);
+ if (!this.ScriptsToLoad) this.ScriptsToLoad = new Array();
+ this.ScriptLoadFinishedCallback = callback;
+ if (typeof(data) == "string" && data.length > 0)
+ {
+ json = Prado.CallbackRequest.decode(data);
+ if(typeof(json) != "object")
+ Logger.warn("Invalid script list:"+data);
+ else
+ for(var key in json)
+ if (/^\d+$/.test(key))
+ {
+ var url = json[key];
+ if (!Prado.ScriptManager.isAssetLoaded(url))
+ this.ScriptsToLoad.push(url);
+ }
+ }
+ this.loadNextScript();
+ },
+
+ loadNextScript: function()
+ {
+ var done = (!this.ScriptsToLoad || (this.ScriptsToLoad.length==0));
+ if (!done)
+ {
+ var url = this.ScriptsToLoad.shift(); var obj = this;
+ if (
+ Prado.ScriptManager.ensureAssetIsLoaded(url,
+ function() {
+ obj.loadNextScript();
+ }
+ )
+ )
+ this.loadNextScript();
+ }
+ else
+ {
+ if (this.ScriptLoadFinishedCallback)
+ {
+ var cb = this.ScriptLoadFinishedCallback;
+ this.ScriptLoadFinishedCallback = null;
+ cb();
+ }
+ }
+ },
+
+ loadStyleSheetsAsync : function(request, transport)
+ {
+ var self = Prado.CallbackRequest;
+ var data = request.getBodyContentPart(self.STYLESHEETLIST_HEADER);
+ if (typeof(data) == "string" && data.length > 0)
+ {
+ json = Prado.CallbackRequest.decode(data);
+ if(typeof(json) != "object")
+ Logger.warn("Invalid stylesheet list:"+data);
+ else
+ for(var key in json)
+ if (/^\d+$/.test(key))
+ Prado.StyleSheetManager.ensureAssetIsLoaded(json[key],null);
+ }
+ },
+
+ loadStyleSheets : function(request, transport, callback)
+ {
+ var self = Prado.CallbackRequest;
+ var data = request.getBodyContentPart(self.STYLESHEETLIST_HEADER);
+ if (!this.StyleSheetsToLoad) this.StyleSheetsToLoad = new Array();
+ this.StyleSheetLoadFinishedCallback = callback;
+ if (typeof(data) == "string" && data.length > 0)
+ {
+ json = Prado.CallbackRequest.decode(data);
+ if(typeof(json) != "object")
+ Logger.warn("Invalid stylesheet list:"+data);
+ else
+ for(var key in json)
+ if (/^\d+$/.test(key))
+ {
+ var url = json[key];
+ if (!Prado.StyleSheetManager.isAssetLoaded(url))
+ this.StyleSheetsToLoad.push(url);
+ }
+ }
+ this.loadNextStyleSheet();
+ },
+
+ loadNextStyleSheet: function()
+ {
+ var done = (!this.StyleSheetsToLoad || (this.StyleSheetsToLoad.length==0));
+ if (!done)
+ {
+ var url = this.StyleSheetsToLoad.shift(); var obj = this;
+ if (
+ Prado.StyleSheetManager.ensureAssetIsLoaded(url,
+ function() {
+ obj.loadNextStyleSheet();
+ }
+ )
+ )
+ this.loadNextStyleSheet();
+ }
+ else
+ {
+ if (this.StyleSheetLoadFinishedCallback)
+ {
+ var cb = this.StyleSheetLoadFinishedCallback;
+ this.StyleSheetLoadFinishedCallback = null;
+ cb();
+ }
+ }
+ },
+
+ /*
+ * Checks which assets are used by the response and ensures they're loaded
+ */
+ loadAssets : function(request, transport, callback)
+ {
+ /*
+
+ ! This is the callback-based loader for stylesheets, which loads them one-by-one, and
+ ! waits for all of them to be loaded before loading scripts and processing the rest of
+ ! the callback.
+ !
+ ! That however is not neccessary, as stylesheets can be loaded asynchronously too.
+ !
+ ! I leave this code here for the case that this turns out to be a compatibility issue
+ ! (for ex. I can imagine some scripts trying to access stylesheet properties and such)
+ ! so if need can be reactivated. If you do so, comment out the async stylesheet loader below!
+
+ var obj = this;
+ this.loadStyleSheets(request,transport, function() {
+ obj.loadScripts(request,transport,callback);
+ });
+
+ */
+
+ this.loadStyleSheetsAsync(request,transport);
+
+ this.loadScripts(request,transport,callback);
+ },
+
+ checkHiddenField: function(name, value)
+ {
+ var id = name.replace(':','_');
+ if (!document.getElementById(id))
+ {
+ var field = document.createElement('input');
+ field.setAttribute('type','hidden');
+ field.id = id;
+ field.name = name;
+ field.value = value;
+ document.body.appendChild(field);
+ }
+ },
+
+ checkHiddenFields : function(request, transport)
+ {
+ var self = Prado.CallbackRequest;
+ var data = request.getBodyContentPart(self.HIDDENFIELDLIST_HEADER);
+ if (typeof(data) == "string" && data.length > 0)
+ {
+ json = Prado.CallbackRequest.decode(data);
+ if(typeof(json) != "object")
+ Logger.warn("Invalid hidden field list:"+data);
+ else
+ for(var key in json)
+ this.checkHiddenField(key,json[key]);
+ }
+ },
+
/**
* Updates the page state. It will update only if EnablePageStateUpdate and
* HasPriority options are both true.
@@ -722,3 +930,194 @@ Prado.Callback = function(UniqueID, parameter, onSuccess, options)
request.dispatch();
return false;
};
+
+
+
+/**
+ * Asset manager classes for lazy loading of scripts and stylesheets
+ * @author Gabor Berczi (gabor.berczi@devworx.hu)
+ */
+
+if (typeof(Prado.AssetManagerClass)=="undefined") {
+
+ Prado.AssetManagerClass = Class.create();
+ Prado.AssetManagerClass.prototype = {
+
+ initialize: function() {
+ this.loadedAssets = new Array();
+ this.discoverLoadedAssets();
+ },
+
+
+ /**
+ * Detect which assets are already loaded by page markup.
+ * This is done by looking up all <asset> elements and registering the values of their src attributes.
+ */
+ discoverLoadedAssets: function() {
+
+ // wait until document has finished loading to avoid javascript errors
+ if (!document.body) return;
+
+ var assets = this.findAssetUrlsInMarkup();
+ for(var i=0;i<assets.length;i++)
+ this.markAssetAsLoaded(assets[i]);
+ },
+
+ /**
+ * Extend url to a fully qualified url.
+ * @param string url
+ */
+ makeFullUrl: function(url) {
+
+ // this is not intended to be a fully blown url "canonicalizator",
+ // just to handle the most common and basic asset paths used by Prado
+
+ if (!this.baseUri) this.baseUri = window.location;
+
+ if (url.indexOf('://')==-1)
+ {
+ var a = document.createElement('a');
+ a.href = url;
+
+ if (a.href.indexOf('://')!=-1)
+ url = a.href;
+ else
+ {
+ var path = a.pathname;
+ if (path.substr(0,1)!='/') path = '/'+path;
+ url = this.baseUri.protocol+'//'+this.baseUri.host+path;
+ }
+ }
+ return url;
+ },
+
+ isAssetLoaded: function(url) {
+ url = this.makeFullUrl(url);
+ return (this.loadedAssets.indexOf(url)!=-1);
+ },
+
+ /**
+ * Mark asset as being already loaded
+ * @param string url of the asset
+ */
+ markAssetAsLoaded: function(url) {
+ url = this.makeFullUrl(url);
+ if (this.loadedAssets.indexOf(url)==-1)
+ this.loadedAssets.push(url);
+ },
+
+ createAssetReadyStateChangeFunction: function(url, element, callback, finalevent) {
+ return function() {
+ if (finalevent || (element.readyState == 'loaded') || (element.readyState == 'complete'))
+ callback(url,element);
+ };
+ },
+
+ /**
+ * Load a new asset dynamically into the page.
+ * Please not thet loading is asynchronous and therefore you can't assume that
+ * the asset is loaded and ready when returning from this function.
+ * @param string url of the asset to load
+ * @param callback will be called when the asset has loaded (or failed to load)
+ */
+ startAssetLoad: function(url, callback) {
+
+ // create new <asset> element in page header
+ var asset = this.createAssetElement(url);
+
+ if (callback)
+ {
+ asset.onreadystatechange = this.createAssetReadyStateChangeFunction(url, asset, callback, false);
+ asset.onload = this.createAssetReadyStateChangeFunction(url, asset, callback, true);
+ }
+
+ var head = document.getElementsByTagName('head')[0];
+ head.appendChild(asset);
+
+ // mark this asset as loaded
+ this.markAssetAsLoaded(url);
+
+ return (callback!=false);
+ },
+
+ /**
+ * Check whether a asset is loaded into the page, and if itsn't, load it now
+ * @param string url of the asset to check/load
+ * @return boolean returns true if asset is already loaded, or false, if loading has just started. callback will be called when loading has finished.
+ */
+ ensureAssetIsLoaded: function(url, callback) {
+ url = this.makeFullUrl(url);
+ if (this.loadedAssets.indexOf(url)==-1)
+ {
+ this.startAssetLoad(url,callback);
+ return false;
+ }
+ else
+ return true;
+ }
+
+ }
+
+};
+
+ Prado.ScriptManagerClass = Class.extend(Prado.AssetManagerClass, {
+
+ findAssetUrlsInMarkup: function() {
+ var urls = new Array();
+ var scripts = document.getElementsByTagName('script');
+ for(var i=0;i<scripts.length;i++)
+ {
+ var e = scripts[i]; var src = e.src;
+ if (src!="")
+ urls.push(src);
+ }
+ return urls;
+ },
+
+ createAssetElement: function(url) {
+ var asset = document.createElement('script');
+ asset.type = 'text/javascript';
+ asset.src = url;
+// asset.async = false; // HTML5 only
+ return asset;
+ }
+
+ });
+
+ Prado.StyleSheetManagerClass = Class.extend(Prado.AssetManagerClass, {
+
+ findAssetUrlsInMarkup: function() {
+ var urls = new Array();
+ var scripts = document.getElementsByTagName('link');
+ for(var i=0;i<scripts.length;i++)
+ {
+ var e = scripts[i]; var href = e.href;
+ if ((e.rel=="stylesheet") && (href.length>0))
+ urls.push(href);
+ }
+ return urls;
+ },
+
+ createAssetElement: function(url) {
+ var asset = document.createElement('link');
+ asset.rel = 'stylesheet';
+ asset.media = 'screen';
+ asset.setAttribute('type', 'text/css');
+ asset.href = url;
+// asset.async = false; // HTML5 only
+ return asset;
+ }
+
+ });
+
+ if (typeof(Prado.ScriptManager)=="undefined") Prado.ScriptManager = new Prado.ScriptManagerClass();
+ if (typeof(Prado.StyleSheetManager)=="undefined") Prado.StyleSheetManager = new Prado.StyleSheetManagerClass();
+
+ // make sure we scan for loaded scripts again when the page has been loaded
+ var discover = function() {
+ Prado.ScriptManager.discoverLoadedAssets();
+ Prado.StyleSheetManager.discoverLoadedAssets();
+ }
+ if (window.attachEvent) window.attachEvent('onload', discover);
+ else if (window.addEventListener) window.addEventListener('load', discover, false);
+
diff --git a/framework/Web/TAssetManager.php b/framework/Web/TAssetManager.php
index 61634910..7f3d4986 100644
--- a/framework/Web/TAssetManager.php
+++ b/framework/Web/TAssetManager.php
@@ -182,7 +182,7 @@ class TAssetManager extends TModule
* @return array List of published assets
* @since 3.1.6
*/
- protected function getPublished()
+ public function getPublished()
{
return $this->_published;
}
diff --git a/framework/Web/UI/ActiveControls/TActivePageAdapter.php b/framework/Web/UI/ActiveControls/TActivePageAdapter.php
index f8abd3ed..c5008dfe 100644
--- a/framework/Web/UI/ActiveControls/TActivePageAdapter.php
+++ b/framework/Web/UI/ActiveControls/TActivePageAdapter.php
@@ -3,6 +3,7 @@
* TActivePageAdapter, TCallbackErrorHandler and TInvalidCallbackException class file.
*
* @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @author Gabor Berczi <gabor.berczi@devworx.hu> (lazyload additions & progressive rendering)
* @link http://www.pradosoft.com/
* @copyright Copyright &copy; 2005-2011 PradoSoft
* @license http://www.pradosoft.com/license/
@@ -23,6 +24,7 @@ Prado::using('System.Web.UI.ActiveControls.TCallbackEventParameter');
* Callback request handler.
*
* @author Wei Zhuo <weizhuo[at]gamil[dot]com>
+ * @author Gabor Berczi <gabor.berczi@devworx.hu> (lazyload additions & progressive rendering)
* @version $Id$
* @package System.Web.UI.ActiveControls
* @since 3.1
@@ -45,6 +47,18 @@ class TActivePageAdapter extends TControlAdapter
* Callback page state header name.
*/
const CALLBACK_PAGESTATE_HEADER = 'X-PRADO-PAGESTATE';
+ /**
+ * Script list header name.
+ */
+ const CALLBACK_SCRIPTLIST_HEADER = 'X-PRADO-SCRIPTLIST';
+ /**
+ * Stylesheet list header name.
+ */
+ const CALLBACK_STYLESHEETLIST_HEADER = 'X-PRADO-STYLESHEETLIST';
+ /**
+ * Hidden field list header name.
+ */
+ const CALLBACK_HIDDENFIELDLIST_HEADER = 'X-PRADO-HIDDENFIELDLIST';
/**
* Callback redirect url header name.
@@ -189,6 +203,24 @@ class TActivePageAdapter extends TControlAdapter
$actions = TJavaScript::jsonEncode($executeJavascript);
$this->appendContentPart($response, self::CALLBACK_ACTION_HEADER, $actions);
//$response->appendHeader(self::CALLBACK_ACTION_HEADER.': '.$actions);
+
+
+ $cs = $this->Page->getClientScript();
+
+ // collect all stylesheet file references
+ $stylesheets = $cs->getStyleSheetUrls();
+ if (count($stylesheets)>0)
+ $this->appendContentPart($response, self::CALLBACK_STYLESHEETLIST_HEADER, TJavaScript::jsonEncode($stylesheets));
+
+ // collect all script file references
+ $scripts = $cs->getScriptUrls();
+ if (count($scripts)>0)
+ $this->appendContentPart($response, self::CALLBACK_SCRIPTLIST_HEADER, TJavaScript::jsonEncode($scripts));
+
+ // collect all hidden field references
+ $fields = $cs->getHiddenFields();
+ if (count($fields)>0)
+ $this->appendContentPart($response, self::CALLBACK_HIDDENFIELDLIST_HEADER, TJavaScript::jsonEncode($fields));
}
/**
diff --git a/framework/Web/UI/TClientScriptManager.php b/framework/Web/UI/TClientScriptManager.php
index eec347eb..677daa9b 100644
--- a/framework/Web/UI/TClientScriptManager.php
+++ b/framework/Web/UI/TClientScriptManager.php
@@ -3,6 +3,7 @@
* TClientScriptManager and TClientSideOptions class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
+ * @author Gabor Berczi <gabor.berczi@devworx.hu> (lazyload additions & progressive rendering)
* @link http://www.pradosoft.com/
* @copyright Copyright &copy; 2005-2011 PradoSoft
* @license http://www.pradosoft.com/license/
@@ -16,6 +17,7 @@
* TClientScriptManager manages javascript and CSS stylesheets for a page.
*
* @author Qiang Xue <qiang.xue@gmail.com>
+ * @author Gabor Berczi <gabor.berczi@devworx.hu> (lazyload additions & progressive rendering)
* @version $Id$
* @package System.Web.UI
* @since 3.0
@@ -107,6 +109,16 @@ class TClientScriptManager extends TApplicationComponent
|| count($this->_headScriptFiles) || count($this->_headScripts);
}
+ public static function getPradoPackages()
+ {
+ return self::$_pradoPackages;
+ }
+
+ public static function getPradoScripts()
+ {
+ return self::$_pradoScripts;
+ }
+
/**
* Registers Prado javascript by library name. See "Web/Javascripts/source/packages.php"
* for library names.
@@ -192,6 +204,37 @@ class TClientScriptManager extends TApplicationComponent
}
/**
+ * Returns the URLs of all script files referenced on the page
+ * @return array Combined list of all script urls used in the page
+ */
+ public function getScriptUrls()
+ {
+ $scripts = array();
+
+ $packages=array_keys($this->_registeredPradoScripts);
+ $base = Prado::getFrameworkPath().DIRECTORY_SEPARATOR.self::SCRIPT_PATH;
+ list($path,$baseUrl)=$this->getPackagePathUrl($base);
+ foreach ($packages as $p)
+ {
+ foreach (self::$_pradoScripts[$p] as $dep)
+ {
+ foreach (self::$_pradoPackages[$dep] as $script)
+ {
+ if (!in_array($url=$baseUrl.'/'.$script,$scripts))
+ $scripts[]=$url;
+ }
+ }
+ }
+
+ $scripts = array_merge($scripts, array_values($this->_headScriptFiles));
+ $scripts = array_merge($scripts, array_values($this->_scriptFiles));
+
+ $scripts = array_unique($scripts);
+
+ return $scripts;
+ }
+
+ /**
* Publishes a javascript library path and register packages to be loaded.
* See TClientScriptLoader for component that enables users to register custom javascript libraries.
* @param string javascript library base path
@@ -424,6 +467,23 @@ class TClientScriptManager extends TApplicationComponent
}
/**
+ * Returns the URLs of all stylesheet files referenced on the page
+ * @return array Combined list of all stylesheet urls used in the page
+ */
+ public function getStyleSheetUrls()
+ {
+ $stylesheets = array_values($this->_styleSheets);
+
+ foreach(Prado::getApplication()->getAssetManager()->getPublished() as $path=>$url)
+ if (substr($url,strlen($url)-4)=='.css')
+ $stylesheets[] = $url;
+
+ $stylesheets = array_unique($stylesheets);
+
+ return $stylesheets;
+ }
+
+ /**
* Registers a javascript file in the page head
* @param string a unique key identifying the file
* @param string URL to the javascript file
@@ -714,6 +774,11 @@ class TClientScriptManager extends TApplicationComponent
$writer->write("<div style=\"visibility:hidden;\">\n".$str."</div>\n");
}
+ public function getHiddenFields()
+ {
+ return $this->_hiddenFields;
+ }
+
/**
* Checks whether page rendering has not begun yet
*/
diff --git a/framework/Web/UI/WebControls/THtmlArea.php b/framework/Web/UI/WebControls/THtmlArea.php
index 2411f8f3..383411c5 100644
--- a/framework/Web/UI/WebControls/THtmlArea.php
+++ b/framework/Web/UI/WebControls/THtmlArea.php
@@ -402,6 +402,8 @@ class THtmlArea extends TTextBox
$options['debug'] = false;
$js = TJavaScript::encode($options,true,true);
$script = "if(typeof(tinyMCE_GZ)!='undefined'){ tinyMCE_GZ.init({$js}); }";
+ if ($this->getPage()->getIsCallback())
+ $script.= 'tinymce.dom.Event._pageInit();';
$scripts->registerEndScript($key, $script);
}
}
@@ -420,7 +422,7 @@ class THtmlArea extends TTextBox
{
$scripts = $this->getPage()->getClientScript();
$options = TJavaScript::encode($this->getEditorOptions(),true,true); // Force encoding of empty strings
- $script = "if(typeof(tinyMCE)!='undefined'){ tinyMCE.init($options); }";
+ $script = "if(typeof(tinyMCE)!='undefined')\r\n{ tinyMCE.init($options); }";
$scripts->registerEndScript('prado:THtmlArea'.$this->ClientID,$script);
}