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"> </span>' ),
modeSelect.$element,
$( '<span
class="ve-demo-menu-divider"> </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