jenkins-bot has submitted this change and it was merged.
Change subject: [BREAKING CHANGE] Refactor for multiple surfaces
......................................................................
[BREAKING CHANGE] Refactor for multiple surfaces
Replace target.surface with target.surfaces, and introduce
the concept of an active surface at target.surface. By default
surfaces become active when focused.
Bonus:
* Move debug bar setup from target to surface.
* Move import rules to config.
Change-Id: I5c2afe424a3cc4e9973af4a657553f1634c9a7e1
---
M src/init/sa/ve.init.sa.Target.js
M src/init/ve.init.Target.js
M src/ui/ve.ui.DebugBar.js
M src/ui/ve.ui.Surface.js
M src/ui/widgets/ve.ui.SurfaceWidget.js
5 files changed, 111 insertions(+), 73 deletions(-)
Approvals:
Catrope: Looks good to me, approved
jenkins-bot: Verified
diff --git a/src/init/sa/ve.init.sa.Target.js b/src/init/sa/ve.init.sa.Target.js
index 8c01bd3..3f91d08 100644
--- a/src/init/sa/ve.init.sa.Target.js
+++ b/src/init/sa/ve.init.sa.Target.js
@@ -59,7 +59,7 @@
* @fires surfaceReady
*/
ve.init.sa.Target.prototype.setup = function ( dmDoc ) {
- var target = this;
+ var surface, target = this;
if ( this.setupDone ) {
return;
@@ -67,29 +67,23 @@
// Properties
this.setupDone = true;
- this.surface = this.createSurface( dmDoc, { excludeCommands:
this.constructor.static.excludeCommands } );
- this.$element.append( this.surface.$element );
+ surface = this.addSurface( dmDoc );
+ this.$element.append( surface.$element );
- this.setupToolbar();
- if ( ve.debug ) {
- this.setupDebugBar();
- }
+ this.setupToolbar( { classes: ['ve-init-sa-target-toolbar'] } );
// Initialization
// The following classes can be used here:
// ve-init-sa-target-mobile
// ve-init-sa-target-desktop
this.$element.addClass( 've-init-sa-target ve-init-sa-target-' +
this.surfaceType );
- this.toolbar.$element.addClass( 've-init-sa-target-toolbar' );
- this.toolbar.enableFloatable();
-
- this.toolbar.initialize();
- this.surface.setImportRules( this.constructor.static.importRules );
- this.surface.initialize();
+ this.getToolbar().enableFloatable();
+ this.getToolbar().initialize();
+ surface.initialize();
// HACK: On mobile place the context inside toolbar.$bar which floats
if ( this.surfaceType === 'mobile' ) {
- this.toolbar.$bar.append( this.surface.context.$element );
+ this.getToolbar().$bar.append( surface.context.$element );
}
// This must be emitted asynchronously because
ve.init.Platform#initialize
@@ -105,16 +99,23 @@
* @inheritdoc
*/
ve.init.sa.Target.prototype.createSurface = function ( dmDoc, config ) {
+ config = ve.extendObject( {
+ excludeCommands: this.constructor.static.excludeCommands,
+ importRules: this.constructor.static.importRules
+ }, config );
return new this.surfaceClass( dmDoc, config );
};
/**
* @inheritdoc
*/
-ve.init.sa.Target.prototype.setupToolbar = function () {
- ve.init.sa.Target.super.prototype.setupToolbar.call( this, { shadow:
true, actions: true } );
+ve.init.sa.Target.prototype.setupToolbar = function ( config ) {
+ config = ve.extendObject( { shadow: true, actions: true }, config );
- var actions = new ve.ui.TargetToolbar( this, this.surface );
+ // Parent method
+ ve.init.sa.Target.super.prototype.setupToolbar.call( this, config );
+
+ var actions = new ve.ui.TargetToolbar( this, this.getSurface() );
actions.setup( [
{
diff --git a/src/init/ve.init.Target.js b/src/init/ve.init.Target.js
index feb58a3..44f59bb 100644
--- a/src/init/ve.init.Target.js
+++ b/src/init/ve.init.Target.js
@@ -16,18 +16,18 @@
* @throws {Error} Container must be attached to the DOM
*/
ve.init.Target = function VeInitTarget( $container ) {
- // Mixin constructors
- OO.EventEmitter.call( this );
-
if ( !$.contains( $container[0].ownerDocument, $container[0] ) ) {
throw new Error( 'Container must be attached to the DOM' );
}
+ // Mixin constructors
+ OO.EventEmitter.call( this );
+
// Properties
this.$element = $container;
+ this.surfaces = [];
this.surface = null;
this.toolbar = null;
- this.debugBar = null;
// Initialization
this.$element.addClass( 've-init-target' );
@@ -44,10 +44,7 @@
* Destroy the target
*/
ve.init.Target.prototype.destroy = function () {
- if ( this.surface ) {
- this.surface.destroy();
- this.surface = null;
- }
+ this.clearSurfaces();
if ( this.toolbar ) {
this.toolbar.destroy();
this.toolbar = null;
@@ -66,7 +63,7 @@
*
* By default the surface's document is not focused. If the target wants
* the browsers' focus to be in the surface (ready for typing and cursoring)
- * call `this.surface.getView().focus()` in a handler for this event.
+ * call `surface.getView().focus()` in a handler for this event.
*
* @event surfaceReady
*/
@@ -179,11 +176,61 @@
* @returns {ve.ui.Surface}
*/
ve.init.Target.prototype.createSurface = function ( dmDoc, config ) {
+ config = ve.extendObject( {
+ excludeCommands: this.constructor.static.excludeCommands,
+ importRules: this.constructor.static.importRules
+ }, config );
return new ve.ui.DesktopSurface( dmDoc, config );
};
/**
- * Get the target's surface
+ * Add a surface to the target
+ *
+ * @param {ve.dm.Document} dmDoc Document model
+ * @param {Object} [config] Configuration options
+ * @returns {ve.ui.Surface}
+ */
+ve.init.Target.prototype.addSurface = function ( dmDoc, config ) {
+ var surface = this.createSurface( dmDoc, config );
+ this.surfaces.push( surface );
+ if ( !this.getSurface() ) {
+ this.setSurface( surface );
+ }
+ surface.getView().connect( this, { focus: this.onSurfaceViewFocus.bind(
this, surface ) } );
+ return surface;
+};
+
+/**
+ * Destroy and remove all surfaces from the target
+ */
+ve.init.Target.prototype.clearSurfaces = function () {
+ while ( this.surfaces.length ) {
+ this.surfaces.pop().destroy();
+ }
+};
+
+/**
+ * Handle focus events from a surface's view
+ *
+ * @param {ve.ui.Surface} surface Surface firing the event
+ */
+ve.init.Target.prototype.onSurfaceViewFocus = function ( surface ) {
+ this.setSurface( surface );
+};
+
+/**
+ * Set the target's active surface
+ *
+ * @param {ve.ui.Surface} surface Surface
+ */
+ve.init.Target.prototype.setSurface = function ( surface ) {
+ if ( surface !== this.surface ) {
+ this.surface = surface;
+ }
+};
+
+/**
+ * Get the target's active surface
*
* @return {ve.ui.Surface} Surface
*/
@@ -201,15 +248,6 @@
};
/**
- * Get the target's debug bar
- *
- * @return {ve.ui.DebugBar} Toolbar
- */
-ve.init.Target.prototype.getDebugBar = function () {
- return this.debugBar;
-};
-
-/**
* Set up the toolbar and insert it into the DOM.
*
* The default implementation inserts it before the surface, but subclasses
can override this.
@@ -217,24 +255,11 @@
* @param {Object} [config] Configuration options
*/
ve.init.Target.prototype.setupToolbar = function ( config ) {
- if ( !this.surface ) {
+ if ( !this.surfaces.length ) {
throw new Error( 'Surface must be setup before Toolbar' );
}
- this.toolbar = new ve.ui.TargetToolbar( this, this.surface, config );
+ this.toolbar = new ve.ui.TargetToolbar( this, this.getSurface(), config
);
this.toolbar.setup( this.constructor.static.toolbarGroups );
- this.toolbar.$element.insertBefore( this.surface.$element );
- this.toolbar.$bar.append( this.surface.toolbarDialogs.$element );
-};
-
-/**
- * Set up the debug bar and insert it into the DOM.
- *
- * The default implementation inserts it after the surface, but subclasses can
override this.
- */
-ve.init.Target.prototype.setupDebugBar = function () {
- if ( !this.surface ) {
- throw new Error( 'Surface must be setup before DebugBar' );
- }
- this.debugBar = new ve.ui.DebugBar( this.surface );
- this.debugBar.$element.insertAfter( this.surface.$element );
+ this.toolbar.$element.insertBefore( this.getSurface().$element );
+ this.toolbar.$bar.append( this.getSurface().toolbarDialogs.$element );
};
diff --git a/src/ui/ve.ui.DebugBar.js b/src/ui/ve.ui.DebugBar.js
index b2fba89..ce66d55 100644
--- a/src/ui/ve.ui.DebugBar.js
+++ b/src/ui/ve.ui.DebugBar.js
@@ -292,3 +292,11 @@
this.filibusterToggle.setLabel( 'Start Filibuster' );
}
};
+
+/**
+ * Destroy the debug bar
+ */
+ve.ui.DebugBar.prototype.destroy = function () {
+ this.getSurface().getModel().disconnect();
+ this.$element.remove();
+};
diff --git a/src/ui/ve.ui.Surface.js b/src/ui/ve.ui.Surface.js
index 0b4a816..ca3280e 100644
--- a/src/ui/ve.ui.Surface.js
+++ b/src/ui/ve.ui.Surface.js
@@ -10,11 +10,13 @@
* @class
* @abstract
* @extends OO.ui.Element
+ * @mixins OO.EventEmitter
*
* @constructor
* @param {HTMLDocument|Array|ve.dm.LinearData|ve.dm.Document} dataOrDoc
Document data to edit
* @param {Object} [config] Configuration options
* @cfg {string[]} [excludeCommands] List of commands to exclude
+ * @cfg {Object} [importRules] Import rules
*/
ve.ui.Surface = function VeUiSurface( dataOrDoc, config ) {
config = config || {};
@@ -49,12 +51,13 @@
this.commands = [];
this.commandsByTrigger = {};
this.triggers = {};
- this.importRules = {};
+ this.importRules = config.importRules || {};
this.enabled = true;
this.context = this.createContext();
this.progresses = [];
this.showProgressDebounced = ve.debounce( this.showProgress.bind( this
) );
this.filibuster = null;
+ this.debugBar = null;
this.toolbarHeight = 0;
this.toolbarDialogs = new ve.ui.ToolbarDialogWindowManager( {
@@ -107,6 +110,9 @@
this.view.destroy();
this.context.destroy();
this.dialogs.destroy();
+ if ( this.debugBar ) {
+ this.debugBar.destroy();
+ }
// Remove DOM elements
this.$element.remove();
@@ -126,6 +132,10 @@
this.getView().$element.after( this.localOverlay.$element );
// Attach globalOverlay to the global <body>, not the local frame's
<body>
$( 'body' ).append( this.globalOverlay.$element );
+
+ if ( ve.debug ) {
+ this.setupDebugBar();
+ }
// The following classes can be used here:
// ve-ui-surface-dir-ltr
@@ -158,6 +168,14 @@
*/
ve.ui.Surface.prototype.createDialogWindowManager = function () {
throw new Error( 've.ui.Surface.createDialogWindowManager must be
overridden in subclass' );
+};
+
+/**
+ * Set up the debug bar and insert it into the DOM.
+ */
+ve.ui.Surface.prototype.setupDebugBar = function () {
+ this.debugBar = new ve.ui.DebugBar( this );
+ this.debugBar.$element.insertAfter( this.$element );
};
/**
@@ -398,16 +416,6 @@
*/
ve.ui.Surface.prototype.getImportRules = function () {
return this.importRules;
-};
-
-/**
- * Set sanitization rules for rich paste
- *
- * @see ve.dm.ElementLinearData#sanitize
- * @param {Object} importRules Import rules
- */
-ve.ui.Surface.prototype.setImportRules = function ( importRules ) {
- this.importRules = importRules;
};
/**
diff --git a/src/ui/widgets/ve.ui.SurfaceWidget.js
b/src/ui/widgets/ve.ui.SurfaceWidget.js
index d2314b1..c18fd59 100644
--- a/src/ui/widgets/ve.ui.SurfaceWidget.js
+++ b/src/ui/widgets/ve.ui.SurfaceWidget.js
@@ -26,7 +26,11 @@
OO.ui.Widget.call( this, config );
// Properties
- this.surface = ve.init.target.createSurface( doc, { $: this.$,
excludeCommands: config.excludeCommands } );
+ this.surface = ve.init.target.createSurface( doc, {
+ $: this.$,
+ excludeCommands: config.excludeCommands,
+ importRules: config.importRules
+ } );
this.toolbar = new ve.ui.Toolbar( this.surface, { $: this.$ } );
// Initialization
@@ -37,14 +41,6 @@
.append( this.toolbar.$element, this.surface.$element );
if ( config.tools ) {
this.toolbar.setup( config.tools );
- }
- if ( config.importRules ) {
- this.surface.setImportRules( config.importRules );
- }
-
- if ( ve.debug ) {
- var debugBar = new ve.ui.DebugBar( this.surface, { $: this.$ }
);
- this.$element.append( debugBar.$element );
}
};
--
To view, visit https://gerrit.wikimedia.org/r/176952
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I5c2afe424a3cc4e9973af4a657553f1634c9a7e1
Gerrit-PatchSet: 9
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits