jenkins-bot has submitted this change and it was merged.
Change subject: [BREAKING CHANGE] Make content isolation optional
......................................................................
[BREAKING CHANGE] Make content isolation optional
Changes:
WindowManager.js
* No longer require manager as an argument to window, and instead set the
manager after the window is added and ready to be loaded
* Add isolate option, which configures managed windows to isolate their
contents using iframes
* Added shouldIsolate method for checking isolate config option state
* Bonus: Add preparingTo(Open|Close) promise tracking to avoid the black
hole that occurs while processing preparing promises
* Bonus: Made opening a window while another is preparing, opening or open
return a rejected promise with an error instead of waiting for it to
open, closing it, and then opening it again, which seemed evil
* Bonus: Made openWindow and closeWindow methods actually return promises
Window.js, Frame.js
* Merged OO.ui.Frame into Window.js
* Made isolation optional
* Remove border compensation from getContentHeight which was evil and broken
TextInputMenuWidget.js
* Updated use of widget.$.frame to widget.$.$frame to check if a widget is
in a parent or child frame, and access the frame's iframe element
Window.less
* Restricted font and overlay styles for dialogs to isolated windows only
WindowManager.less
* Made frames always expand to fill their parent div, making them visually
identical to their non-isolated counterparts
Window.less
* Merged in Frame.less styles
Element.js
* Switched from taking OO.ui.Frame to jQuery selections of iframe elements
as an argument for referencing the frame jQuery is bound to
ProcessDialog.js, MessageDialog.js and Dialog.js
* Updated use of this.frame.* to this.* as per merging of OO.ui.Frame and
OO.ui.Window
demos/dialog.js
* Add toggle for isolate mode
Change-Id: I88e5e7ac7f078ab52230f1d5918b90ffc2f893e8
---
M build/modules.json
M demos/dialogs.js
M jsduck.categories.json
M src/Dialog.js
M src/Element.js
D src/Frame.js
M src/Window.js
M src/WindowManager.js
M src/dialogs/MessageDialog.js
M src/dialogs/ProcessDialog.js
D src/styles/Frame.less
M src/styles/Window.less
M src/styles/WindowManager.less
D src/themes/apex/Frame.less
M src/themes/apex/Window.less
M src/widgets/TextInputMenuWidget.js
16 files changed, 452 insertions(+), 422 deletions(-)
Approvals:
Catrope: Looks good to me, approved
jenkins-bot: Verified
diff --git a/build/modules.json b/build/modules.json
index 23c4660..fddc34a 100644
--- a/build/modules.json
+++ b/build/modules.json
@@ -6,7 +6,6 @@
"src/ActionSet.js",
"src/Element.js",
- "src/Frame.js",
"src/Layout.js",
"src/Widget.js",
"src/Window.js",
@@ -87,7 +86,6 @@
],
"styles": [
"src/styles/core.less",
- "src/styles/Frame.less",
"src/styles/Toolbar.less",
"src/styles/ToolGroup.less",
"src/styles/Window.less",
@@ -138,7 +136,6 @@
"oojs-ui-apex": {
"styles": [
"src/themes/apex/Dialog.less",
- "src/themes/apex/Frame.less",
"src/themes/apex/Toolbar.less",
"src/themes/apex/ToolGroup.less",
"src/themes/apex/Window.less",
diff --git a/demos/dialogs.js b/demos/dialogs.js
index 6bb329b..f1a66a7 100644
--- a/demos/dialogs.js
+++ b/demos/dialogs.js
@@ -2,11 +2,14 @@
var i, l, name, openButton, DialogClass, config,
$demo = $( '.oo-ui-demo' ),
fieldset = new OO.ui.FieldsetLayout( { label: 'Dialogs' } ),
+ isolateSwitch = new OO.ui.ToggleSwitchWidget(),
windows = {},
- windowManager = new OO.ui.WindowManager();
+ isolatedWindows = {},
+ windowManager = new OO.ui.WindowManager(),
+ isolatedWindowManager = new OO.ui.WindowManager( { isolate:
true } );
- function SimpleDialog( manager, config ) {
- SimpleDialog.super.call( this, manager, config );
+ function SimpleDialog( config ) {
+ SimpleDialog.super.call( this, config );
}
OO.inheritClass( SimpleDialog, OO.ui.Dialog );
SimpleDialog.static.title = 'Simple dialog';
@@ -15,7 +18,7 @@
dialog = this;
SimpleDialog.super.prototype.initialize.apply( this, arguments
);
- this.content = new OO.ui.PanelLayout( { $: this.$, padded: true
} );
+ this.content = new OO.ui.PanelLayout( { $: this.$, padded:
true, expanded: false } );
this.content.$element.append( '<p>Dialog content</p>' );
closeButton = new OO.ui.ButtonWidget( {
@@ -29,9 +32,12 @@
this.content.$element.append( closeButton.$element );
this.$body.append( this.content.$element );
};
+ SimpleDialog.prototype.getBodyHeight = function () {
+ return this.content.$element.outerHeight( true );
+ };
- function ProcessDialog( manager, config ) {
- ProcessDialog.super.call( this, manager, config );
+ function ProcessDialog( config ) {
+ ProcessDialog.super.call( this, config );
}
OO.inheritClass( ProcessDialog, OO.ui.ProcessDialog );
ProcessDialog.static.title = 'Process dialog';
@@ -41,7 +47,7 @@
];
ProcessDialog.prototype.initialize = function () {
ProcessDialog.super.prototype.initialize.apply( this, arguments
);
- this.content = new OO.ui.PanelLayout( { $: this.$, padded: true
} );
+ this.content = new OO.ui.PanelLayout( { $: this.$, padded:
true, expanded: false } );
this.content.$element.append( '<p>Dialog content</p>' );
this.$body.append( this.content.$element );
};
@@ -54,9 +60,12 @@
}
return ProcessDialog.super.prototype.getActionProcess.call(
this, action );
};
+ ProcessDialog.prototype.getBodyHeight = function () {
+ return this.content.$element.outerHeight( true );
+ };
- function BrokenDialog( manager, config ) {
- BrokenDialog.super.call( this, manager, config );
+ function BrokenDialog( config ) {
+ BrokenDialog.super.call( this, config );
this.broken = false;
}
OO.inheritClass( BrokenDialog, OO.ui.ProcessDialog );
@@ -130,8 +139,8 @@
.setLabel( this.label );
};
- function BookletDialog( manager, config ) {
- BookletDialog.super.call( this, manager, config );
+ function BookletDialog( config ) {
+ BookletDialog.super.call( this, config );
}
OO.inheritClass( BookletDialog, OO.ui.ProcessDialog );
BookletDialog.static.title = 'Booklet dialog';
@@ -299,23 +308,35 @@
}
}
];
+
+ function openDialog( name, data ) {
+ if ( isolateSwitch.getValue() ) {
+ isolatedWindowManager.openWindow( name, data );
+ } else {
+ windowManager.openWindow( name, data );
+ }
+ }
+
+ fieldset.addItems( [ new OO.ui.FieldLayout( isolateSwitch, { label:
'Isolate dialogs', align: 'top' } ) ] );
for ( i = 0, l = config.length; i < l; i++ ) {
name = 'window_' + i;
DialogClass = config[i].dialogClass || SimpleDialog;
- windows[name] = new DialogClass( windowManager,
config[i].config );
+ windows[name] = new DialogClass( config[i].config );
+ isolatedWindows[name] = new DialogClass( config[i].config );
openButton = new OO.ui.ButtonWidget( {
framed: false,
icon: 'window',
label: config[i].name
} );
openButton.on(
- 'click', OO.ui.bind( windowManager.openWindow,
windowManager, name, config[i].data )
+ 'click', OO.ui.bind( openDialog, this, name,
config[i].data )
);
fieldset.addItems( [ new OO.ui.FieldLayout( openButton, {
align: 'inline' } ) ] );
}
windowManager.addWindows( windows );
+ isolatedWindowManager.addWindows( isolatedWindows );
$demo.append( $( '<div class="oo-ui-demo-container"></div>' ).append(
- fieldset.$element, windowManager.$element
+ fieldset.$element, windowManager.$element,
isolatedWindowManager.$element
) );
};
diff --git a/jsduck.categories.json b/jsduck.categories.json
index 92afb6c..7c19c1a 100644
--- a/jsduck.categories.json
+++ b/jsduck.categories.json
@@ -7,7 +7,6 @@
"classes": [
"OO.ui",
"OO.ui.Element",
- "OO.ui.Frame",
"OO.ui.Toolbar",
"OO.ui.Window",
"OO.ui.Dialog",
diff --git a/src/Dialog.js b/src/Dialog.js
index b507d41..66d7e48 100644
--- a/src/Dialog.js
+++ b/src/Dialog.js
@@ -27,9 +27,9 @@
* @constructor
* @param {Object} [config] Configuration options
*/
-OO.ui.Dialog = function OoUiDialog( manager, config ) {
+OO.ui.Dialog = function OoUiDialog( config ) {
// Parent constructor
- OO.ui.Dialog.super.call( this, manager, config );
+ OO.ui.Dialog.super.call( this, config );
// Properties
this.actions = new OO.ui.ActionSet();
@@ -102,7 +102,7 @@
*
* @param {jQuery.Event} e Key down event
*/
-OO.ui.Dialog.prototype.onFrameDocumentKeyDown = function ( e ) {
+OO.ui.Dialog.prototype.onDocumentKeyDown = function ( e ) {
if ( e.which === OO.ui.Keys.ESCAPE ) {
this.close();
return false;
@@ -234,11 +234,11 @@
// Events
if ( this.constructor.static.escapable ) {
- this.frame.$document.on( 'keydown', OO.ui.bind(
this.onFrameDocumentKeyDown, this ) );
+ this.$document.on( 'keydown', OO.ui.bind(
this.onDocumentKeyDown, this ) );
}
// Initialization
- this.frame.$content.addClass( 'oo-ui-dialog-content' );
+ this.$content.addClass( 'oo-ui-dialog-content' );
};
/**
@@ -283,7 +283,7 @@
*/
OO.ui.Dialog.prototype.pushPending = function () {
if ( this.pending === 0 ) {
- this.frame.$content.addClass(
'oo-ui-actionDialog-content-pending' );
+ this.$content.addClass( 'oo-ui-actionDialog-content-pending' );
this.$head.addClass( 'oo-ui-texture-pending' );
}
this.pending++;
@@ -300,7 +300,7 @@
*/
OO.ui.Dialog.prototype.popPending = function () {
if ( this.pending === 1 ) {
- this.frame.$content.removeClass(
'oo-ui-actionDialog-content-pending' );
+ this.$content.removeClass( 'oo-ui-actionDialog-content-pending'
);
this.$head.removeClass( 'oo-ui-texture-pending' );
}
this.pending = Math.max( 0, this.pending - 1 );
diff --git a/src/Element.js b/src/Element.js
index effc9d8..84bb2f9 100644
--- a/src/Element.js
+++ b/src/Element.js
@@ -56,18 +56,19 @@
*
* @static
* @param {jQuery|HTMLElement|HTMLDocument|Window} context Context to bind the
function to
- * @param {OO.ui.Frame} [frame] Frame of the document context
+ * @param {jQuery} [$iframe] HTML iframe element that contains the document,
omit if document is
+ * not in an iframe
* @return {Function} Bound jQuery function
*/
-OO.ui.Element.getJQuery = function ( context, frame ) {
+OO.ui.Element.getJQuery = function ( context, $iframe ) {
function wrapper( selector ) {
return $( selector, wrapper.context );
}
wrapper.context = this.getDocument( context );
- if ( frame ) {
- wrapper.frame = frame;
+ if ( $iframe ) {
+ wrapper.$iframe = $iframe;
}
return wrapper;
diff --git a/src/Frame.js b/src/Frame.js
deleted file mode 100644
index 6611006..0000000
--- a/src/Frame.js
+++ /dev/null
@@ -1,264 +0,0 @@
-/**
- * Embedded iframe with the same styles as its parent.
- *
- * @class
- * @extends OO.ui.Element
- * @mixins OO.EventEmitter
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.Frame = function OoUiFrame( config ) {
- // Parent constructor
- OO.ui.Frame.super.call( this, config );
-
- // Mixin constructors
- OO.EventEmitter.call( this );
-
- // Properties
- this.loading = null;
- this.config = config;
- this.dir = null;
-
- // Initialize
- this.$element
- .addClass( 'oo-ui-frame' )
- .attr( { frameborder: 0, scrolling: 'no' } );
-
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.Frame, OO.ui.Element );
-OO.mixinClass( OO.ui.Frame, OO.EventEmitter );
-
-/* Static Properties */
-
-/**
- * @static
- * @inheritdoc
- */
-OO.ui.Frame.static.tagName = 'iframe';
-
-/* Events */
-
-/**
- * @event load
- */
-
-/* Static Methods */
-
-/**
- * Transplant the CSS styles from as parent document to a frame's document.
- *
- * This loops over the style sheets in the parent document, and copies their
nodes to the
- * frame's document. It then polls the document to see when all styles have
loaded, and once they
- * have, resolves the promise.
- *
- * If the styles still haven't loaded after a long time (5 seconds by
default), we give up waiting
- * and resolve the promise anyway. This protects against cases like a display:
none; iframe in
- * Firefox, where the styles won't load until the iframe becomes visible.
- *
- * For details of how we arrived at the strategy used in this function, see
#load.
- *
- * @static
- * @inheritable
- * @param {HTMLDocument} parentDoc Document to transplant styles from
- * @param {HTMLDocument} frameDoc Document to transplant styles to
- * @param {number} [timeout=5000] How long to wait before giving up (in ms).
If 0, never give up.
- * @return {jQuery.Promise} Promise resolved when styles have loaded
- */
-OO.ui.Frame.static.transplantStyles = function ( parentDoc, frameDoc, timeout
) {
- var i, numSheets, styleNode, styleText, newNode, timeoutID, pollNodeId,
$pendingPollNodes,
- $pollNodes = $( [] ),
- // Fake font-family value
- fontFamily = 'oo-ui-frame-transplantStyles-loaded',
- nextIndex = parentDoc.oouiFrameTransplantStylesNextIndex || 0,
- deferred = $.Deferred();
-
- for ( i = 0, numSheets = parentDoc.styleSheets.length; i < numSheets;
i++ ) {
- styleNode = parentDoc.styleSheets[i].ownerNode;
- if ( styleNode.disabled ) {
- continue;
- }
-
- if ( styleNode.nodeName.toLowerCase() === 'link' ) {
- // External stylesheet; use @import
- styleText = '@import url(' + styleNode.href + ');';
- } else {
- // Internal stylesheet; just copy the text
- styleText = styleNode.textContent;
- }
-
- // Create a node with a unique ID that we're going to monitor
to see when the CSS
- // has loaded
- if ( styleNode.oouiFrameTransplantStylesId ) {
- // If we're nesting transplantStyles operations and
this node already has
- // a CSS rule to wait for loading, reuse it
- pollNodeId = styleNode.oouiFrameTransplantStylesId;
- } else {
- // Otherwise, create a new ID
- pollNodeId = 'oo-ui-frame-transplantStyles-loaded-' +
nextIndex;
- nextIndex++;
-
- // Add #pollNodeId { font-family: ... } to the end of
the stylesheet / after the @import
- // The font-family rule will only take effect once the
@import finishes
- styleText += '\n' + '#' + pollNodeId + ' { font-family:
' + fontFamily + '; }';
- }
-
- // Create a node with id=pollNodeId
- $pollNodes = $pollNodes.add( $( '<div>', frameDoc )
- .attr( 'id', pollNodeId )
- .appendTo( frameDoc.body )
- );
-
- // Add our modified CSS as a <style> tag
- newNode = frameDoc.createElement( 'style' );
- newNode.textContent = styleText;
- newNode.oouiFrameTransplantStylesId = pollNodeId;
- frameDoc.head.appendChild( newNode );
- }
- frameDoc.oouiFrameTransplantStylesNextIndex = nextIndex;
-
- // Poll every 100ms until all external stylesheets have loaded
- $pendingPollNodes = $pollNodes;
- timeoutID = setTimeout( function pollExternalStylesheets() {
- while (
- $pendingPollNodes.length > 0 &&
- $pendingPollNodes.eq( 0 ).css( 'font-family' ) ===
fontFamily
- ) {
- $pendingPollNodes = $pendingPollNodes.slice( 1 );
- }
-
- if ( $pendingPollNodes.length === 0 ) {
- // We're done!
- if ( timeoutID !== null ) {
- timeoutID = null;
- $pollNodes.remove();
- deferred.resolve();
- }
- } else {
- timeoutID = setTimeout( pollExternalStylesheets, 100 );
- }
- }, 100 );
- // ...but give up after a while
- if ( timeout !== 0 ) {
- setTimeout( function () {
- if ( timeoutID ) {
- clearTimeout( timeoutID );
- timeoutID = null;
- $pollNodes.remove();
- deferred.reject();
- }
- }, timeout || 5000 );
- }
-
- return deferred.promise();
-};
-
-/* Methods */
-
-/**
- * Load the frame contents.
- *
- * Once the iframe's stylesheets are loaded, the `load` event will be emitted
and the returned
- * promise will be resolved. Calling while loading will return a promise but
not trigger a new
- * loading cycle. Calling after loading is complete will return a promise
that's already been
- * resolved.
- *
- * Sounds simple right? Read on...
- *
- * When you create a dynamic iframe using open/write/close, the window.load
event for the
- * iframe is triggered when you call close, and there's no further load event
to indicate that
- * everything is actually loaded.
- *
- * In Chrome, stylesheets don't show up in document.styleSheets until they
have loaded, so we could
- * just poll that array and wait for it to have the right length. However, in
Firefox, stylesheets
- * are added to document.styleSheets immediately, and the only way you can
determine whether they've
- * loaded is to attempt to access .cssRules and wait for that to stop throwing
an exception. But
- * cross-domain stylesheets never allow .cssRules to be accessed even after
they have loaded.
- *
- * The workaround is to change all `<link href="...">` tags to `<style>@import
url(...)</style>` tags.
- * Because `@import` is blocking, Chrome won't add the stylesheet to
document.styleSheets until
- * the `@import` has finished, and Firefox won't allow .cssRules to be
accessed until the `@import`
- * has finished. And because the contents of the `<style>` tag are from the
same origin, accessing
- * .cssRules is allowed.
- *
- * However, now that we control the styles we're injecting, we might as well
do away with
- * browser-specific polling hacks like document.styleSheets and .cssRules, and
instead inject
- * `<style>@import url(...); #foo { font-family: someValue; }</style>`, then
create `<div id="foo">`
- * and wait for its font-family to change to someValue. Because `@import` is
blocking, the font-family
- * rule is not applied until after the `@import` finishes.
- *
- * All this stylesheet injection and polling magic is in #transplantStyles.
- *
- * @return {jQuery.Promise} Promise resolved when loading is complete
- * @fires load
- */
-OO.ui.Frame.prototype.load = function () {
- var win, doc,
- frame = this;
-
- // Return existing promise if already loading or loaded
- if ( this.loading ) {
- return this.loading.promise();
- }
-
- // Load the frame
- this.loading = $.Deferred();
-
- win = this.$element.prop( 'contentWindow' );
- doc = win.document;
-
- // Cache directionality
- this.dir = OO.ui.Element.getDir( this.$element ) || 'ltr';
-
- // Initialize contents
- doc.open();
- // The following classes can be used here:
- // oo-ui-ltr
- // oo-ui-rtl
- doc.write(
- '<!doctype html>' +
- '<html>' +
- '<body class="oo-ui-frame-content oo-ui-' +
this.getDir() + '" dir="' + this.getDir() + '">' +
- '</body>' +
- '</html>'
- );
- doc.close();
-
- // Properties
- this.$ = OO.ui.Element.getJQuery( doc, this );
- this.$content = this.$( '.oo-ui-frame-content' ).attr( 'tabIndex', 0 );
- this.$document = this.$( doc );
-
- // Initialization
- this.constructor.static.transplantStyles( this.getElementDocument(),
this.$document[0] )
- .always( function () {
- frame.emit( 'load' );
- frame.loading.resolve();
- } );
-
- return this.loading.promise();
-};
-
-/**
- * Set the size of the frame.
- *
- * @param {number} width Frame width in pixels
- * @param {number} height Frame height in pixels
- * @chainable
- */
-OO.ui.Frame.prototype.setSize = function ( width, height ) {
- this.$element.css( { width: width, height: height } );
- return this;
-};
-
-/**
- * Get the directionality of the frame
- *
- * @return {string} Directionality, 'ltr' or 'rtl'
- */
-OO.ui.Frame.prototype.getDir = function () {
- return this.dir;
-};
diff --git a/src/Window.js b/src/Window.js
index aa363a4..8a93fe5 100644
--- a/src/Window.js
+++ b/src/Window.js
@@ -34,15 +34,12 @@
* If the requested size is not recognized, the window manager will choose a
sensible fallback.
*
* @constructor
- * @param {OO.ui.WindowManager} manager Manager of window
* @param {Object} [config] Configuration options
* @cfg {string} [size] Symbolic name of dialog size, `small`, `medium`,
`large` or `full`; omit to
* use #static-size
* @fires initialize
*/
-OO.ui.Window = function OoUiWindow( manager, config ) {
- var win = this;
-
+OO.ui.Window = function OoUiWindow( config ) {
// Configuration initialization
config = config || {};
@@ -52,46 +49,25 @@
// Mixin constructors
OO.EventEmitter.call( this );
- if ( !( manager instanceof OO.ui.WindowManager ) ) {
- throw new Error( 'Cannot construct window: window must have a
manager' );
- }
-
// Properties
- this.manager = manager;
+ this.manager = null;
this.initialized = false;
this.visible = false;
this.opening = null;
this.closing = null;
this.opened = null;
this.timing = null;
+ this.loading = null;
this.size = config.size || this.constructor.static.size;
- this.frame = new OO.ui.Frame( { $: this.$ } );
this.$frame = this.$( '<div>' );
- this.$ = function () {
- throw new Error( 'this.$() cannot be used until the frame has
been initialized.' );
- };
// Initialization
this.$element
.addClass( 'oo-ui-window' )
- // Hide the window using visibility: hidden; while the iframe
is still loading
- // Can't use display: none; because that prevents the iframe
from loading in Firefox
- .css( 'visibility', 'hidden' )
.append( this.$frame );
- this.$frame
- .addClass( 'oo-ui-window-frame' )
- .append( this.frame.$element );
+ this.$frame.addClass( 'oo-ui-window-frame' );
- // Events
- this.frame.on( 'load', function () {
- win.initialize();
- win.initialized = true;
- // Undo the visibility: hidden; hack and apply display: none;
- // We can do this safely now that the iframe has initialized
- // (don't do this from within #initialize because it has to
happen
- // after the all subclasses have been handled as well).
- win.$element.hide().css( 'visibility', '' );
- } );
+ // NOTE: Additional intitialization will occur when #setManager is
called
};
/* Setup */
@@ -119,6 +95,116 @@
*/
OO.ui.Window.static.size = 'medium';
+/* Static Methods */
+
+/**
+ * Transplant the CSS styles from as parent document to a frame's document.
+ *
+ * This loops over the style sheets in the parent document, and copies their
nodes to the
+ * frame's document. It then polls the document to see when all styles have
loaded, and once they
+ * have, resolves the promise.
+ *
+ * If the styles still haven't loaded after a long time (5 seconds by
default), we give up waiting
+ * and resolve the promise anyway. This protects against cases like a display:
none; iframe in
+ * Firefox, where the styles won't load until the iframe becomes visible.
+ *
+ * For details of how we arrived at the strategy used in this function, see
#load.
+ *
+ * @static
+ * @inheritable
+ * @param {HTMLDocument} parentDoc Document to transplant styles from
+ * @param {HTMLDocument} frameDoc Document to transplant styles to
+ * @param {number} [timeout=5000] How long to wait before giving up (in ms).
If 0, never give up.
+ * @return {jQuery.Promise} Promise resolved when styles have loaded
+ */
+OO.ui.Window.static.transplantStyles = function ( parentDoc, frameDoc, timeout
) {
+ var i, numSheets, styleNode, styleText, newNode, timeoutID, pollNodeId,
$pendingPollNodes,
+ $pollNodes = $( [] ),
+ // Fake font-family value
+ fontFamily = 'oo-ui-frame-transplantStyles-loaded',
+ nextIndex = parentDoc.oouiFrameTransplantStylesNextIndex || 0,
+ deferred = $.Deferred();
+
+ for ( i = 0, numSheets = parentDoc.styleSheets.length; i < numSheets;
i++ ) {
+ styleNode = parentDoc.styleSheets[i].ownerNode;
+ if ( styleNode.disabled ) {
+ continue;
+ }
+
+ if ( styleNode.nodeName.toLowerCase() === 'link' ) {
+ // External stylesheet; use @import
+ styleText = '@import url(' + styleNode.href + ');';
+ } else {
+ // Internal stylesheet; just copy the text
+ styleText = styleNode.textContent;
+ }
+
+ // Create a node with a unique ID that we're going to monitor
to see when the CSS
+ // has loaded
+ if ( styleNode.oouiFrameTransplantStylesId ) {
+ // If we're nesting transplantStyles operations and
this node already has
+ // a CSS rule to wait for loading, reuse it
+ pollNodeId = styleNode.oouiFrameTransplantStylesId;
+ } else {
+ // Otherwise, create a new ID
+ pollNodeId = 'oo-ui-frame-transplantStyles-loaded-' +
nextIndex;
+ nextIndex++;
+
+ // Add #pollNodeId { font-family: ... } to the end of
the stylesheet / after the @import
+ // The font-family rule will only take effect once the
@import finishes
+ styleText += '\n' + '#' + pollNodeId + ' { font-family:
' + fontFamily + '; }';
+ }
+
+ // Create a node with id=pollNodeId
+ $pollNodes = $pollNodes.add( $( '<div>', frameDoc )
+ .attr( 'id', pollNodeId )
+ .appendTo( frameDoc.body )
+ );
+
+ // Add our modified CSS as a <style> tag
+ newNode = frameDoc.createElement( 'style' );
+ newNode.textContent = styleText;
+ newNode.oouiFrameTransplantStylesId = pollNodeId;
+ frameDoc.head.appendChild( newNode );
+ }
+ frameDoc.oouiFrameTransplantStylesNextIndex = nextIndex;
+
+ // Poll every 100ms until all external stylesheets have loaded
+ $pendingPollNodes = $pollNodes;
+ timeoutID = setTimeout( function pollExternalStylesheets() {
+ while (
+ $pendingPollNodes.length > 0 &&
+ $pendingPollNodes.eq( 0 ).css( 'font-family' ) ===
fontFamily
+ ) {
+ $pendingPollNodes = $pendingPollNodes.slice( 1 );
+ }
+
+ if ( $pendingPollNodes.length === 0 ) {
+ // We're done!
+ if ( timeoutID !== null ) {
+ timeoutID = null;
+ $pollNodes.remove();
+ deferred.resolve();
+ }
+ } else {
+ timeoutID = setTimeout( pollExternalStylesheets, 100 );
+ }
+ }, 100 );
+ // ...but give up after a while
+ if ( timeout !== 0 ) {
+ setTimeout( function () {
+ if ( timeoutID ) {
+ clearTimeout( timeoutID );
+ timeoutID = null;
+ $pollNodes.remove();
+ deferred.reject();
+ }
+ }, timeout || 5000 );
+ }
+
+ return deferred.promise();
+};
+
/* Methods */
/**
@@ -137,6 +223,24 @@
*/
OO.ui.Window.prototype.isVisible = function () {
return this.visible;
+};
+
+/**
+ * Check if window is loading.
+ *
+ * @return {boolean} Window is loading
+ */
+OO.ui.Window.prototype.isLoading = function () {
+ return this.loading && this.loading.state() === 'pending';
+};
+
+/**
+ * Check if window is loaded.
+ *
+ * @return {boolean} Window is loaded
+ */
+OO.ui.Window.prototype.isLoaded = function () {
+ return this.loading && this.loading.state() === 'resolved';
};
/**
@@ -182,15 +286,6 @@
};
/**
- * Get the window frame.
- *
- * @return {OO.ui.Frame} Frame of window
- */
-OO.ui.Window.prototype.getFrame = function () {
- return this.frame;
-};
-
-/**
* Get the window size.
*
* @return {string} Symbolic size name, e.g. 'small', 'medium', 'large', 'full'
@@ -207,8 +302,8 @@
OO.ui.Window.prototype.getContentHeight = function () {
return Math.round(
// Add buffer for border
- ( ( this.$frame.outerHeight() - this.$frame.innerHeight() ) * 2
) +
- // Height of contents
+ ( this.$frame.outerHeight() - this.$frame.innerHeight() ) +
+ // Use combined heights of children
( this.$head.outerHeight( true ) + this.getBodyHeight() +
this.$foot.outerHeight( true ) )
);
};
@@ -220,6 +315,15 @@
*/
OO.ui.Window.prototype.getBodyHeight = function () {
return this.$body[0].scrollHeight;
+};
+
+/**
+ * Get the directionality of the frame
+ *
+ * @return {string} Directionality, 'ltr' or 'rtl'
+ */
+OO.ui.Window.prototype.getDir = function () {
+ return this.dir;
};
/**
@@ -291,6 +395,76 @@
};
/**
+ * Toggle visibility of window.
+ *
+ * If the window is isolated and hasn't fully loaded yet, the visiblity
property will be used
+ * instead of display.
+ *
+ * @param {boolean} [show] Make window visible, omit to toggle visibility
+ * @fires visible
+ * @chainable
+ */
+OO.ui.Window.prototype.toggle = function ( show ) {
+ show = show === undefined ? !this.visible : !!show;
+
+ if ( show !== this.isVisible() ) {
+ this.visible = show;
+
+ if ( this.isolated && !this.isLoaded() ) {
+ // Hide the window using visibility instead of display
until loading is complete
+ // Can't use display: none; because that prevents the
iframe from loading in Firefox
+ this.$element.css( 'visibility', show ? 'visible' :
'hidden' );
+ } else {
+ this.$element.toggle( show ).css( 'visibility', '' );
+ }
+ this.emit( 'toggle', show );
+ }
+
+ return this;
+};
+
+/**
+ * Set the window manager.
+ *
+ * This must be called before initialize. Calling it more than once will cause
an error.
+ *
+ * @param {OO.ui.WindowManager} manager Manager for this window
+ * @throws {Error} If called more than once
+ * @chainable
+ */
+OO.ui.Window.prototype.setManager = function ( manager ) {
+ if ( this.manager ) {
+ throw new Error( 'Cannot set window manager, window already has
a manager' );
+ }
+
+ // Properties
+ this.manager = manager;
+ this.isolated = manager.shouldIsolate();
+
+ // Initialization
+ if ( this.isolated ) {
+ this.$iframe = this.$( '<iframe>' );
+ this.$iframe.attr( { frameborder: 0, scrolling: 'no' } );
+ this.$frame.append( this.$iframe );
+ this.$ = function () {
+ throw new Error( 'this.$() cannot be used until the
frame has been initialized.' );
+ };
+ // WARNING: Do not use this.$ again until #initialize is called
+ } else {
+ this.$content = this.$( '<div>' );
+ this.$document = $( this.getElementDocument() );
+ this.$content.addClass( 'oo-ui-window-content' );
+ this.$frame.append( this.$content );
+ }
+ this.toggle( false );
+
+ // Figure out directionality:
+ this.dir = OO.ui.Element.getDir( this.$iframe || this.$content ) ||
'ltr';
+
+ return this;
+};
+
+/**
* Set the window size.
*
* @param {string} size Symbolic size name, e.g. 'small', 'medium', 'large',
'full'
@@ -339,11 +513,15 @@
*
* Once this method is called, this.$ can be used to create elements within
the frame.
*
+ * @throws {Error} If not attached to a manager
* @chainable
*/
OO.ui.Window.prototype.initialize = function () {
+ if ( !this.manager ) {
+ throw new Error( 'Cannot initialize window, must be attached to
a manager' );
+ }
+
// Properties
- this.$ = this.frame.$;
this.$head = this.$( '<div>' );
this.$body = this.$( '<div>' );
this.$foot = this.$( '<div>' );
@@ -354,9 +532,7 @@
this.$body.addClass( 'oo-ui-window-body' );
this.$foot.addClass( 'oo-ui-window-foot' );
this.$overlay.addClass( 'oo-ui-window-overlay' );
- this.frame.$content
- .addClass( 'oo-ui-window-content' )
- .append( this.$head, this.$body, this.$foot, this.$overlay );
+ this.$content.append( this.$head, this.$body, this.$foot, this.$overlay
);
return this;
};
@@ -389,18 +565,6 @@
};
/**
- * Load window.
- *
- * This is called by OO.ui.WindowManager durring window adding, and should not
be called directly
- * by other systems.
- *
- * @return {jQuery.Promise} Promise resolved when window is loaded
- */
-OO.ui.Window.prototype.load = function () {
- return this.frame.load();
-};
-
-/**
* Setup window.
*
* This is called by OO.ui.WindowManager durring window opening, and should
not be called directly
@@ -416,10 +580,9 @@
this.$element.show();
this.visible = true;
this.getSetupProcess( data ).execute().done( function () {
- win.manager.updateWindowSize( win );
// Force redraw by asking the browser to measure the elements'
widths
win.$element.addClass( 'oo-ui-window-setup' ).width();
- win.frame.$content.addClass( 'oo-ui-window-content-setup'
).width();
+ win.$content.addClass( 'oo-ui-window-content-setup' ).width();
deferred.resolve();
} );
@@ -439,11 +602,11 @@
var win = this,
deferred = $.Deferred();
- this.frame.$content[0].focus();
+ this.$content.focus();
this.getReadyProcess( data ).execute().done( function () {
// Force redraw by asking the browser to measure the elements'
widths
win.$element.addClass( 'oo-ui-window-ready' ).width();
- win.frame.$content.addClass( 'oo-ui-window-content-ready'
).width();
+ win.$content.addClass( 'oo-ui-window-content-ready' ).width();
deferred.resolve();
} );
@@ -464,13 +627,17 @@
deferred = $.Deferred();
this.getHoldProcess( data ).execute().done( function () {
- var $focused = win.frame.$content.find( ':focus' );
- if ( $focused.length ) {
- $focused[0].blur();
+ // Get the focused element within the window's content
+ var $focus = win.$content.find( OO.ui.Element.getDocument(
win.$content ).activeElement );
+
+ // Blur the focused element
+ if ( $focus.length ) {
+ $focus[0].blur();
}
+
// Force redraw by asking the browser to measure the elements'
widths
win.$element.removeClass( 'oo-ui-window-ready' ).width();
- win.frame.$content.removeClass( 'oo-ui-window-content-ready'
).width();
+ win.$content.removeClass( 'oo-ui-window-content-ready'
).width();
deferred.resolve();
} );
@@ -493,7 +660,7 @@
this.getTeardownProcess( data ).execute().done( function () {
// Force redraw by asking the browser to measure the elements'
widths
win.$element.removeClass( 'oo-ui-window-setup' ).width();
- win.frame.$content.removeClass( 'oo-ui-window-content-setup'
).width();
+ win.$content.removeClass( 'oo-ui-window-content-setup'
).width();
win.$element.hide();
win.visible = false;
deferred.resolve();
@@ -501,3 +668,100 @@
return deferred.promise();
};
+
+/**
+ * Load the frame contents.
+ *
+ * Once the iframe's stylesheets are loaded, the `load` event will be emitted
and the returned
+ * promise will be resolved. Calling while loading will return a promise but
not trigger a new
+ * loading cycle. Calling after loading is complete will return a promise
that's already been
+ * resolved.
+ *
+ * Sounds simple right? Read on...
+ *
+ * When you create a dynamic iframe using open/write/close, the window.load
event for the
+ * iframe is triggered when you call close, and there's no further load event
to indicate that
+ * everything is actually loaded.
+ *
+ * In Chrome, stylesheets don't show up in document.styleSheets until they
have loaded, so we could
+ * just poll that array and wait for it to have the right length. However, in
Firefox, stylesheets
+ * are added to document.styleSheets immediately, and the only way you can
determine whether they've
+ * loaded is to attempt to access .cssRules and wait for that to stop throwing
an exception. But
+ * cross-domain stylesheets never allow .cssRules to be accessed even after
they have loaded.
+ *
+ * The workaround is to change all `<link href="...">` tags to `<style>@import
url(...)</style>`
+ * tags. Because `@import` is blocking, Chrome won't add the stylesheet to
document.styleSheets
+ * until the `@import` has finished, and Firefox won't allow .cssRules to be
accessed until the
+ * `@import` has finished. And because the contents of the `<style>` tag are
from the same origin,
+ * accessing .cssRules is allowed.
+ *
+ * However, now that we control the styles we're injecting, we might as well
do away with
+ * browser-specific polling hacks like document.styleSheets and .cssRules, and
instead inject
+ * `<style>@import url(...); #foo { font-family: someValue; }</style>`, then
create `<div id="foo">`
+ * and wait for its font-family to change to someValue. Because `@import` is
blocking, the
+ * font-family rule is not applied until after the `@import` finishes.
+ *
+ * All this stylesheet injection and polling magic is in #transplantStyles.
+ *
+ * @return {jQuery.Promise} Promise resolved when loading is complete
+ * @fires load
+ */
+OO.ui.Window.prototype.load = function () {
+ var sub, doc, loading,
+ win = this;
+
+ // Non-isolated windows are already "loaded"
+ if ( !this.loading && !this.isolated ) {
+ this.loading = $.Deferred().resolve();
+ this.initialize();
+ // Set initialized state after so sub-classes aren't confused
by it being set by calling
+ // their parent initialize method
+ this.initialized = true;
+ }
+
+ // Return existing promise if already loading or loaded
+ if ( this.loading ) {
+ return this.loading.promise();
+ }
+
+ // Load the frame
+ loading = this.loading = $.Deferred();
+ sub = this.$iframe.prop( 'contentWindow' );
+ doc = sub.document;
+
+ // Initialize contents
+ doc.open();
+ doc.write(
+ '<!doctype html>' +
+ '<html>' +
+ '<body class="oo-ui-window-isolated
oo-ui-window-content oo-ui-' + this.dir + '"' +
+ ' style="direction:' + this.dir + ';" dir="' +
this.dir + '">' +
+ '</body>' +
+ '</html>'
+ );
+ doc.close();
+
+ // Properties
+ this.$ = OO.ui.Element.getJQuery( doc, this.$element );
+ this.$content = this.$( '.oo-ui-window-content' ).attr( 'tabIndex', 0 );
+ this.$document = this.$( doc );
+
+ // Initialization
+ this.constructor.static.transplantStyles( this.getElementDocument(),
this.$document[0] )
+ .always( function () {
+ // Initialize isolated windows
+ win.initialize();
+ // Set initialized state after so sub-classes aren't
confused by it being set by calling
+ // their parent initialize method
+ win.initialized = true;
+ // Undo the visibility: hidden; hack and apply display:
none;
+ // We can do this safely now that the iframe has
initialized
+ // (don't do this from within #initialize because it
has to happen
+ // after the all subclasses have been handled as well).
+ win.toggle( win.isVisible() );
+
+ loading.resolve();
+ } );
+
+ return loading.promise();
+};
diff --git a/src/WindowManager.js b/src/WindowManager.js
index 43516e9..37dbb25 100644
--- a/src/WindowManager.js
+++ b/src/WindowManager.js
@@ -38,6 +38,7 @@
*
* @constructor
* @param {Object} [config] Configuration options
+ * @cfg {boolean} [isolate] Configure managed windows to isolate their content
using inline frames
* @cfg {OO.Factory} [factory] Window factory to use for automatic
instantiation
* @cfg {boolean} [modal=true] Prevent interaction outside the dialog
*/
@@ -54,10 +55,13 @@
// Properties
this.factory = config.factory;
this.modal = config.modal === undefined || !!config.modal;
+ this.isolate = !!config.isolate;
this.windows = {};
this.opening = null;
this.opened = null;
this.closing = null;
+ this.preparingToOpen = null;
+ this.preparingToClose = null;
this.size = null;
this.currentWindow = null;
this.$ariaHidden = null;
@@ -228,6 +232,17 @@
};
/**
+ * Check if window contents should be isolated.
+ *
+ * Window content isolation is done using inline frames.
+ *
+ * @return {boolean} Window contents should be isolated
+ */
+OO.ui.WindowManager.prototype.shouldIsolate = function () {
+ return this.isolate;
+};
+
+/**
* Check if a window is being managed.
*
* @param {OO.ui.Window} win Window to check
@@ -295,8 +310,7 @@
* If window is not yet instantiated, it will be instantiated and added
automatically.
*
* @param {string} name Symbolic window name
- * @return {jQuery.Promise} Promise resolved when window is ready to be
accessed; when resolved the
- * first argument is an OO.ui.Window; when rejected the first argument is an
OO.ui.Error
+ * @return {jQuery.Promise} Promise resolved with matching window, or rejected
with an OO.ui.Error
* @throws {Error} If the symbolic name is unrecognized by the factory
* @throws {Error} If the symbolic name unrecognized as a managed window
*/
@@ -312,10 +326,8 @@
) );
} else {
win = this.factory.create( name, this, { $:
this.$ } );
- this.addWindows( [ win ] ).then(
- OO.ui.bind( deferred.resolve, deferred,
win ),
- deferred.reject
- );
+ this.addWindows( [ win ] );
+ deferred.resolve( win );
}
} else {
deferred.reject( new OO.ui.Error(
@@ -364,23 +376,30 @@
opening.reject( new OO.ui.Error(
'Cannot open window: window is not attached to manager'
) );
+ } else if ( this.preparingToOpen || this.opening || this.opened ) {
+ opening.reject( new OO.ui.Error(
+ 'Cannot open window: another window is opening or open'
+ ) );
}
// Window opening
if ( opening.state() !== 'rejected' ) {
- // Begin loading the window if it's not loaded already - may
take noticable time and we want
- // too do this in paralell with any preparatory actions
- preparing.push( win.load() );
+ // Begin loading the window if it's not loading or loaded
already - may take noticable time
+ // and we want to do this in paralell with any other
preparatory actions
+ if ( !win.isLoading() && !win.isLoaded() ) {
+ // Finish initializing the window (must be done after
manager is attached to DOM)
+ win.setManager( this );
+ preparing.push( win.load() );
+ }
- if ( this.opening || this.opened ) {
- // If a window is currently opening or opened, close it
first
- preparing.push( this.closeWindow( this.currentWindow )
);
- } else if ( this.closing ) {
+ if ( this.closing ) {
// If a window is currently closing, wait for it to
complete
preparing.push( this.closing );
}
- $.when.apply( $, preparing ).done( function () {
+ this.preparingToOpen = $.when.apply( $, preparing );
+ // Ensure handlers get called after preparingToOpen is set
+ this.preparingToOpen.done( function () {
if ( manager.modal ) {
manager.$( manager.getElementDocument() ).on( {
// Prevent scrolling by keys in
top-level window
@@ -400,13 +419,15 @@
}
manager.currentWindow = win;
manager.opening = opening;
+ manager.preparingToOpen = null;
manager.emit( 'opening', win, opening, data );
- manager.updateWindowSize( win );
setTimeout( function () {
win.setup( data ).then( function () {
+ manager.updateWindowSize( win );
manager.opening.notify( { state:
'setup' } );
setTimeout( function () {
win.ready( data ).then(
function () {
+
manager.updateWindowSize( win );
manager.opening.notify(
{ state: 'ready' } );
manager.opening = null;
manager.opened =
$.Deferred();
@@ -418,7 +439,7 @@
} );
}
- return opening;
+ return opening.promise();
};
/**
@@ -453,7 +474,7 @@
closing.reject( new OO.ui.Error(
'Cannot close window: window already closed with
different data'
) );
- } else if ( this.closing ) {
+ } else if ( this.preparingToClose || this.closing ) {
closing.reject( new OO.ui.Error(
'Cannot close window: window already closing with
different data'
) );
@@ -466,9 +487,11 @@
preparing.push( this.opening );
}
- // Close the window
- $.when.apply( $, preparing ).done( function () {
+ this.preparingToClose = $.when.apply( $, preparing );
+ // Ensure handlers get called after preparingToClose is set
+ this.preparingToClose.done( function () {
manager.closing = closing;
+ manager.preparingToClose = null;
manager.emit( 'closing', win, closing, data );
manager.opened = null;
opened.resolve( closing.promise(), data );
@@ -505,23 +528,18 @@
} );
}
- return closing;
+ return closing.promise();
};
/**
* Add windows.
*
- * If the window manager is attached to the DOM then windows will be
automatically loaded as they
- * are added.
- *
* @param {Object.<string,OO.ui.Window>|OO.ui.Window[]} windows Windows to add
- * @return {jQuery.Promise} Promise resolved when all windows are added
* @throws {Error} If one of the windows being added without an explicit
symbolic name does not have
* a statically configured symbolic name
*/
OO.ui.WindowManager.prototype.addWindows = function ( windows ) {
- var i, len, win, name, list,
- promises = [];
+ var i, len, win, name, list;
if ( $.isArray( windows ) ) {
// Convert to map of windows by looking up symbolic names from
static configuration
@@ -542,13 +560,7 @@
win = list[name];
this.windows[name] = win;
this.$element.append( win.$element );
-
- if ( this.isElementAttached() ) {
- promises.push( win.load() );
- }
}
-
- return $.when.apply( $, promises );
};
/**
diff --git a/src/dialogs/MessageDialog.js b/src/dialogs/MessageDialog.js
index 964732c..1a1814c 100644
--- a/src/dialogs/MessageDialog.js
+++ b/src/dialogs/MessageDialog.js
@@ -11,9 +11,9 @@
* @constructor
* @param {Object} [config] Configuration options
*/
-OO.ui.MessageDialog = function OoUiMessageDialog( manager, config ) {
+OO.ui.MessageDialog = function OoUiMessageDialog( config ) {
// Parent constructor
- OO.ui.MessageDialog.super.call( this, manager, config );
+ OO.ui.MessageDialog.super.call( this, config );
// Properties
this.verticalActionLayout = null;
@@ -159,7 +159,7 @@
// Initialization
this.title.$element.addClass( 'oo-ui-messageDialog-title' );
- this.frame.$content.addClass( 'oo-ui-messageDialog-content' );
+ this.$content.addClass( 'oo-ui-messageDialog-content' );
this.container.$element.append( this.text.$element );
this.text.$element.append( this.title.$element, this.message.$element );
this.$body.append( this.container.$element );
diff --git a/src/dialogs/ProcessDialog.js b/src/dialogs/ProcessDialog.js
index 2e0dd27..18a1ee2 100644
--- a/src/dialogs/ProcessDialog.js
+++ b/src/dialogs/ProcessDialog.js
@@ -24,9 +24,9 @@
* @constructor
* @param {Object} [config] Configuration options
*/
-OO.ui.ProcessDialog = function OoUiProcessDialog( manager, config ) {
+OO.ui.ProcessDialog = function OoUiProcessDialog( config ) {
// Parent constructor
- OO.ui.ProcessDialog.super.call( this, manager, config );
+ OO.ui.ProcessDialog.super.call( this, config );
// Initialization
this.$element.addClass( 'oo-ui-processDialog' );
@@ -109,7 +109,7 @@
this.$errors
.addClass( 'oo-ui-processDialog-errors' )
.append( this.$errorsTitle, this.dismissButton.$element,
this.retryButton.$element );
- this.frame.$content
+ this.$content
.addClass( 'oo-ui-processDialog-content' )
.append( this.$errors );
this.$navigation
diff --git a/src/styles/Frame.less b/src/styles/Frame.less
deleted file mode 100644
index dc0525c..0000000
--- a/src/styles/Frame.less
+++ /dev/null
@@ -1,14 +0,0 @@
-.oo-ui-frame {
- margin: 0;
- padding: 0;
-
- &-content {
- margin: 0;
- padding: 0;
- }
-
- /* Content div takes focus when opened, so hide outline */
- &-content:focus {
- outline: none;
- }
-}
diff --git a/src/styles/Window.less b/src/styles/Window.less
index ca384f8..3eaee52 100644
--- a/src/styles/Window.less
+++ b/src/styles/Window.less
@@ -3,13 +3,20 @@
.oo-ui-window {
line-height: 1em;
- & > &-frame {
+ &-frame {
.oo-ui-box-sizing(border-box);
- > .oo-ui-frame {
+ > iframe {
width: 100%;
height: 100%;
+ margin: 0;
+ padding: 0;
}
+ }
+
+ /* Content div takes focus when opened, so hide outline */
+ &-content:focus {
+ outline: none;
}
&-head,
@@ -17,6 +24,12 @@
.oo-ui-unselectable();
}
+ &-body {
+ margin: 0;
+ padding: 0;
+ background: none;
+ }
+
&-overlay {
position: absolute;
top: 0;
diff --git a/src/styles/WindowManager.less b/src/styles/WindowManager.less
index 1232a63..dc625d3 100644
--- a/src/styles/WindowManager.less
+++ b/src/styles/WindowManager.less
@@ -22,6 +22,11 @@
overflow: hidden;
max-width: 100%;
max-height: 100%;
+
+ > iframe {
+ width: 100%;
+ height: 100%;
+ }
}
}
}
diff --git a/src/themes/apex/Frame.less b/src/themes/apex/Frame.less
deleted file mode 100644
index fb19a77..0000000
--- a/src/themes/apex/Frame.less
+++ /dev/null
@@ -1,6 +0,0 @@
-.oo-ui-frame {
- &-content {
- font-family: sans-serif;
- font-size: 0.8em;
- }
-}
diff --git a/src/themes/apex/Window.less b/src/themes/apex/Window.less
index cc6866d..21544f9 100644
--- a/src/themes/apex/Window.less
+++ b/src/themes/apex/Window.less
@@ -1,11 +1,13 @@
@import '../../styles/mixins';
.oo-ui-window {
- &-content {
+ &-isolated&-content {
+ font-family: sans-serif;
+ font-size: 0.8em;
background: transparent;
}
- &-overlay {
+ &-isolated&-overlay {
font-family: sans-serif;
line-height: 1.5em;
font-size: 1em;
diff --git a/src/widgets/TextInputMenuWidget.js
b/src/widgets/TextInputMenuWidget.js
index 42e1ba4..d8e2299 100644
--- a/src/widgets/TextInputMenuWidget.js
+++ b/src/widgets/TextInputMenuWidget.js
@@ -77,9 +77,9 @@
dimensions.top += $container.height();
// Compensate for frame position if in a differnt frame
- if ( this.input.$.frame && this.input.$.context !==
this.$element[0].ownerDocument ) {
+ if ( this.input.$.$iframe && this.input.$.context !==
this.$element[0].ownerDocument ) {
frameOffset = OO.ui.Element.getRelativePosition(
- this.input.$.frame.$element,
this.$element.offsetParent()
+ this.input.$.$iframe, this.$element.offsetParent()
);
dimensions.left += frameOffset.left;
dimensions.top += frameOffset.top;
--
To view, visit https://gerrit.wikimedia.org/r/148761
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I88e5e7ac7f078ab52230f1d5918b90ffc2f893e8
Gerrit-PatchSet: 21
Gerrit-Project: oojs/ui
Gerrit-Branch: master
Gerrit-Owner: Trevor Parscal <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Esanders <[email protected]>
Gerrit-Reviewer: Jforrester <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits