Esanders has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/177109

Change subject: [BREAKING CHANGE] Multiple surface support and demo
......................................................................

[BREAKING CHANGE] Multiple surface support and demo

Change-Id: Ic999ec166d8005e0368c0dc95c0a5593e6bf2945
---
M demos/ve/demo.css
M demos/ve/demo.js
M src/init/sa/ve.init.sa.Target.js
M src/init/ve.init.Target.js
M src/ui/ve.ui.TargetToolbar.js
M src/ui/ve.ui.Toolbar.js
6 files changed, 118 insertions(+), 131 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor 
refs/changes/09/177109/1

diff --git a/demos/ve/demo.css b/demos/ve/demo.css
index 762714f..c79d00f 100644
--- a/demos/ve/demo.css
+++ b/demos/ve/demo.css
@@ -93,6 +93,7 @@
 
 .ve-ui-debugBar {
        padding: 1.5em;
+       margin-bottom: 1em;
 }
 
 .ve-ui-debugBar-filibuster {
diff --git a/demos/ve/demo.js b/demos/ve/demo.js
index 363d1c9..2497fe4 100644
--- a/demos/ve/demo.js
+++ b/demos/ve/demo.js
@@ -20,19 +20,18 @@
                        return items;
                }
 
-               var currentTarget,
-                       initialPage,
+               var initialPage,
 
                        lastMode = null,
 
                        $menu = $( '.ve-demo-menu' ),
                        $editor = $( '.ve-demo-editor' ),
-                       $targetContainer = $( '<div>' ),
+                       target = new ve.init.sa.Target( $( '<div>' ).appendTo( 
$editor ) ),
 
                        switching = false,
 
                        currentLang = $.i18n().locale,
-                       currentDir = $targetContainer.css( 'direction' ) || 
'ltr',
+                       currentDir = target.$element.css( 'direction' ) || 
'ltr',
 
                        // Menu widgets
                        pageDropdown = new OO.ui.DropdownWidget( {
@@ -44,6 +43,7 @@
                                { label: 'Page', input: pageDropdown }
                        ),
                        pageMenu = pageDropdown.getMenu(),
+                       addSurfaceButton = new OO.ui.ButtonWidget( { icon: 
'add' } ),
 
                        modeSelect = new OO.ui.ButtonSelectWidget().addItems( [
                                new OO.ui.ButtonOptionWidget( { data: 've', 
label: 'VE' } ),
@@ -70,6 +70,10 @@
                                history.replaceState( null, document.title, 
'#!/src/' + page );
                        }
                        switchPage( 've', page );
+               } );
+
+               addSurfaceButton.on( 'click', function () {
+                       addSurface( '' );
                } );
 
                languageInput.setLangAndDir( currentLang, currentDir );
