487 lines
No EOL
22 KiB
HTML
487 lines
No EOL
22 KiB
HTML
<html><head><title>UpdateManager.js</title><link rel="stylesheet" type="text/css" href="../resources/style.css" media="screen"/></head><body><h1>UpdateManager.js</h1><pre class="highlighted"><code><i>/**
|
|
* @class Ext.UpdateManager
|
|
* @extends Ext.util.Observable
|
|
* Provides AJAX-style update <b>for</b> Element object.<br><br>
|
|
* Usage:<br>
|
|
* <pre><code>
|
|
* <i>// Get it from a Ext.Element object</i>
|
|
* <b>var</b> el = Ext.get("foo");
|
|
* <b>var</b> mgr = el.getUpdateManager();
|
|
* mgr.update("http:<i>//myserver.com/index.php", "param1=1&amp;param2=2");</i>
|
|
* ...
|
|
* mgr.formUpdate("myFormId", "http:<i>//myserver.com/index.php");</i>
|
|
* <br>
|
|
* <i>// or directly (returns the same UpdateManager instance)</i>
|
|
* <b>var</b> mgr = <b>new</b> Ext.UpdateManager("myElementId");
|
|
* mgr.startAutoRefresh(60, "http:<i>//myserver.com/index.php");</i>
|
|
* mgr.on("update", myFcnNeedsToKnow);
|
|
* <br>
|
|
<i>// short handed call directly from the element object</i>
|
|
Ext.get("foo").load({
|
|
url: "bar.php",
|
|
scripts:true,
|
|
params: "<b>for</b>=bar",
|
|
text: "Loading Foo..."
|
|
});
|
|
* </code></pre>
|
|
* @constructor
|
|
* Create <b>new</b> UpdateManager directly.
|
|
* @param {String/HTMLElement/Ext.Element} el The element to update
|
|
* @param {Boolean} forceNew (optional) By <b>default</b> the constructor checks to see <b>if</b> the passed element already has an UpdateManager and <b>if</b> it does it returns the same instance. This will skip that check (useful <b>for</b> extending <b>this</b> class).
|
|
*/</i>
|
|
Ext.UpdateManager = <b>function</b>(el, forceNew){
|
|
el = Ext.get(el);
|
|
<b>if</b>(!forceNew && el.updateManager){
|
|
<b>return</b> el.updateManager;
|
|
}
|
|
<i>/**
|
|
* The Element object
|
|
* @type Ext.Element
|
|
*/</i>
|
|
<b>this</b>.el = el;
|
|
<i>/**
|
|
* Cached url to use <b>for</b> refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
|
|
* @type String
|
|
*/</i>
|
|
<b>this</b>.defaultUrl = null;
|
|
|
|
<b>this</b>.addEvents({
|
|
<i>/**
|
|
* @event beforeupdate
|
|
* Fired before an update is made, <b>return</b> false from your handler and the update is cancelled.
|
|
* @param {Ext.Element} el
|
|
* @param {String/Object/Function} url
|
|
* @param {String/Object} params
|
|
*/</i>
|
|
"beforeupdate": true,
|
|
<i>/**
|
|
* @event update
|
|
* Fired after successful update is made.
|
|
* @param {Ext.Element} el
|
|
* @param {Object} oResponseObject The response Object
|
|
*/</i>
|
|
"update": true,
|
|
<i>/**
|
|
* @event failure
|
|
* Fired on update failure.
|
|
* @param {Ext.Element} el
|
|
* @param {Object} oResponseObject The response Object
|
|
*/</i>
|
|
"failure": true
|
|
});
|
|
<b>var</b> d = Ext.UpdateManager.defaults;
|
|
<i>/**
|
|
* Blank page URL to use <b>with</b> SSL file uploads (Defaults to Ext.UpdateManager.defaults.sslBlankUrl or "about:blank").
|
|
* @type String
|
|
*/</i>
|
|
<b>this</b>.sslBlankUrl = d.sslBlankUrl;
|
|
<i>/**
|
|
* Whether to append unique parameter on get request to disable caching (Defaults to Ext.UpdateManager.defaults.disableCaching or false).
|
|
* @type Boolean
|
|
*/</i>
|
|
<b>this</b>.disableCaching = d.disableCaching;
|
|
<i>/**
|
|
* Text <b>for</b> loading indicator (Defaults to Ext.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
|
|
* @type String
|
|
*/</i>
|
|
<b>this</b>.indicatorText = d.indicatorText;
|
|
<i>/**
|
|
* Whether to show indicatorText when loading (Defaults to Ext.UpdateManager.defaults.showLoadIndicator or true).
|
|
* @type String
|
|
*/</i>
|
|
<b>this</b>.showLoadIndicator = d.showLoadIndicator;
|
|
<i>/**
|
|
* Timeout <b>for</b> requests or form posts <b>in</b> seconds (Defaults to Ext.UpdateManager.defaults.timeout or 30 seconds).
|
|
* @type Number
|
|
*/</i>
|
|
<b>this</b>.timeout = d.timeout;
|
|
|
|
<i>/**
|
|
* True to process scripts <b>in</b> the output (Defaults to Ext.UpdateManager.defaults.loadScripts (false)).
|
|
* @type Boolean
|
|
*/</i>
|
|
<b>this</b>.loadScripts = d.loadScripts;
|
|
|
|
<i>/**
|
|
* Transaction object of current executing transaction
|
|
*/</i>
|
|
<b>this</b>.transaction = null;
|
|
|
|
<i>/**
|
|
* @private
|
|
*/</i>
|
|
<b>this</b>.autoRefreshProcId = null;
|
|
<i>/**
|
|
* Delegate <b>for</b> refresh() prebound to "<b>this</b>", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
|
|
* @type Function
|
|
*/</i>
|
|
<b>this</b>.refreshDelegate = <b>this</b>.refresh.createDelegate(<b>this</b>);
|
|
<i>/**
|
|
* Delegate <b>for</b> update() prebound to "<b>this</b>", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
|
|
* @type Function
|
|
*/</i>
|
|
<b>this</b>.updateDelegate = <b>this</b>.update.createDelegate(<b>this</b>);
|
|
<i>/**
|
|
* Delegate <b>for</b> formUpdate() prebound to "<b>this</b>", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
|
|
* @type Function
|
|
*/</i>
|
|
<b>this</b>.formUpdateDelegate = <b>this</b>.formUpdate.createDelegate(<b>this</b>);
|
|
<i>/**
|
|
* @private
|
|
*/</i>
|
|
<b>this</b>.successDelegate = <b>this</b>.processSuccess.createDelegate(<b>this</b>);
|
|
<i>/**
|
|
* @private
|
|
*/</i>
|
|
<b>this</b>.failureDelegate = <b>this</b>.processFailure.createDelegate(<b>this</b>);
|
|
|
|
<i>/**
|
|
* The renderer <b>for</b> this UpdateManager. Defaults to {@link Ext.UpdateManager.BasicRenderer}.
|
|
*/</i>
|
|
<b>this</b>.renderer = <b>new</b> Ext.UpdateManager.BasicRenderer();
|
|
|
|
Ext.UpdateManager.superclass.constructor.call(<b>this</b>);
|
|
};
|
|
|
|
Ext.extend(Ext.UpdateManager, Ext.util.Observable, {
|
|
<i>/**
|
|
* Get the Element <b>this</b> UpdateManager is bound to
|
|
* @<b>return</b> {Ext.Element} The element
|
|
*/</i>
|
|
getEl : <b>function</b>(){
|
|
<b>return</b> this.el;
|
|
},
|
|
<i>/**
|
|
* Performs an async request, updating <b>this</b> element <b>with</b> the response. If params are specified it uses POST, otherwise it uses GET.
|
|
* @param {Object/String/Function} url The url <b>for</b> this request or a <b>function</b> to call to get the url or a config object containing any of the following options:
|
|
<pre><code>
|
|
um.update({<br/>
|
|
url: "your-url.php",<br/>
|
|
params: {param1: "foo", param2: "bar"}, <i>// or a URL encoded string<br/></i>
|
|
callback: yourFunction,<br/>
|
|
scope: yourObject, <i>//(optional scope) <br/></i>
|
|
discardUrl: false, <br/>
|
|
nocache: false,<br/>
|
|
text: "Loading...",<br/>
|
|
timeout: 30,<br/>
|
|
scripts: false<br/>
|
|
});
|
|
</code></pre>
|
|
* The only required property is url. The optional properties nocache, text and scripts
|
|
* are shorthand <b>for</b> disableCaching, indicatorText and loadScripts and are used to set their associated property on <b>this</b> UpdateManager instance.
|
|
* @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
|
|
* @param {Function} callback (optional) Callback when transaction is complete - called <b>with</b> signature (oElement, bSuccess, oResponse)
|
|
* @param {Boolean} discardUrl (optional) By <b>default</b> when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
|
|
*/</i>
|
|
update : <b>function</b>(url, params, callback, discardUrl){
|
|
<b>if</b>(this.fireEvent("beforeupdate", <b>this</b>.el, url, params) !== false){
|
|
<b>var</b> method = <b>this</b>.method;
|
|
<b>if</b>(typeof url == "object"){ <i>// must be config object</i>
|
|
<b>var</b> cfg = url;
|
|
url = cfg.url;
|
|
params = params || cfg.params;
|
|
callback = callback || cfg.callback;
|
|
discardUrl = discardUrl || cfg.discardUrl;
|
|
<b>if</b>(callback && cfg.scope){
|
|
callback = callback.createDelegate(cfg.scope);
|
|
}
|
|
<b>if</b>(typeof cfg.method != "undefined"){method = cfg.method;};
|
|
<b>if</b>(typeof cfg.nocache != "undefined"){<b>this</b>.disableCaching = cfg.nocache;};
|
|
<b>if</b>(typeof cfg.text != "undefined"){<b>this</b>.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
|
|
<b>if</b>(typeof cfg.scripts != "undefined"){<b>this</b>.loadScripts = cfg.scripts;};
|
|
<b>if</b>(typeof cfg.timeout != "undefined"){<b>this</b>.timeout = cfg.timeout;};
|
|
}
|
|
<b>this</b>.showLoading();
|
|
<b>if</b>(!discardUrl){
|
|
<b>this</b>.defaultUrl = url;
|
|
}
|
|
<b>if</b>(typeof url == "<b>function</b>"){
|
|
url = url.call(<b>this</b>);
|
|
}
|
|
<b>if</b>(typeof params == "<b>function</b>"){
|
|
params = params();
|
|
}
|
|
<b>if</b>(params && <b>typeof</b> params != "string"){ <i>// must be object</i>
|
|
<b>var</b> buf = [];
|
|
<b>for</b>(var key <b>in</b> params){
|
|
<b>if</b>(typeof params[key] != "<b>function</b>"){
|
|
buf.push(encodeURIComponent(key), "=", encodeURIComponent(params[key]), "&");
|
|
}
|
|
}
|
|
<b>delete</b> buf[buf.length-1];
|
|
params = buf.join("");
|
|
}
|
|
<b>var</b> cb = {
|
|
success: <b>this</b>.successDelegate,
|
|
failure: <b>this</b>.failureDelegate,
|
|
timeout: (<b>this</b>.timeout*1000),
|
|
argument: {"url": url, "form": null, "callback": callback, "params": params}
|
|
};
|
|
method = method || (params ? "POST" : "GET");
|
|
<b>if</b>(method == "GET"){
|
|
url = <b>this</b>.prepareUrl(url);
|
|
}
|
|
<b>this</b>.transaction = Ext.lib.Ajax.request(method, url, cb, params);
|
|
}
|
|
},
|
|
|
|
<i>/**
|
|
* Performs an async form post, updating <b>this</b> element <b>with</b> the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
|
|
* Uses <b>this</b>.sslBlankUrl <b>for</b> SSL file uploads to prevent IE security warning. See YUI docs <b>for</b> more info.
|
|
* @param {String/HTMLElement} form The form Id or form element
|
|
* @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
|
|
* @param {Boolean} reset (optional) Whether to try to reset the form after the update
|
|
* @param {Function} callback (optional) Callback when transaction is complete - called <b>with</b> signature (oElement, bSuccess, oResponse)
|
|
*/</i>
|
|
formUpdate : <b>function</b>(form, url, reset, callback){
|
|
<b>if</b>(this.fireEvent("beforeupdate", <b>this</b>.el, form, url) !== false){
|
|
formEl = Ext.getDom(form);
|
|
<b>if</b>(typeof url == "<b>function</b>"){
|
|
url = url.call(<b>this</b>);
|
|
}
|
|
<b>if</b>(typeof params == "<b>function</b>"){
|
|
params = params();
|
|
}
|
|
url = url || formEl.action;
|
|
<b>var</b> cb = {
|
|
success: <b>this</b>.successDelegate,
|
|
failure: <b>this</b>.failureDelegate,
|
|
timeout: (<b>this</b>.timeout*1000),
|
|
argument: {"url": url, "form": formEl, "callback": callback, "reset": reset}
|
|
};
|
|
<b>var</b> isUpload = false;
|
|
<b>var</b> enctype = formEl.getAttribute("enctype");
|
|
<b>if</b>(enctype && enctype.toLowerCase() == "multipart/form-data"){
|
|
isUpload = true;
|
|
cb.upload = <b>this</b>.successDelegate;
|
|
}
|
|
<b>this</b>.transaction = Ext.lib.Ajax.formRequest(formEl, url, cb, null, isUpload, <b>this</b>.sslBlankUrl);
|
|
<b>this</b>.showLoading.defer(1, <b>this</b>);
|
|
}
|
|
},
|
|
|
|
<i>/**
|
|
* Refresh the element <b>with</b> the last used url or defaultUrl. If there is no url, it returns immediately
|
|
* @param {Function} callback (optional) Callback when transaction is complete - called <b>with</b> signature (oElement, bSuccess)
|
|
*/</i>
|
|
refresh : <b>function</b>(callback){
|
|
<b>if</b>(this.defaultUrl == null){
|
|
<b>return</b>;
|
|
}
|
|
<b>this</b>.update(<b>this</b>.defaultUrl, null, callback, true);
|
|
},
|
|
|
|
<i>/**
|
|
* Set <b>this</b> element to auto refresh.
|
|
* @param {Number} interval How often to update (<b>in</b> seconds).
|
|
* @param {String/Function} url (optional) The url <b>for</b> this request or a <b>function</b> to call to get the url (Defaults to the last used url)
|
|
* @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
|
|
* @param {Function} callback (optional) Callback when transaction is complete - called <b>with</b> signature (oElement, bSuccess)
|
|
* @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
|
|
*/</i>
|
|
startAutoRefresh : <b>function</b>(interval, url, params, callback, refreshNow){
|
|
<b>if</b>(refreshNow){
|
|
<b>this</b>.update(url || <b>this</b>.defaultUrl, params, callback, true);
|
|
}
|
|
<b>if</b>(this.autoRefreshProcId){
|
|
clearInterval(<b>this</b>.autoRefreshProcId);
|
|
}
|
|
<b>this</b>.autoRefreshProcId = setInterval(<b>this</b>.update.createDelegate(<b>this</b>, [url || <b>this</b>.defaultUrl, params, callback, true]), interval*1000);
|
|
},
|
|
|
|
<i>/**
|
|
* Stop auto refresh on <b>this</b> element.
|
|
*/</i>
|
|
stopAutoRefresh : <b>function</b>(){
|
|
<b>if</b>(this.autoRefreshProcId){
|
|
clearInterval(<b>this</b>.autoRefreshProcId);
|
|
<b>delete</b> this.autoRefreshProcId;
|
|
}
|
|
},
|
|
|
|
isAutoRefreshing : <b>function</b>(){
|
|
<b>return</b> this.autoRefreshProcId ? true : false;
|
|
},
|
|
<i>/**
|
|
* Called to update the element to "Loading" state. Override to perform custom action.
|
|
*/</i>
|
|
showLoading : <b>function</b>(){
|
|
<b>if</b>(this.showLoadIndicator){
|
|
<b>this</b>.el.update(<b>this</b>.indicatorText);
|
|
}
|
|
},
|
|
|
|
<i>/**
|
|
* Adds unique parameter to query string <b>if</b> disableCaching = true
|
|
* @private
|
|
*/</i>
|
|
prepareUrl : <b>function</b>(url){
|
|
<b>if</b>(this.disableCaching){
|
|
<b>var</b> append = "_dc=" + (<b>new</b> Date().getTime());
|
|
<b>if</b>(url.indexOf("?") !== -1){
|
|
url += "&" + append;
|
|
}<b>else</b>{
|
|
url += "?" + append;
|
|
}
|
|
}
|
|
<b>return</b> url;
|
|
},
|
|
|
|
<i>/**
|
|
* @private
|
|
*/</i>
|
|
processSuccess : <b>function</b>(response){
|
|
<b>this</b>.transaction = null;
|
|
<b>if</b>(response.argument.form && response.argument.reset){
|
|
try{ <i>// put <b>in</b> try/catch since some older FF releases had problems <b>with</b> this</i>
|
|
response.argument.form.reset();
|
|
}catch(e){}
|
|
}
|
|
<b>if</b>(this.loadScripts){
|
|
<b>this</b>.renderer.render(<b>this</b>.el, response, <b>this</b>,
|
|
<b>this</b>.updateComplete.createDelegate(<b>this</b>, [response]));
|
|
}<b>else</b>{
|
|
<b>this</b>.renderer.render(<b>this</b>.el, response, <b>this</b>);
|
|
<b>this</b>.updateComplete(response);
|
|
}
|
|
},
|
|
|
|
updateComplete : <b>function</b>(response){
|
|
<b>this</b>.fireEvent("update", <b>this</b>.el, response);
|
|
<b>if</b>(typeof response.argument.callback == "<b>function</b>"){
|
|
response.argument.callback(<b>this</b>.el, true, response);
|
|
}
|
|
},
|
|
|
|
<i>/**
|
|
* @private
|
|
*/</i>
|
|
processFailure : <b>function</b>(response){
|
|
<b>this</b>.transaction = null;
|
|
<b>this</b>.fireEvent("failure", <b>this</b>.el, response);
|
|
<b>if</b>(typeof response.argument.callback == "<b>function</b>"){
|
|
response.argument.callback(<b>this</b>.el, false, response);
|
|
}
|
|
},
|
|
|
|
<i>/**
|
|
* Set the content renderer <b>for</b> this UpdateManager. See {@link Ext.UpdateManager.BasicRenderer#render} <b>for</b> more details.
|
|
* @param {Object} renderer The object implementing the render() method
|
|
*/</i>
|
|
setRenderer : <b>function</b>(renderer){
|
|
<b>this</b>.renderer = renderer;
|
|
},
|
|
|
|
getRenderer : <b>function</b>(){
|
|
<b>return</b> this.renderer;
|
|
},
|
|
|
|
<i>/**
|
|
* Set the defaultUrl used <b>for</b> updates
|
|
* @param {String/Function} defaultUrl The url or a <b>function</b> to call to get the url
|
|
*/</i>
|
|
setDefaultUrl : <b>function</b>(defaultUrl){
|
|
<b>this</b>.defaultUrl = defaultUrl;
|
|
},
|
|
|
|
<i>/**
|
|
* Aborts the executing transaction
|
|
*/</i>
|
|
abort : <b>function</b>(){
|
|
<b>if</b>(this.transaction){
|
|
Ext.lib.Ajax.abort(<b>this</b>.transaction);
|
|
}
|
|
},
|
|
|
|
<i>/**
|
|
* Returns true <b>if</b> an update is <b>in</b> progress
|
|
* @<b>return</b> {Boolean}
|
|
*/</i>
|
|
isUpdating : <b>function</b>(){
|
|
<b>if</b>(this.transaction){
|
|
<b>return</b> Ext.lib.Ajax.isCallInProgress(<b>this</b>.transaction);
|
|
}
|
|
<b>return</b> false;
|
|
}
|
|
});
|
|
|
|
<i>/**
|
|
* @class Ext.UpdateManager.defaults
|
|
* The defaults collection enables customizing the <b>default</b> properties of UpdateManager
|
|
*/</i>
|
|
Ext.UpdateManager.defaults = {
|
|
<i>/**
|
|
* Timeout <b>for</b> requests or form posts <b>in</b> seconds (Defaults 30 seconds).
|
|
* @type Number
|
|
*/</i>
|
|
timeout : 30,
|
|
|
|
<i>/**
|
|
* True to process scripts by <b>default</b> (Defaults to false).
|
|
* @type Boolean
|
|
*/</i>
|
|
loadScripts : false,
|
|
|
|
<i>/**
|
|
* Blank page URL to use <b>with</b> SSL file uploads (Defaults to "javascript:false").
|
|
* @type String
|
|
*/</i>
|
|
sslBlankUrl : (Ext.SSL_SECURE_URL || "javascript:false"),
|
|
<i>/**
|
|
* Whether to append unique parameter on get request to disable caching (Defaults to false).
|
|
* @type Boolean
|
|
*/</i>
|
|
disableCaching : false,
|
|
<i>/**
|
|
* Whether to show indicatorText when loading (Defaults to true).
|
|
* @type Boolean
|
|
*/</i>
|
|
showLoadIndicator : true,
|
|
<i>/**
|
|
* Text <b>for</b> loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
|
|
* @type String
|
|
*/</i>
|
|
indicatorText : '<div class="loading-indicator">Loading...</div>'
|
|
};
|
|
|
|
<i>/**
|
|
* Static convenience method. This method is deprecated <b>in</b> favor of el.load({url:'foo.php', ...}).
|
|
*Usage:
|
|
* <pre><code>Ext.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
|
|
* @param {String/HTMLElement/Ext.Element} el The element to update
|
|
* @param {String} url The url
|
|
* @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
|
|
* @param {Object} options (optional) A config object <b>with</b> any of the UpdateManager properties you want to set - <b>for</b> example: {disableCaching:true, indicatorText: "Loading data..."}
|
|
* @static
|
|
* @deprecated
|
|
* @member Ext.UpdateManager
|
|
*/</i>
|
|
Ext.UpdateManager.updateElement = <b>function</b>(el, url, params, options){
|
|
<b>var</b> um = Ext.get(el, true).getUpdateManager();
|
|
Ext.apply(um, options);
|
|
um.update(url, params, options ? options.callback : null);
|
|
};
|
|
<i>// alias <b>for</b> backwards compat</i>
|
|
Ext.UpdateManager.update = Ext.UpdateManager.updateElement;
|
|
<i>/**
|
|
* @class Ext.UpdateManager.BasicRenderer
|
|
* Default Content renderer. Updates the elements innerHTML <b>with</b> the responseText.
|
|
*/</i>
|
|
Ext.UpdateManager.BasicRenderer = <b>function</b>(){};
|
|
|
|
Ext.UpdateManager.BasicRenderer.prototype = {
|
|
<i>/**
|
|
* This is called when the transaction is completed and it's time to update the element - The BasicRenderer
|
|
* updates the elements innerHTML <b>with</b> the responseText - To perform a custom render (i.e. XML or JSON processing),
|
|
* create an object <b>with</b> a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
|
|
* @param {Ext.Element} el The element being rendered
|
|
* @param {Object} response The YUI Connect response object
|
|
* @param {UpdateManager} updateManager The calling update manager
|
|
* @param {Function} callback A callback that will need to be called <b>if</b> loadScripts is true on the UpdateManager
|
|
*/</i>
|
|
render : <b>function</b>(el, response, updateManager, callback){
|
|
el.update(response.responseText, updateManager.loadScripts, callback);
|
|
}
|
|
};
|
|
</code></pre><hr><div style="font-size:10px;text-align:center;color:gray;">Ext - Copyright © 2006-2007 Ext JS, LLC<br />All rights reserved.</div>
|
|
</body></html> |