MDL-56364 javascript: Prevent bad prototype chains in core dialogue
The DIALOGUE object was using Y.clone to copy the input config, messing up the prototype chains for any objects in the config param. Really, this doesn't need to clone the config, so I've refactored to avoid doing so. Also moved some of the other attribute init and setup calls to the appropriate places (initializer and modifyAttrs).
This commit is contained in:
+86
-31
@@ -55,10 +55,14 @@ var DIALOGUE_NAME = 'Moodle dialogue',
|
||||
* @class M.core.dialogue
|
||||
* @extends Panel
|
||||
*/
|
||||
DIALOGUE = function(c) {
|
||||
var config = Y.clone(c);
|
||||
config.COUNT = Y.stamp(this);
|
||||
var id = 'moodle-dialogue-' + config.COUNT;
|
||||
DIALOGUE = function(config) {
|
||||
// The code below is a hack to add the custom content node to the DOM, on the fly, per-instantiation and to assign the value
|
||||
// of 'srcNode' to this newly created node. Normally (see docs: https://yuilibrary.com/yui/docs/widget/widget-extend.html),
|
||||
// this node would be pre-existing in the DOM, and an id string would simply be passed in as a property of the config object
|
||||
// during widget instantiation, however, because we're creating it on the fly (and 'config.srcNode' isn't set yet), care must
|
||||
// be taken to add it to the DOM and to properly set the value of 'config.srcNode' before calling the parent constructor.
|
||||
// Note: additional classes can be added to this content node by setting the 'additionalBaseClass' config property (a string).
|
||||
var id = 'moodle-dialogue-' + Y.stamp(this); // Can't use this.get('id') as it's not set at this stage.
|
||||
config.notificationBase =
|
||||
Y.Node.create('<div class="' + CSS.BASE + '">')
|
||||
.append(Y.Node.create('<div id="' + id + '" role="dialog" ' +
|
||||
@@ -67,33 +71,8 @@ DIALOGUE = function(c) {
|
||||
.append(Y.Node.create('<div class="' + CSS.BODY + ' yui3-widget-bd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS.FOOTER + ' yui3-widget-ft"></div>')));
|
||||
Y.one(document.body).append(config.notificationBase);
|
||||
|
||||
if (config.additionalBaseClass) {
|
||||
config.notificationBase.addClass(config.additionalBaseClass);
|
||||
}
|
||||
|
||||
config.srcNode = '#' + id;
|
||||
|
||||
// closeButton param to keep the stable versions API.
|
||||
if (config.closeButton === false) {
|
||||
config.buttons = null;
|
||||
} else {
|
||||
config.buttons = [
|
||||
{
|
||||
section: Y.WidgetStdMod.HEADER,
|
||||
classNames: 'closebutton',
|
||||
action: function() {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
DIALOGUE.superclass.constructor.apply(this, [config]);
|
||||
|
||||
if (config.closeButton !== false) {
|
||||
// The buttons constructor does not allow custom attributes
|
||||
this.get('buttons').header[0].setAttribute('title', this.get('closeButtonTitle'));
|
||||
}
|
||||
};
|
||||
Y.extend(DIALOGUE, Y.Panel, {
|
||||
// Window resize event listener.
|
||||
@@ -130,6 +109,11 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
initializer: function() {
|
||||
var bb;
|
||||
|
||||
if (this.get('closeButton') !== false) {
|
||||
// The buttons constructor does not allow custom attributes
|
||||
this.get('buttons').header[0].setAttribute('title', this.get('closeButtonTitle'));
|
||||
}
|
||||
|
||||
// Initialise the element cache.
|
||||
this._hiddenSiblings = [];
|
||||
|
||||
@@ -181,6 +165,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Add any additional classes to the content node if required.
|
||||
var nBase = this.get('notificationBase');
|
||||
var additionalClasses = this.get('additionalBaseClass');
|
||||
if (additionalClasses !== '') {
|
||||
nBase.addClass(additionalClasses);
|
||||
}
|
||||
|
||||
// Remove the dialogue from the DOM when it is destroyed.
|
||||
this.after('destroyedChange', function() {
|
||||
this.get(BASE).remove(true);
|
||||
@@ -536,6 +527,23 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
NAME: DIALOGUE_NAME,
|
||||
CSS_PREFIX: DIALOGUE_PREFIX,
|
||||
ATTRS: {
|
||||
/**
|
||||
* Any additional classes to add to the base Node.
|
||||
*
|
||||
* @attribute additionalBaseClass
|
||||
* @type String
|
||||
* @default ''
|
||||
*/
|
||||
additionalBaseClass: {
|
||||
value: ''
|
||||
},
|
||||
|
||||
/**
|
||||
* The Notification base Node.
|
||||
*
|
||||
* @attribute notificationBase
|
||||
* @type Node
|
||||
*/
|
||||
notificationBase: {
|
||||
|
||||
},
|
||||
@@ -616,9 +624,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
* @attribute COUNT
|
||||
* @type String
|
||||
* @default null
|
||||
* @writeonce
|
||||
*/
|
||||
COUNT: {
|
||||
value: null
|
||||
writeOnce: true,
|
||||
valueFn: function() {
|
||||
return Y.stamp(this);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -656,7 +668,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
focusOnShowSelector: {
|
||||
value: null
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -741,6 +752,50 @@ Y.Base.modifyAttrs(DIALOGUE, {
|
||||
*/
|
||||
extraClasses: {
|
||||
value: []
|
||||
},
|
||||
|
||||
/**
|
||||
* Identifier for the widget.
|
||||
*
|
||||
* @attribute id
|
||||
* @type String
|
||||
* @default a product of guid().
|
||||
* @writeOnce
|
||||
*/
|
||||
id: {
|
||||
writeOnce: true,
|
||||
valueFn: function() {
|
||||
var id = 'moodle-dialogue-' + Y.stamp(this);
|
||||
return id;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Collection containing the widget's buttons.
|
||||
*
|
||||
* @attribute buttons
|
||||
* @type Object
|
||||
* @default {}
|
||||
*/
|
||||
buttons: {
|
||||
// Readonly is really important. We don't want to allow users of the plugin to pass in buttons. closeButton handles this.
|
||||
readOnly: true,
|
||||
getter: Y.WidgetButtons.prototype._getButtons,
|
||||
valueFn: function() {
|
||||
if (this.get('closeButton') === false) {
|
||||
return null;
|
||||
} else {
|
||||
return [
|
||||
{
|
||||
section: Y.WidgetStdMod.HEADER,
|
||||
classNames: 'closebutton',
|
||||
action: function() {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+86
-31
@@ -55,10 +55,14 @@ var DIALOGUE_NAME = 'Moodle dialogue',
|
||||
* @class M.core.dialogue
|
||||
* @extends Panel
|
||||
*/
|
||||
DIALOGUE = function(c) {
|
||||
var config = Y.clone(c);
|
||||
config.COUNT = Y.stamp(this);
|
||||
var id = 'moodle-dialogue-' + config.COUNT;
|
||||
DIALOGUE = function(config) {
|
||||
// The code below is a hack to add the custom content node to the DOM, on the fly, per-instantiation and to assign the value
|
||||
// of 'srcNode' to this newly created node. Normally (see docs: https://yuilibrary.com/yui/docs/widget/widget-extend.html),
|
||||
// this node would be pre-existing in the DOM, and an id string would simply be passed in as a property of the config object
|
||||
// during widget instantiation, however, because we're creating it on the fly (and 'config.srcNode' isn't set yet), care must
|
||||
// be taken to add it to the DOM and to properly set the value of 'config.srcNode' before calling the parent constructor.
|
||||
// Note: additional classes can be added to this content node by setting the 'additionalBaseClass' config property (a string).
|
||||
var id = 'moodle-dialogue-' + Y.stamp(this); // Can't use this.get('id') as it's not set at this stage.
|
||||
config.notificationBase =
|
||||
Y.Node.create('<div class="' + CSS.BASE + '">')
|
||||
.append(Y.Node.create('<div id="' + id + '" role="dialog" ' +
|
||||
@@ -67,33 +71,8 @@ DIALOGUE = function(c) {
|
||||
.append(Y.Node.create('<div class="' + CSS.BODY + ' yui3-widget-bd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS.FOOTER + ' yui3-widget-ft"></div>')));
|
||||
Y.one(document.body).append(config.notificationBase);
|
||||
|
||||
if (config.additionalBaseClass) {
|
||||
config.notificationBase.addClass(config.additionalBaseClass);
|
||||
}
|
||||
|
||||
config.srcNode = '#' + id;
|
||||
|
||||
// closeButton param to keep the stable versions API.
|
||||
if (config.closeButton === false) {
|
||||
config.buttons = null;
|
||||
} else {
|
||||
config.buttons = [
|
||||
{
|
||||
section: Y.WidgetStdMod.HEADER,
|
||||
classNames: 'closebutton',
|
||||
action: function() {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
DIALOGUE.superclass.constructor.apply(this, [config]);
|
||||
|
||||
if (config.closeButton !== false) {
|
||||
// The buttons constructor does not allow custom attributes
|
||||
this.get('buttons').header[0].setAttribute('title', this.get('closeButtonTitle'));
|
||||
}
|
||||
};
|
||||
Y.extend(DIALOGUE, Y.Panel, {
|
||||
// Window resize event listener.
|
||||
@@ -130,6 +109,11 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
initializer: function() {
|
||||
var bb;
|
||||
|
||||
if (this.get('closeButton') !== false) {
|
||||
// The buttons constructor does not allow custom attributes
|
||||
this.get('buttons').header[0].setAttribute('title', this.get('closeButtonTitle'));
|
||||
}
|
||||
|
||||
// Initialise the element cache.
|
||||
this._hiddenSiblings = [];
|
||||
|
||||
@@ -181,6 +165,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Add any additional classes to the content node if required.
|
||||
var nBase = this.get('notificationBase');
|
||||
var additionalClasses = this.get('additionalBaseClass');
|
||||
if (additionalClasses !== '') {
|
||||
nBase.addClass(additionalClasses);
|
||||
}
|
||||
|
||||
// Remove the dialogue from the DOM when it is destroyed.
|
||||
this.after('destroyedChange', function() {
|
||||
this.get(BASE).remove(true);
|
||||
@@ -536,6 +527,23 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
NAME: DIALOGUE_NAME,
|
||||
CSS_PREFIX: DIALOGUE_PREFIX,
|
||||
ATTRS: {
|
||||
/**
|
||||
* Any additional classes to add to the base Node.
|
||||
*
|
||||
* @attribute additionalBaseClass
|
||||
* @type String
|
||||
* @default ''
|
||||
*/
|
||||
additionalBaseClass: {
|
||||
value: ''
|
||||
},
|
||||
|
||||
/**
|
||||
* The Notification base Node.
|
||||
*
|
||||
* @attribute notificationBase
|
||||
* @type Node
|
||||
*/
|
||||
notificationBase: {
|
||||
|
||||
},
|
||||
@@ -613,9 +621,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
* @attribute COUNT
|
||||
* @type String
|
||||
* @default null
|
||||
* @writeonce
|
||||
*/
|
||||
COUNT: {
|
||||
value: null
|
||||
writeOnce: true,
|
||||
valueFn: function() {
|
||||
return Y.stamp(this);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -653,7 +665,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
focusOnShowSelector: {
|
||||
value: null
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -738,6 +749,50 @@ Y.Base.modifyAttrs(DIALOGUE, {
|
||||
*/
|
||||
extraClasses: {
|
||||
value: []
|
||||
},
|
||||
|
||||
/**
|
||||
* Identifier for the widget.
|
||||
*
|
||||
* @attribute id
|
||||
* @type String
|
||||
* @default a product of guid().
|
||||
* @writeOnce
|
||||
*/
|
||||
id: {
|
||||
writeOnce: true,
|
||||
valueFn: function() {
|
||||
var id = 'moodle-dialogue-' + Y.stamp(this);
|
||||
return id;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Collection containing the widget's buttons.
|
||||
*
|
||||
* @attribute buttons
|
||||
* @type Object
|
||||
* @default {}
|
||||
*/
|
||||
buttons: {
|
||||
// Readonly is really important. We don't want to allow users of the plugin to pass in buttons. closeButton handles this.
|
||||
readOnly: true,
|
||||
getter: Y.WidgetButtons.prototype._getButtons,
|
||||
valueFn: function() {
|
||||
if (this.get('closeButton') === false) {
|
||||
return null;
|
||||
} else {
|
||||
return [
|
||||
{
|
||||
section: Y.WidgetStdMod.HEADER,
|
||||
classNames: 'closebutton',
|
||||
action: function() {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+86
-31
@@ -25,10 +25,14 @@ var DIALOGUE_NAME = 'Moodle dialogue',
|
||||
* @class M.core.dialogue
|
||||
* @extends Panel
|
||||
*/
|
||||
DIALOGUE = function(c) {
|
||||
var config = Y.clone(c);
|
||||
config.COUNT = Y.stamp(this);
|
||||
var id = 'moodle-dialogue-' + config.COUNT;
|
||||
DIALOGUE = function(config) {
|
||||
// The code below is a hack to add the custom content node to the DOM, on the fly, per-instantiation and to assign the value
|
||||
// of 'srcNode' to this newly created node. Normally (see docs: https://yuilibrary.com/yui/docs/widget/widget-extend.html),
|
||||
// this node would be pre-existing in the DOM, and an id string would simply be passed in as a property of the config object
|
||||
// during widget instantiation, however, because we're creating it on the fly (and 'config.srcNode' isn't set yet), care must
|
||||
// be taken to add it to the DOM and to properly set the value of 'config.srcNode' before calling the parent constructor.
|
||||
// Note: additional classes can be added to this content node by setting the 'additionalBaseClass' config property (a string).
|
||||
var id = 'moodle-dialogue-' + Y.stamp(this); // Can't use this.get('id') as it's not set at this stage.
|
||||
config.notificationBase =
|
||||
Y.Node.create('<div class="' + CSS.BASE + '">')
|
||||
.append(Y.Node.create('<div id="' + id + '" role="dialog" ' +
|
||||
@@ -37,33 +41,8 @@ DIALOGUE = function(c) {
|
||||
.append(Y.Node.create('<div class="' + CSS.BODY + ' yui3-widget-bd"></div>'))
|
||||
.append(Y.Node.create('<div class="' + CSS.FOOTER + ' yui3-widget-ft"></div>')));
|
||||
Y.one(document.body).append(config.notificationBase);
|
||||
|
||||
if (config.additionalBaseClass) {
|
||||
config.notificationBase.addClass(config.additionalBaseClass);
|
||||
}
|
||||
|
||||
config.srcNode = '#' + id;
|
||||
|
||||
// closeButton param to keep the stable versions API.
|
||||
if (config.closeButton === false) {
|
||||
config.buttons = null;
|
||||
} else {
|
||||
config.buttons = [
|
||||
{
|
||||
section: Y.WidgetStdMod.HEADER,
|
||||
classNames: 'closebutton',
|
||||
action: function() {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
DIALOGUE.superclass.constructor.apply(this, [config]);
|
||||
|
||||
if (config.closeButton !== false) {
|
||||
// The buttons constructor does not allow custom attributes
|
||||
this.get('buttons').header[0].setAttribute('title', this.get('closeButtonTitle'));
|
||||
}
|
||||
};
|
||||
Y.extend(DIALOGUE, Y.Panel, {
|
||||
// Window resize event listener.
|
||||
@@ -100,6 +79,11 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
initializer: function() {
|
||||
var bb;
|
||||
|
||||
if (this.get('closeButton') !== false) {
|
||||
// The buttons constructor does not allow custom attributes
|
||||
this.get('buttons').header[0].setAttribute('title', this.get('closeButtonTitle'));
|
||||
}
|
||||
|
||||
// Initialise the element cache.
|
||||
this._hiddenSiblings = [];
|
||||
|
||||
@@ -151,6 +135,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Add any additional classes to the content node if required.
|
||||
var nBase = this.get('notificationBase');
|
||||
var additionalClasses = this.get('additionalBaseClass');
|
||||
if (additionalClasses !== '') {
|
||||
nBase.addClass(additionalClasses);
|
||||
}
|
||||
|
||||
// Remove the dialogue from the DOM when it is destroyed.
|
||||
this.after('destroyedChange', function() {
|
||||
this.get(BASE).remove(true);
|
||||
@@ -506,6 +497,23 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
NAME: DIALOGUE_NAME,
|
||||
CSS_PREFIX: DIALOGUE_PREFIX,
|
||||
ATTRS: {
|
||||
/**
|
||||
* Any additional classes to add to the base Node.
|
||||
*
|
||||
* @attribute additionalBaseClass
|
||||
* @type String
|
||||
* @default ''
|
||||
*/
|
||||
additionalBaseClass: {
|
||||
value: ''
|
||||
},
|
||||
|
||||
/**
|
||||
* The Notification base Node.
|
||||
*
|
||||
* @attribute notificationBase
|
||||
* @type Node
|
||||
*/
|
||||
notificationBase: {
|
||||
|
||||
},
|
||||
@@ -586,9 +594,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
* @attribute COUNT
|
||||
* @type String
|
||||
* @default null
|
||||
* @writeonce
|
||||
*/
|
||||
COUNT: {
|
||||
value: null
|
||||
writeOnce: true,
|
||||
valueFn: function() {
|
||||
return Y.stamp(this);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -626,7 +638,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
focusOnShowSelector: {
|
||||
value: null
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -711,6 +722,50 @@ Y.Base.modifyAttrs(DIALOGUE, {
|
||||
*/
|
||||
extraClasses: {
|
||||
value: []
|
||||
},
|
||||
|
||||
/**
|
||||
* Identifier for the widget.
|
||||
*
|
||||
* @attribute id
|
||||
* @type String
|
||||
* @default a product of guid().
|
||||
* @writeOnce
|
||||
*/
|
||||
id: {
|
||||
writeOnce: true,
|
||||
valueFn: function() {
|
||||
var id = 'moodle-dialogue-' + Y.stamp(this);
|
||||
return id;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Collection containing the widget's buttons.
|
||||
*
|
||||
* @attribute buttons
|
||||
* @type Object
|
||||
* @default {}
|
||||
*/
|
||||
buttons: {
|
||||
// Readonly is really important. We don't want to allow users of the plugin to pass in buttons. closeButton handles this.
|
||||
readOnly: true,
|
||||
getter: Y.WidgetButtons.prototype._getButtons,
|
||||
valueFn: function() {
|
||||
if (this.get('closeButton') === false) {
|
||||
return null;
|
||||
} else {
|
||||
return [
|
||||
{
|
||||
section: Y.WidgetStdMod.HEADER,
|
||||
classNames: 'closebutton',
|
||||
action: function() {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user