mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-15 18:39:52 +00:00
74ed8e8766
Change-Id: Ibf6d4f08c4761727b2e3952a76e474c8221b38f9
200 lines
5.6 KiB
JavaScript
200 lines
5.6 KiB
JavaScript
/**
|
|
* VisualEditor Surface class.
|
|
*
|
|
* @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
/**
|
|
* Creates an ve.Surface object.
|
|
*
|
|
* A surface is a top-level object which contains both a surface model and a surface view.
|
|
*
|
|
* @class
|
|
* @constructor
|
|
* @param {String} parent Selector of element to attach to
|
|
* @param {HTMLElement} dom HTML element of document to edit
|
|
* @param {Object} options Configuration options
|
|
*/
|
|
ve.Surface = function VeSurface( parent, dom, options ) {
|
|
// Properties
|
|
this.$ = $( '<div class="ve-surface"></div>' );
|
|
this.documentModel = new ve.dm.Document( ve.dm.converter.getDataFromDom( dom ) );
|
|
this.options = ve.extendObject( true, ve.Surface.defaultOptions, options );
|
|
this.model = new ve.dm.Surface( this.documentModel );
|
|
this.view = new ve.ce.Surface( this.$, this.model );
|
|
this.toolbars = {};
|
|
|
|
// DOM Changes
|
|
$( parent ).append( this.$ );
|
|
this.$.append(
|
|
// Contain floating content
|
|
$( '<div style="clear: both;"></div>' ),
|
|
// Temporary paste buffer
|
|
// TODO: make 'paste' in surface stateful and remove this attrib
|
|
// TODO: Use a less generic id than "paste", or get rid of the ID alltogether
|
|
$( '<div id="paste" class="paste" contenteditable="true"></div>' )
|
|
);
|
|
|
|
// Initialization
|
|
this.setupToolbars();
|
|
ve.instances.push( this );
|
|
this.model.startHistoryTracking();
|
|
};
|
|
|
|
/* Static Members */
|
|
|
|
ve.Surface.defaultOptions = {
|
|
'toolbars': {
|
|
'top': {
|
|
'tools': [
|
|
{ 'name': 'history', 'items' : ['undo', 'redo'] },
|
|
{ 'name': 'textStyle', 'items' : ['format'] },
|
|
{ 'name': 'textStyle', 'items' : ['bold', 'italic', 'link', 'clear'] },
|
|
{ 'name': 'list', 'items' : ['number', 'bullet', 'outdent', 'indent'] }
|
|
]
|
|
}
|
|
}
|
|
};
|
|
|
|
/* Methods */
|
|
|
|
ve.Surface.prototype.getModel = function () {
|
|
return this.model;
|
|
};
|
|
|
|
ve.Surface.prototype.getDocumentModel = function () {
|
|
return this.documentModel;
|
|
};
|
|
|
|
ve.Surface.prototype.getView = function () {
|
|
return this.view;
|
|
};
|
|
|
|
ve.Surface.prototype.setupToolbars = function () {
|
|
var surface = this;
|
|
|
|
// Build each toolbar
|
|
$.each( this.options.toolbars, function ( name, config ) {
|
|
if ( config !== null ) {
|
|
if ( name === 'top' ) {
|
|
surface.toolbars[name] = {
|
|
'$wrapper': $( '<div class="ve-ui-toolbar-wrapper"></div>' ),
|
|
'$': $( '<div class="ve-ui-toolbar"></div>' )
|
|
.append(
|
|
$( '<div class="ve-ui-actions"></div>' ),
|
|
$( '<div style="clear:both"></div>' ),
|
|
$( '<div class="ve-ui-toolbar-shadow"></div>' )
|
|
)
|
|
};
|
|
surface.toolbars[name].$wrapper.append( surface.toolbars[name].$ );
|
|
surface.$.before( surface.toolbars[name].$wrapper );
|
|
|
|
if ( 'float' in config && config.float === true ) {
|
|
// Float top toolbar
|
|
surface.floatTopToolbar();
|
|
}
|
|
}
|
|
surface.toolbars[name].instance = new ve.ui.Toolbar(
|
|
surface.toolbars[name].$,
|
|
surface.view,
|
|
config.tools
|
|
);
|
|
}
|
|
} );
|
|
};
|
|
|
|
/*
|
|
* This code is responsible for switching toolbar into floating mode when scrolling ( with
|
|
* keyboard or mouse ).
|
|
* TODO: Determine if this would be better in ui.toolbar vs here.
|
|
* TODO: This needs to be refactored so that it only works on the main editor top tool bar.
|
|
*/
|
|
ve.Surface.prototype.floatTopToolbar = function () {
|
|
if ( !this.toolbars.top ) {
|
|
return;
|
|
}
|
|
var $wrapper = this.toolbars.top.$wrapper,
|
|
$toolbar = this.toolbars.top.$,
|
|
$window = $( window );
|
|
|
|
$window.on( {
|
|
'resize': function () {
|
|
if ( $wrapper.hasClass( 've-ui-toolbar-wrapper-floating' ) ) {
|
|
var toolbarsOffset = $wrapper.offset(),
|
|
left = toolbarsOffset.left,
|
|
right = $window.width() - $wrapper.outerWidth() - left;
|
|
$toolbar.css( {
|
|
'left': left,
|
|
'right': right
|
|
} );
|
|
}
|
|
},
|
|
'scroll': function () {
|
|
var left, right,
|
|
toolbarsOffset = $wrapper.offset(),
|
|
$editorDocument = $wrapper.parent().find('.ve-surface .ve-ce-documentNode'),
|
|
$lastBranch = $editorDocument.children( '.ve-ce-branchNode:last' );
|
|
|
|
if ( $window.scrollTop() > toolbarsOffset.top ) {
|
|
left = toolbarsOffset.left;
|
|
right = $window.width() - $wrapper.outerWidth() - left;
|
|
// If not floating, set float
|
|
if ( !$wrapper.hasClass( 've-ui-toolbar-wrapper-floating' ) ) {
|
|
$wrapper
|
|
.css( 'height', $wrapper.height() )
|
|
.addClass( 've-ui-toolbar-wrapper-floating' );
|
|
$toolbar.css( {
|
|
'left': left,
|
|
'right': right
|
|
} );
|
|
} else {
|
|
// Toolbar is floated
|
|
if (
|
|
// There's at least one branch
|
|
$lastBranch.length &&
|
|
// Toolbar is at or below the top of last node in the document
|
|
$window.scrollTop() + $toolbar.height() >= $lastBranch.offset().top
|
|
) {
|
|
if ( !$wrapper.hasClass( 've-ui-toolbar-wrapper-bottom' ) ) {
|
|
$wrapper
|
|
.removeClass( 've-ui-toolbar-wrapper-floating' )
|
|
.addClass( 've-ui-toolbar-wrapper-bottom' );
|
|
$toolbar.css({
|
|
'top': $window.scrollTop() + 'px',
|
|
'left': left,
|
|
'right': right
|
|
});
|
|
}
|
|
} else { // Unattach toolbar
|
|
if ( $wrapper.hasClass( 've-ui-toolbar-wrapper-bottom' ) ) {
|
|
$wrapper
|
|
.removeClass( 've-ui-toolbar-wrapper-bottom' )
|
|
.addClass( 've-ui-toolbar-wrapper-floating' );
|
|
$toolbar.css( {
|
|
'top': 0,
|
|
'left': left,
|
|
'right': right
|
|
} );
|
|
}
|
|
}
|
|
}
|
|
} else { // Return toolbar to top position
|
|
if (
|
|
$wrapper.hasClass( 've-ui-toolbar-wrapper-floating' ) ||
|
|
$wrapper.hasClass( 've-ui-toolbar-wrapper-bottom' )
|
|
) {
|
|
$wrapper.css( 'height', 'auto' )
|
|
.removeClass( 've-ui-toolbar-wrapper-floating' )
|
|
.removeClass( 've-ui-toolbar-wrapper-bottom' );
|
|
$toolbar.css( {
|
|
'top': 0,
|
|
'left': 0,
|
|
'right': 0
|
|
} );
|
|
}
|
|
}
|
|
}
|
|
} );
|
|
};
|