forked from extern/egroupware
Use jQuery's Deferred to solve the deferred loading problem instead of load events.
This commit is contained in:
parent
f10fc199a0
commit
4002907c05
@ -730,26 +730,68 @@ var et2_widget = Class.extend(
|
|||||||
* function is called, the DOM-Tree is created. loadingFinished is
|
* function is called, the DOM-Tree is created. loadingFinished is
|
||||||
* recursively called for all child elements. Do not directly override this
|
* recursively called for all child elements. Do not directly override this
|
||||||
* function but the doLoadingFinished function which is executed before
|
* function but the doLoadingFinished function which is executed before
|
||||||
* descending deeper into the DOM-Tree
|
* descending deeper into the DOM-Tree.
|
||||||
|
*
|
||||||
|
* Some widgets (template) do not load immediately because they request
|
||||||
|
* additional resources via AJAX. They will return a Deferred Promise object.
|
||||||
|
* If you call loadingFinished(promises) after creating such a widget
|
||||||
|
* programmatically, you might need to wait for it to fully complete its
|
||||||
|
* loading before proceeding. In that case use:
|
||||||
|
* <code>
|
||||||
|
* var promises = [];
|
||||||
|
* widget.loadingFinished(promises);
|
||||||
|
* jQuery.when.apply(null, promises).done( doneCallback );
|
||||||
|
* </code>
|
||||||
|
* @see {@link http://api.jquery.com/category/deferred-object/|jQuery Deferred}
|
||||||
|
*
|
||||||
|
* @param {Promise[]} promises List of promises from widgets that are not done. Pass an empty array, it will be filled if needed.
|
||||||
*/
|
*/
|
||||||
loadingFinished: function() {
|
loadingFinished: function(promises) {
|
||||||
// Call all availble setters
|
// Call all availble setters
|
||||||
this.initAttributes(this.options);
|
this.initAttributes(this.options);
|
||||||
|
|
||||||
if (this.doLoadingFinished())
|
// Make sure promises is defined to avoid errors.
|
||||||
|
// We'll warn (below) if programmer should have passed it.
|
||||||
|
if(typeof promises == "undefined")
|
||||||
|
{
|
||||||
|
promises = [];
|
||||||
|
var warn_if_deferred = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loadChildren = function()
|
||||||
{
|
{
|
||||||
// Descend recursively into the tree
|
// Descend recursively into the tree
|
||||||
for (var i = 0; i < this._children.length; i++)
|
for (var i = 0; i < this._children.length; i++)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this._children[i].loadingFinished();
|
this._children[i].loadingFinished(promises);
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e)
|
||||||
{
|
{
|
||||||
egw.debug("error", "There was an error with a widget:\nError:%o\nProblem widget:%o",e.valueOf(),this._children[i],e.stack);
|
egw.debug("error", "There was an error with a widget:\nError:%o\nProblem widget:%o",e.valueOf(),this._children[i],e.stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = this.doLoadingFinished();
|
||||||
|
if(typeof result == "boolean" && result)
|
||||||
|
{
|
||||||
|
// Simple widget finishes nicely
|
||||||
|
loadChildren.apply(this, arguments);
|
||||||
|
}
|
||||||
|
else if (typeof result == "object" && result.done)
|
||||||
|
{
|
||||||
|
// Warn if list was not provided
|
||||||
|
if(warn_if_deferred)
|
||||||
|
{
|
||||||
|
// Might not be a problem, but if you need the widget to be really loaded, it could be
|
||||||
|
egw.debug("warning", "Loading was deferred for widget %o, but creator is not checking. Pass a list to loadingFinished().");
|
||||||
|
}
|
||||||
|
// Widget is waiting. Add to the list
|
||||||
|
promises.push(result);
|
||||||
|
// Fihish loading when it's finished
|
||||||
|
result.done(jQuery.proxy(loadChildren, this));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -779,6 +821,15 @@ var et2_widget = Class.extend(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does specific post-processing after the widget is loaded. Most widgets should not
|
||||||
|
* need to do anything here, it should all be done before.
|
||||||
|
*
|
||||||
|
* @return {boolean|Promise} True if the widget is fully loaded, false to avoid procesing children,
|
||||||
|
* or a Promise if loading is not actually finished (eg. waiting for AJAX)
|
||||||
|
*
|
||||||
|
* @see {@link http://api.jquery.com/deferred.promise/|jQuery Promise}
|
||||||
|
*/
|
||||||
doLoadingFinished: function() {
|
doLoadingFinished: function() {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -1205,7 +1205,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
|
|||||||
{
|
{
|
||||||
// Keep the name of the template, as we'll free up the widget after parsing
|
// Keep the name of the template, as we'll free up the widget after parsing
|
||||||
this.template = _value;
|
this.template = _value;
|
||||||
template.loadingFinished();
|
|
||||||
// Fetch the grid element and parse it
|
// Fetch the grid element and parse it
|
||||||
var definitionGrid = template.getChildren()[0];
|
var definitionGrid = template.getChildren()[0];
|
||||||
if (definitionGrid && definitionGrid instanceof et2_grid)
|
if (definitionGrid && definitionGrid instanceof et2_grid)
|
||||||
@ -1242,7 +1242,11 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Template might not be loaded yet, defer parsing
|
// Template might not be loaded yet, defer parsing
|
||||||
$j(template.getDOMNode()).on("load",
|
var promise = []
|
||||||
|
template.loadingFinished(promise);
|
||||||
|
|
||||||
|
// Wait until template (& children) are done
|
||||||
|
jQuery.when.apply(null, promise).done(
|
||||||
jQuery.proxy(function() {
|
jQuery.proxy(function() {
|
||||||
parse.call(this, template);
|
parse.call(this, template);
|
||||||
this.dynheight.initialized = false;
|
this.dynheight.initialized = false;
|
||||||
@ -1656,7 +1660,7 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader,
|
|||||||
_build_left_right: function(left_or_right, template_name)
|
_build_left_right: function(left_or_right, template_name)
|
||||||
{
|
{
|
||||||
var existing = this.headers[left_or_right == "left" ? 0 : 1];
|
var existing = this.headers[left_or_right == "left" ? 0 : 1];
|
||||||
if(existing)
|
if(existing && existing._type)
|
||||||
{
|
{
|
||||||
if(existing.id == template_name) return;
|
if(existing.id == template_name) return;
|
||||||
existing.free();
|
existing.free();
|
||||||
@ -1666,11 +1670,12 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader,
|
|||||||
// Load the template
|
// Load the template
|
||||||
var header = et2_createWidget("template", {"id": template_name}, this);
|
var header = et2_createWidget("template", {"id": template_name}, this);
|
||||||
jQuery(header.getDOMNode()).addClass(left_or_right == "left" ? "et2_hbox_left":"et2_hbox_right").addClass("nm_header");
|
jQuery(header.getDOMNode()).addClass(left_or_right == "left" ? "et2_hbox_left":"et2_hbox_right").addClass("nm_header");
|
||||||
this.headers.push(header);
|
this.headers[left_or_right == "left" ? 0 : 1] = header;
|
||||||
$j(header.getDOMNode()).on("load", jQuery.proxy(function() {
|
$j(header.getDOMNode()).on("load", jQuery.proxy(function() {
|
||||||
header.loadingFinished();
|
//header.loadingFinished();
|
||||||
this._bindHeaderInput(header);
|
this._bindHeaderInput(header);
|
||||||
},this));
|
},this));
|
||||||
|
header.loadingFinished();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1873,9 +1878,12 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader,
|
|||||||
return this.filters[0];
|
return this.filters[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(var i = 0; i < this.headers.length; i++)
|
if(_sender && _sender._type == "template")
|
||||||
{
|
{
|
||||||
if(_sender.id == this.headers[i].id && _sender._parent == this) return this.header_div[0];
|
for(var i = 0; i < this.headers.length; i++)
|
||||||
|
{
|
||||||
|
if(_sender.id == this.headers[i].id && _sender._parent == this) return this.header_div[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
@ -77,8 +77,8 @@ var et2_template = et2_DOMWidget.extend(
|
|||||||
|
|
||||||
this.div = document.createElement("div");
|
this.div = document.createElement("div");
|
||||||
|
|
||||||
// Flag to indicate that loading is finished
|
// Deferred object so we can load via AJAX
|
||||||
this.loading = false;
|
this.loading = jQuery.Deferred();
|
||||||
|
|
||||||
if (this.id != "" || this.options.template)
|
if (this.id != "" || this.options.template)
|
||||||
{
|
{
|
||||||
@ -108,8 +108,6 @@ var et2_template = et2_DOMWidget.extend(
|
|||||||
|
|
||||||
if(splitted.length)
|
if(splitted.length)
|
||||||
{
|
{
|
||||||
// Still loading, don't fire loading finished
|
|
||||||
this.loading = true;
|
|
||||||
et2_loadXMLFromURL(path, function(_xmldoc) {
|
et2_loadXMLFromURL(path, function(_xmldoc) {
|
||||||
var templates = this.getInstanceManager().templates || {};
|
var templates = this.getInstanceManager().templates || {};
|
||||||
// Scan for templates and store them
|
// Scan for templates and store them
|
||||||
@ -123,13 +121,7 @@ var et2_template = et2_DOMWidget.extend(
|
|||||||
this.loadFromXML(templates[template_name]);
|
this.loadFromXML(templates[template_name]);
|
||||||
|
|
||||||
// Update flag
|
// Update flag
|
||||||
this.loading = false;
|
this.loading.resolve();
|
||||||
|
|
||||||
// Fire the load event (after)
|
|
||||||
var self = this;
|
|
||||||
window.setTimeout(function() {
|
|
||||||
$j(self.getDOMNode()).trigger('load');
|
|
||||||
},0);
|
|
||||||
|
|
||||||
}, this);
|
}, this);
|
||||||
}
|
}
|
||||||
@ -140,17 +132,16 @@ var et2_template = et2_DOMWidget.extend(
|
|||||||
{
|
{
|
||||||
this.egw().debug("log", "Loading template from XML: ", template_name);
|
this.egw().debug("log", "Loading template from XML: ", template_name);
|
||||||
this.loadFromXML(xml);
|
this.loadFromXML(xml);
|
||||||
// Don't call this here - premature
|
// Don't call this here - done by caller, or on whole widget tree
|
||||||
//this.loadingFinished();
|
//this.loadingFinished();
|
||||||
// Fire the load event (after)
|
|
||||||
var self = this;
|
// But resolve the promise
|
||||||
window.setTimeout(function() {
|
this.loading.resolve();
|
||||||
$j(self.getDOMNode()).trigger('load');
|
|
||||||
},0);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.egw().debug("warn", "Unable to find XML for ", template_name);
|
this.egw().debug("warn", "Unable to find XML for ", template_name);
|
||||||
|
this.loading.reject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -175,13 +166,17 @@ var et2_template = et2_DOMWidget.extend(
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override to trigger a load event, to facilitate processing when the xml file
|
* Override to return the promise for deferred loading
|
||||||
* is loaded asyncronously
|
|
||||||
*/
|
*/
|
||||||
doLoadingFinished: function() {
|
doLoadingFinished: function() {
|
||||||
if(this.loading) return false;
|
// Apply parent now, which actually puts into the DOM
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
return true;
|
|
||||||
|
// Fire load event when done loading
|
||||||
|
this.loading.done(jQuery.proxy(function() {$j(this).trigger("load");},this.div));
|
||||||
|
|
||||||
|
// Not done yet, but widget will let you know
|
||||||
|
return this.loading.promise();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
et2_register_widget(et2_template, ["template"]);
|
et2_register_widget(et2_template, ["template"]);
|
||||||
|
@ -245,8 +245,11 @@ etemplate2.prototype.load = function(_name, _url, _data, _callback)
|
|||||||
// Read the XML structure of the requested template
|
// Read the XML structure of the requested template
|
||||||
this.widgetContainer.loadFromXML(this.templates[_name || missing_name]);
|
this.widgetContainer.loadFromXML(this.templates[_name || missing_name]);
|
||||||
|
|
||||||
|
// List of Promises from widgets that are not quite fully loaded
|
||||||
|
var deferred = [];
|
||||||
|
|
||||||
// Inform the widget tree that it has been successfully loaded.
|
// Inform the widget tree that it has been successfully loaded.
|
||||||
this.widgetContainer.loadingFinished();
|
this.widgetContainer.loadingFinished(deferred);
|
||||||
|
|
||||||
// Insert the document fragment to the DOM Container
|
// Insert the document fragment to the DOM Container
|
||||||
this.DOMContainer.appendChild(frag);
|
this.DOMContainer.appendChild(frag);
|
||||||
@ -258,19 +261,23 @@ etemplate2.prototype.load = function(_name, _url, _data, _callback)
|
|||||||
}
|
}
|
||||||
etemplate2._byTemplate[_name].push(this);
|
etemplate2._byTemplate[_name].push(this);
|
||||||
|
|
||||||
// Trigger the "resize" event
|
// Wait for everything to be loaded, then finish it up
|
||||||
this.resize();
|
jQuery.when.apply(null, deferred).done(jQuery.proxy(function() {
|
||||||
|
// Trigger the "resize" event
|
||||||
|
this.resize();
|
||||||
|
|
||||||
if(typeof _callback == "function")
|
// Tell others about it
|
||||||
{
|
if(typeof _callback == "function")
|
||||||
_callback.call(window,this);
|
{
|
||||||
}
|
_callback.call(window,this);
|
||||||
if(_callback != app_callback)
|
}
|
||||||
{
|
if(_callback != app_callback)
|
||||||
app_callback.call(window,this);
|
{
|
||||||
}
|
app_callback.call(window,this);
|
||||||
|
}
|
||||||
|
|
||||||
$j(this.DOMContainer).trigger('load', this);
|
$j(this.DOMContainer).trigger('load', this);
|
||||||
|
},this));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user