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

Reply via email to