Index: umc/widgets/Form.js
===================================================================
--- umc/widgets/Form.js (Revision 36481)
+++ umc/widgets/Form.js (Arbeitskopie)
@@ -26,19 +26,19 @@
* /usr/share/common-licenses/AGPL-3; if not, see
* .
*/
-/*global define */
+/*global define require console*/
define([
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/_base/array",
"dojo/Deferred",
- "dojo/on",
+ "dojo/promise/all",
"dojo/dom-style",
"dijit/form/Form",
"umc/tools",
"umc/render"
-], function(declare, lang, array, Deferred, on, style, Form, tools, render) {
+], function(declare, lang, array, Deferred, all, style, Form, tools, render) {
// in order to break circular dependencies (umc.dialog needs a Form and
// Form needs umc/dialog), we define umc/dialog as an empty object and
@@ -121,13 +121,14 @@
//'class': 'umcNoBorder',
- _initializingElements: 0,
+ _allReady: null,
- _initializedDeferred: null,
-
postMixInProperties: function() {
this.inherited(arguments);
+ // initialize with empty list
+ this._allReady = [];
+
// in case no layout is specified and no content, either, create one automatically
if ((!this.layout || !this.layout.length) && !this.content) {
this.layout = [];
@@ -183,8 +184,6 @@
buildRendering: function() {
this.inherited(arguments);
- this._initializedDeferred = new Deferred();
-
if (this.scrollable) {
style.set(this.containerNode, {
overflow: 'auto'
@@ -225,47 +224,19 @@
}
}
-
- // send an event when all dynamic elements have been initialized
- this._initializingElements = 0;
- //console.log('# Form.buildRendering()');
- tools.forIn(this._widgets, function(iname, iwidget) {
- // only consider elements that load values dynamically
- //console.log('# iwidget:', iwidget.name);
- if ('onValuesLoaded' in iwidget && !(iwidget._valuesLoaded && !iwidget._deferredOrValues)) {
- // widget values have not been loaded completely so far
- //console.log('# -> has event "valuesLoaded"');
- ++this._initializingElements;
- on.once(iwidget, 'valuesLoaded', lang.hitch(this, function() {
- //console.log('# -> valuesLoaded:', iwidget.name, iwidget.get('value'));
- // decrement the internal counter
- --this._initializingElements;
- //console.log('# _initializingElements:', this._initializingElements);
-
- // send event when the last element has been initialized
- if (0 === this._initializingElements) {
- this._initializedDeferred.resolve();
- }
- }));
- }
- }, this);
-
- // maybe all elements are already initialized
- if (!this._initializingElements) {
- this._initializedDeferred.resolve();
- }
-
+ this._updateAllReady();
},
startup: function() {
+ //console.log('### Form: startup - container:', this._container);
this.inherited(arguments);
if (this._container) {
// call the containers startup function if necessary
this._container.startup();
}
- this._initializedDeferred.then(lang.hitch(this, function() {
+ this.ready().then(lang.hitch(this, function() {
+ //console.log('### Form: all ready');
this.onValuesInitialized();
- //console.log('# valuesInitialized');
}));
},
@@ -553,8 +524,31 @@
dialog.notify(error_msg);
}
}));
- }
+ },
+ _updateAllReady: function() {
+ // wait for all widgets to be ready
+ this._allReady = [];
+ //console.log('### Form: iterate over widgets.ready');
+ tools.forIn(this._widgets, function(iname, iwidget) {
+ //console.log('### ' + iname + ' -> ', iwidget.ready ? iwidget.ready() : null);
+ this._allReady.push(iwidget.ready ? iwidget.ready() : null);
+ }, this);
+ },
+
+ ready: function() {
+ // update the internal list in order to wait until everybody is ready
+ if (!this._allReady.length) {
+ _updateAllReady();
+ }
+ var ret = all(this._allReady);
+
+ // empty list when all widgets are ready
+ ret.then(lang.hitch(this, function() {
+ this._allReady = [];
+ }));
+ return ret;
+ }
});
});
Index: umc/widgets/MultiInput.js
===================================================================
--- umc/widgets/MultiInput.js (Revision 36481)
+++ umc/widgets/MultiInput.js (Arbeitskopie)
@@ -32,13 +32,15 @@
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/_base/array",
+ "dojo/Deferred",
+ "dojo/promise/all",
"dijit/form/Button",
"umc/tools",
"umc/render",
"umc/widgets/ContainerWidget",
"umc/widgets/_FormWidgetMixin",
"umc/widgets/LabelPane"
-], function(declare, lang, array, Button, tools, render, ContainerWidget, _FormWidgetMixin, LabelPane) {
+], function(declare, lang, array, Deferred, all, Button, tools, render, ContainerWidget, _FormWidgetMixin, LabelPane) {
return declare("umc.widgets.MultiInput", [ ContainerWidget, _FormWidgetMixin ], {
// summary:
// Widget for a small list of simple and complex entries. An entry can be one or
@@ -77,6 +79,12 @@
_lastDepends: null,
+ _valuesLoaded: false,
+
+ _readyDeferred: null,
+
+ _startupDeferred: null,
+
_createHandler: function(ifunc) {
// This handler will be called by all subwidgets of the MultiInput widget.
// When the first request comes in, we will execute the function to compute
@@ -106,8 +114,14 @@
postMixInProperties: function() {
this.inherited(arguments);
+ // delete the size class
this.sizeClass = null;
+ // the _readyDeferred is being resolved as soon as everything has been set up
+ this._readyDeferred = new Deferred();
+
+ this._startupDeferred = new Deferred();
+
// check the property 'subtypes'
tools.assert(this.subtypes instanceof Array,
'umc/widgets/ContainerWidget: The property subtypes needs to be a string or an array of strings: ' + this.subtypes);
@@ -149,6 +163,12 @@
this._appendElements(1);
},
+ startup: function() {
+ this.inherited(arguments);
+
+ this._startupDeferred.resolve();
+ },
+
_loadValues: function(depends) {
// delegate the call to _loadValues to all widgets
this._lastDepends = depends;
@@ -316,6 +336,11 @@
return;
}
+ // initiate a new Deferred object in case there is none already pending
+ if (this._readyDeferred.isFulfilled()) {
+ this._readyDeferred = new Deferred();
+ }
+
// remove the 'new' button
this._removeNewButton();
@@ -403,7 +428,7 @@
// add row
this._widgets.push(visibleWidgets);
this._rowContainers.push(rowContainer);
- rowContainer.startup();
+ this._startupDeferred.then(lang.hitch(rowContainer, 'startup'));
this.addChild(rowContainer);
// call the _loadValues method by hand
@@ -415,6 +440,21 @@
}, this);
}
+ // wait for all widgets to be ready
+ var allReady = [];
+ var i, j;
+ for (i = 0; i < this._widgets.length; ++i) {
+ for (j = 0; j < this._widgets[i].length; ++j) {
+ //console.log(lang.replace('### MultiInput: widget[{0}][{1}]: waiting -> ', [i, j]), this._widgets[i][j].ready());
+ allReady.push(this._widgets[i][j].ready ? this._widgets[i][j].ready() : null);
+ }
+ }
+ all(allReady).then(lang.hitch(this, function() {
+ //console.log('### MultiInput: all resolved');
+ this._readyDeferred.resolve();
+ this.onValuesLoaded();
+ }));
+
// add the new button
if (this._nRenderedElements < this.max) {
this._addNewButton();
@@ -494,6 +534,19 @@
if (this._widget) {
tools.delegateCall(this, arguments, this._widget);
}
+ },
+
+ onValuesLoaded: function(values) {
+ // summary:
+ // This event is triggered when all values (static and dynamic) have been loaded.
+ // values:
+ // Array containing all dynamic and static values.
+ },
+
+ // ready:
+ // Similiar to `umc/widgets/_FormWidgetMixin:ready`.
+ ready: function() {
+ return this._readyDeferred;
}
});
});
Index: umc/widgets/_SelectMixin.js
===================================================================
--- umc/widgets/_SelectMixin.js (Revision 36481)
+++ umc/widgets/_SelectMixin.js (Arbeitskopie)
@@ -34,13 +34,12 @@
"dojo/_base/array",
"dojo/Deferred",
"dojo/when",
- "dojo/on",
"dojo/json",
"dojo/Stateful",
// TODO: should use dojo/store/
"dojo/data/ItemFileWriteStore",
"umc/tools"
-], function(declare, lang, array, Deferred, when, on, json, Stateful, ItemFileWriteStore, tools) {
+], function(declare, lang, array, Deferred, when, json, Stateful, ItemFileWriteStore, tools) {
return declare("umc.widgets._SelectMixin", Stateful, {
// umcpCommand:
// Reference to the umcpCommand the widget should use.
@@ -105,6 +104,8 @@
_deferredOrValues: null,
+ _readyDeferred: null,
+
_createStore: function() {
return new ItemFileWriteStore({
data: {
@@ -128,6 +129,10 @@
this.inherited(arguments);
this._saveInitialValue();
+
+ // the _readyDeferred is being resolved as soon as everything has been set up
+ //console.log('### _SelectMixin ['+this.name+']: initiate _readyDeferred');
+ this._readyDeferred = new Deferred();
},
postCreate: function() {
@@ -382,7 +387,7 @@
},
_loadValues: function(/*Object?*/ _dependValues) {
- //console.log('###', this.name, ' _loadValues(', _dependValues, ')');
+ //console.log('### _SelectMixin ['+this.name+']: _loadValues');
this._valuesLoaded = true;
// unify `depends` property to be an array
@@ -404,6 +409,7 @@
// only load dynamic values in case all dependencies are fullfilled
if (dependList.length != nDepValues) {
+ //console.log('### _SelectMixin ['+this.name+']: return');
return;
}
@@ -420,9 +426,16 @@
// block concurrent events for value loading
if (this._deferredOrValues) {
// another request is pending
+ //console.log('### _SelectMixin ['+this.name+']: return (2)');
return;
}
+ // initiate a new Deferred object in case there is none already pending
+ if (this._readyDeferred.isFulfilled()) {
+ //console.log('### _SelectMixin ['+this.name+']: re-initiate _readyDeferred');
+ this._readyDeferred = new Deferred();
+ }
+
// get dynamic values
var func = tools.stringOrFunction(this.dynamicValues, this.umcpCommand);
var deferredOrValues = func(params);
@@ -439,9 +452,11 @@
this._clearValues();
this._setStaticValues();
this._setDynamicValues(res);
- this.onDynamicValuesLoaded(res);
// values have been loaded
+ //console.log('### _SelectMixin ['+this.name+']: resolved _readyDeferred');
+ this._readyDeferred.resolve(this.getAllItems());
+ this.onDynamicValuesLoaded(res);
this.onValuesLoaded(this.getAllItems());
// unblock value loading
@@ -452,6 +467,8 @@
this._setStaticValues();
// error handler
+ //console.log('### _SelectMixin ['+this.name+']: resolved _readyDeferred');
+ this._readyDeferred.resolve([]);
this.onDynamicValuesLoaded([]);
this.onValuesLoaded(this.getAllItems());
@@ -465,6 +482,8 @@
this._setStaticValues();
// values have been loaded
+ //console.log('### _SelectMixin ['+this.name+']: resolved _readyDeferred');
+ this._readyDeferred.resolve(this.getAllItems());
this.onValuesLoaded(this.getAllItems());
// unblock value loading
@@ -532,6 +551,13 @@
if (this._valuesLoaded) {
this._loadValues();
}
+ },
+
+ // ready:
+ // Returns null or a Deferred which resolves as soon as any
+ // loading activity of the widget is finished.
+ ready: function() {
+ return this._readyDeferred;
}
});
});
Index: umc/widgets/MixedInput.js
===================================================================
--- umc/widgets/MixedInput.js (Revision 36481)
+++ umc/widgets/MixedInput.js (Arbeitskopie)
@@ -31,6 +31,7 @@
define([
"dojo/_base/declare",
"dojo/_base/lang",
+ "dojo/Deferred",
"dojo/json",
"dijit/layout/ContentPane",
"umc/tools",
@@ -38,7 +39,7 @@
"umc/widgets/TextBox",
"umc/widgets/ComboBox",
"umc/widgets/CheckBox"
-], function(declare, lang, json, ContentPane, tools, _FormWidgetMixin) {
+], function(declare, lang, Deferred, json, ContentPane, tools, _FormWidgetMixin) {
return declare("umc.widgets.MixedInput", [ ContentPane, _FormWidgetMixin ], {
// umcpCommand:
// Reference to the umcpCommand the widget should use.
@@ -74,6 +75,8 @@
style: 'padding: 0',
+ _readyDeferred: null,
+
constructor: function(/*Object*/ props) {
// mixin in the 'disabled' property
props.disabled = this.disabled;
@@ -94,6 +97,8 @@
this._userProperties.sizeClass = this.sizeClass;
this.sizeClass = null;
+
+ this._readyDeferred = new Deferred();
},
buildRendering: function() {
@@ -135,6 +140,11 @@
return;
}
+ // initiate a new Deferred object in case there is none already pending
+ if (this._readyDeferred.isFulfilled()) {
+ this._readyDeferred = new Deferred();
+ }
+
// mixin additional options for the UMCP command
if (this.dynamicOptions && typeof this.dynamicOptions == "object") {
lang.mixin(params, this.dynamicOptions);
@@ -199,6 +209,7 @@
this._widget.startup();
this.onValuesLoaded();
+ this._readyDeferred.resolve();
},
_setValueAttr: function(newVal) {
@@ -249,6 +260,13 @@
if (lang.getObject('_widget.focus', false, this)) {
this._widget.focus();
}
+ },
+
+ // ready:
+ // Returns null or a Deferred which resolves as soon as any
+ // loading activity of the widget is finished.
+ ready: function() {
+ return this._readyDeferred;
}
});
});
Index: umc/widgets/LabelPane.js
===================================================================
--- umc/widgets/LabelPane.js (Revision 36481)
+++ umc/widgets/LabelPane.js (Arbeitskopie)
@@ -31,13 +31,14 @@
define([
"dojo/_base/declare",
"dojo/_base/lang",
+ "dojo/Deferred",
"dojo/dom-class",
"dojo/dom-attr",
"dijit/_WidgetBase",
"dijit/_TemplatedMixin",
"dijit/_Container",
"umc/tools"
-], function(declare, lang, domClass, attr, _WidgetBase, _TemplatedMixin, _Container, tools) {
+], function(declare, lang, Deferred, domClass, attr, _WidgetBase, _TemplatedMixin, _Container, tools) {
lang.extend(_WidgetBase, {
// isLabelDisplayed: Boolean?
// If specified as true, LabelPane assumes that the widget itself will take
@@ -81,6 +82,18 @@
labelNodeRight: null,
+ _startupDeferred: null,
+
+ constructor: function(params) {
+ this._startupDeferred = new Deferred();
+
+ // lang._mixin() would not work sometimes, leaving this.content empty, see
+ // https://forge.univention.org/bugzilla/show_bug.cgi?id=26214#c3
+ tools.forIn(params, function(ikey, ival) {
+ this[ikey] = ival;
+ }, this);
+ },
+
postMixInProperties: function() {
this.inherited(arguments);
@@ -120,6 +133,15 @@
domClass.toggle(this.domNode, 'dijitHidden', this.content.visible === false);
},
+ startup: function() {
+ this.inherited(arguments);
+
+ this._startupDeferred.resolve();
+ if (lang.getObject('content.startup', false, this)) {
+ this.content.startup();
+ }
+ },
+
_setLabelAttr: function(label) {
if (lang.getObject('content.isLabelDisplayed', false, this)) {
// the widget displays the label itself
@@ -164,6 +186,12 @@
else if (lang.getObject('domNode', false, content) && lang.getObject('declaredClass', false, content)) {
this.contentNode.innerHTML = '';
this.addChild(content);
+ if (content.startup) {
+ this._startupDeferred.then(function() {
+ // call widget's startup after we have been started up
+ content.startup();
+ });
+ }
}
this.set( 'disabled', this.disabled );
},
Index: umc/widgets/ComplexInput.js
===================================================================
--- umc/widgets/ComplexInput.js (Revision 36481)
+++ umc/widgets/ComplexInput.js (Arbeitskopie)
@@ -57,6 +57,13 @@
umcpCommand: tools.umcpCommand,
+ _allReady: null,
+
+ constructor: function() {
+ // initialize with empty list
+ this._allReady = [];
+ },
+
buildRendering: function() {
this.inherited(arguments);
@@ -101,6 +108,7 @@
}
}, this);
+ this._updateAllReady();
},
_getValueAttr: function() {
@@ -127,6 +135,28 @@
var iisValid = areValid instanceof Array ? areValid[i] : areValid;
this._widgets[ iname ].setValid( iisValid, imessage );
}, this );
+ },
+
+ _updateAllReady: function() {
+ // wait for all widgets to be ready
+ this._allReady = [];
+ tools.forIn(this._widgets, function(iname, iwidget) {
+ this._allReady.push(iwidget.ready ? iwidget.ready() : null);
+ }, this);
+ },
+
+ ready: function() {
+ // update the internal list in order to wait until everybody is ready
+ if (!this._allReady.length) {
+ _updateAllReady();
+ }
+ var ret = all(this._allReady);
+
+ // empty list when all widgets are ready
+ ret.then(lang.hitch(this, function() {
+ this._allReady = [];
+ }));
+ return ret;
}
});
});
Index: umc/widgets/LoginDialog.js
===================================================================
--- umc/widgets/LoginDialog.js (Revision 36481)
+++ umc/widgets/LoginDialog.js (Arbeitskopie)
@@ -88,7 +88,6 @@
style: 'margin-left: auto; margin-right: auto; margin-top: 1em; width: 280px;',
content: ''
});
- this._text.placeAt(this.containerNode, 'first');
// create the language_combobox
this._languageBox = new ComboBox({
@@ -100,14 +99,6 @@
label: _('Language'),
content: this._languageBox
});
- // we need to manually startup the widgets
- this._languageBox.startup();
- this._languageLabel.startup();
- this._languageLabel.placeAt('umc_LoginDialog_FormContainer');
- // register onchange event
- this.own(this._languageBox.watch('value', function(name, oldLang, newLang) {
- i18nTools.setLanguage(newLang);
- }));
// automatically resize the DialogUnderlay container
this.own(on(win.global, 'resize', lang.hitch(this, function() {
@@ -117,6 +108,26 @@
})));
},
+ postCreate: function() {
+ this.inherited(arguments);
+
+ this._text.placeAt(this.containerNode, 'first');
+ this._languageLabel.placeAt('umc_LoginDialog_FormContainer');
+ },
+
+ startup: function() {
+ this.inherited(arguments);
+
+ // we need to manually startup the widgets
+ this._languageBox.startup();
+ this._languageLabel.startup();
+
+ // register onchange event
+ this.own(this._languageBox.watch('value', function(name, oldLang, newLang) {
+ i18nTools.setLanguage(newLang);
+ }));
+ },
+
_initForm: function() {
// wait until the iframe is completely loaded
setTimeout(lang.hitch(this, function() {
Index: tests/depends.html
===================================================================
--- tests/depends.html (Revision 36481)
+++ tests/depends.html (Arbeitskopie)
@@ -30,7 +30,7 @@
async: true
};
-
+