@@ -114,9 +118,9 @@
 
                        switch ( lastMode ) {
                                case 've':
-                                       closePromise = 
$targetContainer.slideUp().promise();
+                                       closePromise = 
target.getSurface().$element.slideUp().promise();
                                        if ( !page ) {
-                                               model = 
currentTarget.getSurface().getModel().getDocument() ;
+                                               model = 
target.getSurface().getModel().getDocument() ;
                                                doc = 
ve.dm.converter.getDomFromModel( model );
                                                html = ve.properInnerHtml( 
doc.body );
                                                currentDir = model.getDir();
@@ -159,7 +163,7 @@
 
                                                
sourceTextInput.$element.slideDown();
                                                if ( ve.debug ) {
-                                                       
currentTarget.debugBar.$element.remove();
+                                                       
target.debugBar.$element.remove();
                                                }
                                                break;
 
@@ -177,6 +181,7 @@
                        $( '<div>' ).addClass( 've-demo-menu-commands' ).append(
                                pageLabel.$element,
                                pageDropdown.$element,
+                               addSurfaceButton.$element,
                                $( '<span 
class="ve-demo-menu-divider">&nbsp;</span>' ),
                                modeSelect.$element,
                                $( '<span 
class="ve-demo-menu-divider">&nbsp;</span>' ),
@@ -184,7 +189,7 @@
                        )
                );
 
-               $editor.append( $targetContainer, 
sourceTextInput.$element.hide(), $readView );
+               $editor.append( target.$element, 
sourceTextInput.$element.hide(), $readView );
 
                /**
                 * Load a page into the editor
@@ -198,69 +203,56 @@
                                currentDir = src.match( /rtl\.html$/ ) ? 'rtl' 
: 'ltr';
                        }
 
-                       $targetContainer.slideUp().promise().done( function () {
-                               $.ajax( {
-                                       url: src,
-                                       dataType: 'text'
-                               } ).always( function ( result, status ) {
-                                       var pageHtml;
+                       ve.init.platform.getInitializedPromise().done( function 
() {
+                               var promise = target.getSurface() ?
+                                       
target.getSurface().$element.slideUp().promise() :
+                                       $.Deferred().resolve().promise();
 
-                                       if ( status === 'error' ) {
-                                               pageHtml = '<p><i>Failed 
loading page ' + $( '<span>' ).text( src ).html() + '</i></p>';
-                                       } else {
-                                               pageHtml = result;
-                                       }
+                               promise.done( function () {
+                                       $.ajax( {
+                                               url: src,
+                                               dataType: 'text'
+                                       } ).always( function ( result, status ) 
{
+                                               var pageHtml;
 
-                                       loadTarget( pageHtml );
+                                               if ( status === 'error' ) {
+                                                       pageHtml = 
'<p><i>Failed loading page ' + $( '<span>' ).text( src ).html() + '</i></p>';
+                                               } else {
+                                                       pageHtml = result;
+                                               }
+
+                                               loadTarget( pageHtml );
+                                       } );
                                } );
                        } );
                }
 
                function loadTarget( pageHtml ) {
-                       if ( currentTarget ) {
-                               currentTarget.destroy();
+                       while ( target.surfaces.length ) {
+                               target.surfaces.pop().destroy();
                        }
 
-                       var $container = $( '<div>' ),
-                               oldDir = currentDir === 'ltr' ? 'rtl' : 'ltr';
+                       var oldDir = currentDir === 'ltr' ? 'rtl' : 'ltr';
 
                        $( '.stylesheet-' + currentDir ).prop( 'disabled', 
false );
                        $( '.stylesheet-' + oldDir ).prop( 'disabled', true );
 
-                       // Container needs to be visually hidden, but not 
display:none
-                       // so that the toolbar can be measured
-                       $targetContainer.empty().show().css( {
-                               height: 0,
-                               overflow: 'hidden'
-                       } );
+                       target.$element.css( 'direction', currentDir );
 
-                       $targetContainer.css( 'direction', currentDir );
+                       addSurface( pageHtml );
+               }
 
-                       // The container must be attached to the DOM before
-                       // the target is initialised
-                       $targetContainer.append( $container );
-
-                       $targetContainer.show();
-                       currentTarget = new ve.init.sa.Target(
-                               $container,
+               function addSurface( html ) {
+                       var surface = target.addSurface(
                                ve.dm.converter.getModelFromDom(
-                                       ve.createDocumentFromHtml( pageHtml ),
-                                       $targetContainer.ownerDocument,
+                                       ve.createDocumentFromHtml( html ),
+                                       target.$element.ownerDocument,
                                        currentLang,
                                        currentDir
                                )
                        );
-
-                       currentTarget.on( 'surfaceReady', function () {
-                               var surfaceView = 
currentTarget.getSurface().getView();
-                               // Container must be properly hidden before 
slideDown animation
-                               $targetContainer.removeAttr( 'style' ).hide()
-                                       // Restore directionality
-                                       .css( 'direction', currentDir );
-
-                               $targetContainer.slideDown().promise().done( 
function () {
-                                       surfaceView.focus();
-                               } );
+                       surface.$element.hide().slideDown().promise().done( 
function () {
+                               surface.getView().focus();
                        } );
                }
 
diff --git a/src/init/sa/ve.init.sa.Target.js b/src/init/sa/ve.init.sa.Target.js
index 78e8a7e..f4458d4 100644
--- a/src/init/sa/ve.init.sa.Target.js
+++ b/src/init/sa/ve.init.sa.Target.js
@@ -17,15 +17,15 @@
  *
  * @constructor
  * @param {jQuery} $container Container to render target into
- * @param {ve.dm.Document} dmDoc Document model
  * @param {string} [surfaceType] Type of surface to use, 'desktop' or 'mobile'
  * @throws {Error} Unknown surfaceType
  */
-ve.init.sa.Target = function VeInitSaTarget( $container, dmDoc, surfaceType ) {
+ve.init.sa.Target = function VeInitSaTarget( $container, surfaceType ) {
        // Parent constructor
-       ve.init.Target.call( this, $container );
+       ve.init.Target.call( this, $container, { shadow: true, actions: true } 
);
 
        this.surfaceType = surfaceType || 
this.constructor.static.defaultSurfaceType;
+       this.actions = null;
 
        switch ( this.surfaceType ) {
                case 'desktop':
@@ -37,9 +37,11 @@
                default:
                        throw new Error( 'Unknown surfaceType: ' + 
this.surfaceType );
        }
-       this.setupDone = false;
 
-       ve.init.platform.getInitializedPromise().done( this.setup.bind( this, 
dmDoc ) );
+       // 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 );
 };
 
 /* Inheritance */
@@ -53,44 +55,16 @@
 /* Methods */
 
 /**
- * Setup the target
- *
- * @param {ve.dm.Document} dmDoc Document model
- * @fires surfaceReady
+ * @inheritdoc
  */
-ve.init.sa.Target.prototype.setup = function ( dmDoc ) {
-       var surface, target = this;
-
-       if ( this.setupDone ) {
-               return;
-       }
-       this.setupDone = true;
-       surface = this.addSurface( dmDoc );
+ve.init.sa.Target.prototype.addSurface = function () {
+       var surface = ve.init.sa.Target.super.prototype.addSurface.apply( this, 
arguments );
        this.$element.append( surface.$element );
-
-       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.getToolbar().enableFloatable();
-       this.getToolbar().initialize();
-       surface.initialize();
-
-       // HACK: On mobile place the context inside toolbar.$bar which floats
-       if ( this.surfaceType === 'mobile' ) {
-               this.getToolbar().$bar.append( surface.context.$element );
+       if ( !this.getSurface() ) {
+               this.setSurface( surface );
        }
-
-       // This must be emitted asynchronously because 
ve.init.Platform#initialize
-       // is synchronous, and if we emit it right away, then users will be
-       // unable to listen to this event as it will have been emitted before 
the
-       // constructor returns.
-       setTimeout( function () {
-               target.emit( 'surfaceReady' );
-       } );
+       surface.initialize();
+       return surface;
 };
 
 /**
@@ -107,17 +81,25 @@
 /**
  * @inheritdoc
  */
-ve.init.sa.Target.prototype.setupToolbar = function ( config ) {
-       config = ve.extendObject( { shadow: true, actions: true }, config );
-
+ve.init.sa.Target.prototype.setupToolbar = function ( surface ) {
        // Parent method
-       ve.init.sa.Target.super.prototype.setupToolbar.call( this, config );
+       ve.init.sa.Target.super.prototype.setupToolbar.call( this, surface );
 
-       var actions = new ve.ui.TargetToolbar( this, this.getSurface() );
+       if ( !this.getToolbar().initialized ) {
+               this.getToolbar().$element.addClass( 
've-init-sa-target-toolbar' );
+               this.getToolbar().enableFloatable();
+               this.getToolbar().initialize();
 
-       actions.setup( [
+               this.actions = new ve.ui.TargetToolbar( this );
+               this.getToolbar().$actions.append( this.actions.$element );
+       }
+
+       this.actions.setup( [
                { include: [ 'commandHelp' ] }
-       ] );
+       ], surface );
 
-       this.toolbar.$actions.append( actions.$element );
+       // HACK: On mobile place the context inside toolbar.$bar which floats
+       if ( this.surfaceType === 'mobile' ) {
+               this.getToolbar().$bar.append( surface.context.$element );
+       }
 };
diff --git a/src/init/ve.init.Target.js b/src/init/ve.init.Target.js
index 1f7e5ae..581e01a 100644
--- a/src/init/ve.init.Target.js
+++ b/src/init/ve.init.Target.js
@@ -14,9 +14,10 @@
  *
  * @constructor
  * @param {jQuery} $container Container to render target into, must be 
attached to the DOM
+ * @param {Object} toolbarConfig Configuration options for the toolbar
  * @throws {Error} Container must be attached to the DOM
  */
-ve.init.Target = function VeInitTarget( $container ) {
+ve.init.Target = function VeInitTarget( $container, toolbarConfig ) {
        if ( !$.contains( $container[0].ownerDocument, $container[0] ) ) {
                throw new Error( 'Container must be attached to the DOM' );
        }
@@ -30,7 +31,7 @@
        this.elementDocument = this.$element[0].ownerDocument;
        this.surfaces = [];
        this.surface = null;
-       this.toolbar = null;
+       this.toolbar = new ve.ui.TargetToolbar( this, toolbarConfig );
 
        // Initialization
        this.$element.addClass( 've-init-target' );
@@ -223,9 +224,6 @@
 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;
 };
@@ -247,6 +245,7 @@
 ve.init.Target.prototype.setSurface = function ( surface ) {
        if ( surface !== this.surface ) {
                this.surface = surface;
+               this.setupToolbar( surface );
        }
 };
 
@@ -269,17 +268,11 @@
 };
 
 /**
- * Set up the toolbar and insert it into the DOM.
+ * Set up the toolbar.
  *
- * The default implementation inserts it before the surface, but subclasses 
can override this.
- *
- * @param {Object} [config] Configuration options
+ * @param {ve.ui.Surface} surface Surface
  */
-ve.init.Target.prototype.setupToolbar = function ( config ) {
-       if ( !this.surfaces.length ) {
-               throw new Error( 'Surface must be setup before Toolbar' );
-       }
-       this.toolbar = new ve.ui.TargetToolbar( this, this.getSurface(), config 
);
-       this.toolbar.setup( this.constructor.static.toolbarGroups );
-       this.toolbar.$element.insertBefore( this.getSurface().$element );
+ve.init.Target.prototype.setupToolbar = function ( surface ) {
+       this.toolbar.setup( this.constructor.static.toolbarGroups, surface );
+       this.getToolbar().$element.insertBefore( surface.$element );
 };
diff --git a/src/ui/ve.ui.TargetToolbar.js b/src/ui/ve.ui.TargetToolbar.js
index 7603763..d59f48c 100644
--- a/src/ui/ve.ui.TargetToolbar.js
+++ b/src/ui/ve.ui.TargetToolbar.js
@@ -12,12 +12,11 @@
  *
  * @constructor
  * @param {ve.init.Target} target Target to control
- * @param {ve.ui.Surface} surface Surface to control
  * @param {Object} [options] Configuration options
  */
-ve.ui.TargetToolbar = function VeUiTargetToolbar( target, surface, options ) {
+ve.ui.TargetToolbar = function VeUiTargetToolbar( target, options ) {
        // Parent constructor
-       ve.ui.TargetToolbar.super.call( this, surface, options );
+       ve.ui.TargetToolbar.super.call( this, options );
 
        // Properties
        this.target = target;
diff --git a/src/ui/ve.ui.Toolbar.js b/src/ui/ve.ui.Toolbar.js
index cd0b65d..658894d 100644
--- a/src/ui/ve.ui.Toolbar.js
+++ b/src/ui/ve.ui.Toolbar.js
@@ -11,20 +11,13 @@
  * @extends OO.ui.Toolbar
  *
  * @constructor
- * @param {ve.ui.Surface} surface Surface to control
  * @param {Object} [options] Configuration options
  */
-ve.ui.Toolbar = function VeUiToolbar( surface, options ) {
-       var toolbar = this;
-
-       // Configuration initialization
-       options = options || {};
-
+ve.ui.Toolbar = function VeUiToolbar( config ) {
        // Parent constructor
-       OO.ui.Toolbar.call( this, ve.ui.toolFactory, ve.ui.toolGroupFactory, 
options );
+       OO.ui.Toolbar.call( this, ve.ui.toolFactory, ve.ui.toolGroupFactory, 
config );
 
        // Properties
-       this.surface = surface;
        this.floating = false;
        this.floatable = false;
        this.$window = null;
@@ -33,9 +26,10 @@
                // Must use Function#bind (or a closure) instead of direct 
reference
                // because we need a unique function references for each 
Toolbar instance
                // to avoid $window.off() from unbinding other toolbars' event 
handlers.
-               resize: toolbar.onWindowResize.bind( toolbar ),
-               scroll: toolbar.onWindowScroll.bind( toolbar )
+               resize: this.onWindowResize.bind( this ),
+               scroll: this.onWindowScroll.bind( this )
        };
+       this.groupsConfig = null;
        // Default directions
        this.contextDirection = { inline: 'ltr', block: 'ltr' };
        // The following classes can be used here:
@@ -47,8 +41,6 @@
                .addClass( 've-ui-toolbar' )
                .addClass( 've-ui-dir-inline-' + this.contextDirection.inline )
                .addClass( 've-ui-dir-block-' + this.contextDirection.block );
-       // Events
-       this.surface.getModel().connect( this, { contextChange: 
'onContextChange' } );
 };
 
 /* Inheritance */
@@ -66,6 +58,34 @@
 /* Methods */
 
 /**
+ * Attach the toolbar to another surface
+ *
+ * @param {ve.ui.Surface} surface Surface
+ */
+ve.ui.Toolbar.prototype.attachToSurface = function ( surface ) {
+       this.setup( this.groupsConfig, surface );
+};
+
+/**
+ * inheritdoc
+ */
+ve.ui.Toolbar.prototype.setup = function ( groups, surface ) {
+       // Disconnect from last surface
+       if ( this.getSurface() ) {
+               this.getSurface().getModel().disconnect( this );
+       }
+
+       this.surface = surface;
+       this.groupsConfig = groups;
+
+       // Parent method
+       ve.ui.Toolbar.super.prototype.setup.call( this, groups );
+
+       // Events
+       this.getSurface().getModel().connect( this, { contextChange: 
'onContextChange' } );
+};
+
+/**
  * inheritdoc
  */
 ve.ui.Toolbar.prototype.isToolAvailable = function ( name ) {

-- 
To view, visit https://gerrit.wikimedia.org/r/177109
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic999ec166d8005e0368c0dc95c0a5593e6bf2945
Gerrit-PatchSet: 1
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to