From 94479bd79d29479d220141ec0e2243eaf2e7a9b6 Mon Sep 17 00:00:00 2001 From: Rob Moen Date: Thu, 26 Apr 2012 11:49:36 -0700 Subject: [PATCH] Build out ve.Surface constructor to support multiple editor instances Now setting up multiple toolbars per config Tools & Modes are now configurable per toolbar per instance Base elements are created on demand and no longer id specific Note: There are some bugs with multiple instances. Change-Id: Id0bbbca2d1b76fd2db3f3b0f9abd90194930b610 --- demos/ve/index.php | 12 +- modules/sandbox/base.php | 125 ---------- modules/sandbox/sandbox.css | 34 +-- modules/sandbox/sandbox.js | 199 +++------------- modules/ve/ve.Surface.js | 456 ++++++++++++++++++++++++++++++++++-- 5 files changed, 498 insertions(+), 328 deletions(-) diff --git a/demos/ve/index.php b/demos/ve/index.php index 62e6619102..a8c8c351ec 100644 --- a/demos/ve/index.php +++ b/demos/ve/index.php @@ -24,7 +24,7 @@ overflow-y: scroll; background-color: white; } - #es-base { + .es-base { margin: 2em; margin-top: 0em; -webkit-box-shadow: 0 0.25em 1.5em 0 #dddddd; @@ -35,15 +35,15 @@ -o-border-radius: 0.5em; border-radius: 0.5em; } - #es-panes { + .es-panes { border: solid 1px #cccccc; border-top: none; } - #es-editor, .es-showData #es-editor { + .es-editor, .es-showData .es-editor { padding-left: 1em; padding-right: 1em; } - #es-toolbar { + .es-toolbar { -webkit-border-radius: 0; -moz-border-radius: 0; -o-border-radius: 0; @@ -57,12 +57,12 @@ -o-border-top-left-radius: 0.25em; border-top-left-radius: 0.25em; } - #es-toolbar.float { + .es-toolbar.float { left: 2em; right: 2em; top: 0; } - #es-docs { + .es-docs { margin-left: 2.5em; } diff --git a/modules/sandbox/base.php b/modules/sandbox/base.php index b0f02f685b..95e858a71b 100644 --- a/modules/sandbox/base.php +++ b/modules/sandbox/base.php @@ -6,129 +6,4 @@
Example documents:
- - - - diff --git a/modules/sandbox/sandbox.css b/modules/sandbox/sandbox.css index f86f2b60ec..5f3218ac8f 100644 --- a/modules/sandbox/sandbox.css +++ b/modules/sandbox/sandbox.css @@ -1,4 +1,4 @@ -#es-toolbar { +.es-toolbar { border: solid 1px #cccccc; position: relative; -webkit-border-radius: 0.25em; @@ -14,7 +14,7 @@ -o-user-select; user-select: none; } -#es-toolbar-wrapper.float #es-toolbar { +.es-toolbar-wrapper.float .es-toolbar { top: 0; position: fixed; -webkit-border-radius: 0; @@ -24,7 +24,7 @@ z-index: 100; border-top: none; } -#es-toolbar-shadow { +.es-toolbar-shadow { background-image: url(../ve/ui/styles/images/toolbar-shadow.png); background-position: top left; background-repeat: repeat-x; @@ -39,15 +39,15 @@ transition: opacity 500ms ease-in-out; opacity: 0.125; } -#es-toolbar.float #es-toolbar-shadow { +.es-toolbar.float .es-toolbar-shadow { opacity: 0.5; } -.es-showData #es-editor { +.es-showData .es-editor { border-right: solid 1px #cccccc; margin: 0; padding: 0; } -.es-showData #es-visual { +.es-showData .es-visual { margin: 0; padding: 0; float: left; @@ -55,10 +55,10 @@ overflow: hidden; margin-left: -1px; } -#es-panels { +.es-panels { display: none; } -.es-showData #es-panels { +.es-showData .es-panels { display: block; float: right; width: 50%; @@ -108,7 +108,7 @@ font-size: 0.9em; } -#es-toolbar .es-toolbarGroups { +.es-toolbar .es-toolbarGroups { float: left; } .es-modes { @@ -149,38 +149,38 @@ -moz-box-shadow: inset 0px 1px 4px 0px rgba(0, 0, 0, 0.07); box-shadow: inset 0px 1px 4px 0px rgba(0, 0, 0, 0.07); } -#es-mode-wikitext:before { +.es-mode-wikitext:before { background-image: url(../ve/ui/styles/images/wikitext.png); } -#es-mode-json:before { +.es-mode-json:before { background-image: url(../ve/ui/styles/images/json.png); } -#es-mode-html:before { +.es-mode-html:before { background-image: url(../ve/ui/styles/images/html.png); } -#es-mode-render:before { +.es-mode-render:before { background-image: url(../ve/ui/styles/images/render.png); } -#es-mode-history:before { +.es-mode-history:before { background-image: url(../ve/ui/styles/images/history.png); } -#es-mode-help:before { +.es-mode-help:before { background-image: url(../ve/ui/styles/images/help.png); } -#es-panel-history div { +.es-panel-history div { border-bottom: solid 1px #dddddd; padding: 0.5em 0; color: #666666; background-color: #f9f9f9; } -#es-panel-history div.es-panel-history-active { +.es-panel-history div.es-panel-history-active { color: #000000; background-color: white; } diff --git a/modules/sandbox/sandbox.js b/modules/sandbox/sandbox.js index b6f8e8a649..d0191cd149 100644 --- a/modules/sandbox/sandbox.js +++ b/modules/sandbox/sandbox.js @@ -149,7 +149,7 @@ $(document).ready( function() { 'end': 17 } } - ] + ] } }, { @@ -169,7 +169,7 @@ $(document).ready( function() { 'end': 17 } } - ] + ] } }, { @@ -189,7 +189,7 @@ $(document).ready( function() { 'end': 17 } } - ] + ] } }, { @@ -209,7 +209,7 @@ $(document).ready( function() { 'end': 17 } } - ] + ] } }, { @@ -229,7 +229,7 @@ $(document).ready( function() { 'end': 17 } } - ] + ] } }, { @@ -249,7 +249,7 @@ $(document).ready( function() { 'end': 17 } } - ] + ] } }, { @@ -446,7 +446,7 @@ $(document).ready( function() { 'type': 'paragraph', 'content': { 'text': 'Test 4444' } } - ] + ] }, { 'type': 'listItem', @@ -458,7 +458,7 @@ $(document).ready( function() { 'type': 'paragraph', 'content': { 'text': 'Test 55555' } } - ] + ] }, { 'type': 'listItem', @@ -470,7 +470,7 @@ $(document).ready( function() { 'type': 'paragraph', 'content': { 'text': 'Test 666666' } } - ] + ] } ] } @@ -502,150 +502,34 @@ $(document).ready( function() { ] } }; - - /* Create a new instance of VE, optional third param for ve configuration options */ - var sandboxEditor = new ve.Surface('sandbox', wikidoms['Wikipedia article'] /*, options */), - surfaceModel = sandboxEditor.getSurfaceModel(), - documentModel = sandboxEditor.getDocumentModel(), - editorID = sandboxEditor.getID(); - /* - * This code is responsible for switching toolbar into floating mode when scrolling (with - * keyboard or mouse). - */ - var $toolbarWrapper = $( '#es-toolbar-wrapper' ), - $toolbar = $( '#es-toolbar' ), - $window = $( window ); - $window.scroll( function() { - var toolbarWrapperOffset = $toolbarWrapper.offset(); - if ( $window.scrollTop() > toolbarWrapperOffset.top ) { - if ( !$toolbarWrapper.hasClass( 'float' ) ) { - var left = toolbarWrapperOffset.left, - right = $window.width() - $toolbarWrapper.outerWidth() - left; - $toolbarWrapper.css( 'height', $toolbarWrapper.height() ).addClass( 'float' ); - $toolbar.css( { 'left': left, 'right': right } ); - } - } else { - if ( $toolbarWrapper.hasClass( 'float' ) ) { - $toolbarWrapper.css( 'height', 'auto' ).removeClass( 'float' ); - $toolbar.css( { 'left': 0, 'right': 0 } ); + /* Sandbox integration hack. Allows both MW integration and demo pages to work */ + if ( $('#content').length === 0 ) { + $( 'body' ).append( + $( '
' ).attr( 'id', 'content' ) + ); + } + + /* Sandbox config object. */ + var options = { + toolbars: { + top: { + /* What modes this toolbar will have */ + modes: ['wikitext', 'json', 'html', 'render', 'history', 'help'] } } - } ); + }; - var $modeButtons = $( '.es-modes-button' ), - $panels = $( '.es-panel' ), - $base = $( '#es-base' ), - currentMode = null, - modes = { - 'wikitext': { - '$': $( '#es-mode-wikitext' ), - '$panel': $( '#es-panel-wikitext' ), - 'update': function() { - this.$panel.text( - ve.dm.WikitextSerializer.stringify( documentModel.getPlainObject() ) - ); - } - }, - 'json': { - '$': $( '#es-mode-json' ), - '$panel': $( '#es-panel-json' ), - 'update': function() { - this.$panel.text( ve.dm.JsonSerializer.stringify( documentModel.getPlainObject(), { - 'indentWith': ' ' - } ) ); - } - }, - 'html': { - '$': $( '#es-mode-html' ), - '$panel': $( '#es-panel-html' ), - 'update': function() { - this.$panel.text( - ve.dm.HtmlSerializer.stringify( documentModel.getPlainObject() ) - ); - } - }, - 'render': { - '$': $( '#es-mode-render' ), - '$panel': $( '#es-panel-render' ), - 'update': function() { - this.$panel.html( - ve.dm.HtmlSerializer.stringify( documentModel.getPlainObject() ) - ); - } - }, - 'history': { - '$': $( '#es-mode-history' ), - '$panel': $( '#es-panel-history' ), - 'update': function() { - var history = surfaceModel.getHistory(), - i = history.length, - end = Math.max( 0, i - 25 ), - j, - k, - ops, - events = '', - z = 0, - operations, - data; - - while ( --i >= end ) { - z++; - operations = []; - for ( j = 0; j < history[i].stack.length; j++) { - ops = history[i].stack[j].getOperations().slice(0); - for ( k = 0; k < ops.length; k++ ) { - data = ops[k].data || ops[k].length; - if ( ve.isArray( data ) ) { - data = data[0]; - if ( ve.isArray( data ) ) { - data = data[0]; - } - } - if ( typeof data !== 'string' && typeof data !== 'number' ) { - data = '-'; - } - ops[k] = ops[k].type.substr( 0, 3 ) + '(' + data + ')'; - } - operations.push('[' + ops.join( ', ' ) + ']'); - } - events += '' + operations.join(', ') + '
'; - } - - this.$panel.html( events ); - } - }, - 'help': { - '$': $( '#es-mode-help' ), - '$panel': $( '#es-panel-help' ), - 'update': function() {} - } - }; - $.each( modes, function( name, mode ) { - mode.$.click( function() { - var disable = $(this).hasClass( 'es-modes-button-down' ); - var visible = $base.hasClass( 'es-showData' ); - $modeButtons.removeClass( 'es-modes-button-down' ); - $panels.hide(); - if ( disable ) { - if ( visible ) { - $base.removeClass( 'es-showData' ); - $window.resize(); - } - currentMode = null; - } else { - $(this).addClass( 'es-modes-button-down' ); - mode.$panel.show(); - if ( !visible ) { - $base.addClass( 'es-showData' ); - $window.resize(); - } - mode.update.call( mode ); - currentMode = mode; - } - } ); - } ); + /* + Create Sandbox instance of VE + Attach to #content element + */ + var sandboxEditor = new ve.Surface( '#content', wikidoms['Wikipedia article'], options ), + surfaceModel = sandboxEditor.getSurfaceModel(), + documentModel = sandboxEditor.getDocumentModel(), + parent = sandboxEditor.getParent(); + /* Sandbox links above the editor */ var $docsList = $( '#es-docs-list' ); $.each( wikidoms, function( title, wikidom ) { $docsList.append( @@ -667,8 +551,8 @@ $(document).ready( function() { ); surfaceModel.purgeHistory(); - if ( currentMode ) { - currentMode.update.call( currentMode ); + if ( sandboxEditor.currentMode ) { + sandboxEditor.currentMode.update.call( sandboxEditor.currentMode ); } return false; } ) @@ -676,19 +560,8 @@ $(document).ready( function() { ); } ); - surfaceModel.on( 'transact', function() { - if ( currentMode ) { - currentMode.update.call( currentMode ); - } - } ); - surfaceModel.on( 'select', function() { - if ( currentMode === modes.history ) { - currentMode.update.call( currentMode ); - } - } ); - + /* Sandbox Warning Message */ $( '#es-docs' ).css( { 'visibility': 'visible' } ); - $( '#es-base' ).css( { 'visibility': 'visible' } ); // Show the warning that this software is experimental // TODO: Use a cookie to remember the warning has been dismissed $( '#es-warning' ).show(); @@ -696,5 +569,5 @@ $(document).ready( function() { $(this).parent().slideUp(); return false; } ); - $( '#es-mode-wikitext' ).click(); + $( '.es-mode-wikitext' ).click(); } ); diff --git a/modules/ve/ve.Surface.js b/modules/ve/ve.Surface.js index 763e3d220d..d003601218 100644 --- a/modules/ve/ve.Surface.js +++ b/modules/ve/ve.Surface.js @@ -5,36 +5,450 @@ * * @class * @constructor - * @param {String} id Unique name of editor instance + * @param {String} parent Selector of element to attach to * @param {Array} data Document data * @param {Object} options Configuration options */ -ve.Surface = function( id, data, options ) { +ve.Surface = function( parent, data, options ) { // Properties - this.id = id; - this.options = ve.extendObject( { + this.parent = parent; + this.modes = {}; + this.currentMode = null; + /* Extend VE configuration recursively */ + this.options = ve.extendObject( true, { // Default options toolbars: { - 'top': [{ 'name': 'history', 'items' : ['undo', 'redo'] }, - { 'name': 'textStyle', 'items' : ['format'] }, - { 'name': 'textStyle', 'items' : ['bold', 'italic', 'link', 'clear'] }, - { 'name': 'list', 'items' : ['number', 'bullet', 'outdent', 'indent'] }] + 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'] }] + } + }, + //TODO: i18n + modes: { + wikitext: 'Toggle wikitext view', + json: 'Toggle JSON view', + html: 'Toggle HTML view', + render: 'Toggle preview', + history: 'Toggle transaction history view', + help: 'Toggle help view' } + }, options ); - + + // A place to store element references + this.$base = null; + this.$surface = null; + this.toolbarWrapper = {}; + + /* Register Document and Surface Models */ this.documentModel = ve.dm.DocumentNode.newFromPlainObject( data ); this.surfaceModel = new ve.dm.Surface( this.documentModel ); - //TODO: Find source of breakage when view element is not #es-editor - this.view = new ve.ce.Surface( $( '#es-editor' ), this.surfaceModel ); - this.context = new ve.ui.Context( this.view ); - - //TODO: Configure toolbar based on this.options. - this.toolbar = new ve.ui.Toolbar( $( '#es-toolbar' ), this.view, this.options.toolbars.top ); + // Setup VE DOM Skeleton + this.setupBaseElements(); + + // Setup Surface View + this.setupSurfaceView(); + + // Setup toolbars based on this.options + this.setupToolbars(); + + // Setup various toolbar modes and panels + this.setupModes(); + // Registration ve.instances.push( this ); + console.log (this); }; +/* Setup Methods */ + +ve.Surface.prototype.setupBaseElements = function() { + // Make new base element + this.$base = $('
') + .attr( 'class', 'es-base' ) + .append( + $('
').attr('class', 'es-panes') + .append( + $('
').attr('class', 'es-visual') + ).append( + $('
').attr('class', 'es-panels') + ).append( + $('
').attr('style', 'clear:both') + ) + ).append( + $('
').attr( { + 'id': 'paste', //TODO: make 'paste' in surface stateful and remove this attrib + 'class': 'paste', + 'contenteditable': 'true', + 'style': 'height:1px;width:1px;display:none;opacity:0;position:absolute;' + }) + ); + // Attach the base the the parent + $( this.getParent() ).append( this.$base ); +}; + +ve.Surface.prototype.setupSurfaceView = function() { + this.$surface = $('
').attr('class', 'es-editor'); + this.$base.find('.es-visual').append( this.$surface ); + + /* Instantiate surface layer */ + this.view = new ve.ce.Surface( $( '.es-editor' ), this.getSurfaceModel() ); + this.context = new ve.ui.Context( this.view ); +}; + +ve.Surface.prototype.setupToolbars = function() { + var _this = this; + + // Build each toolbar + $.each( this.options.toolbars, function(name, config) { + if ( config !== null ) { + if(name === 'top') { + // Append toolbar wrapper at the top, just above .es-panes + _this.toolbarWrapper[name] = $('
') + .attr('class', 'es-toolbar-wrapper') + .append( + $('
').attr('class', 'es-toolbar') + .append( + $('
').attr('class', 'es-modes') + ).append( + $('
').attr('style', 'clear:both') + ).append( + $('
').attr('class', 'es-toolbar-shadow') + ) + ); + + _this.$base.find('.es-panes').before( _this.toolbarWrapper[name] ); + } + // Instantiate the toolbar + _this['toolbar-' + name] = new ve.ui.Toolbar( _this.$base.find( '.es-toolbar' ), _this.view, config.tools ); + } + + }); + + // Setup sticky toolbar + this.makeMainEditorToolbarFloat(); +}; + +/* + * 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.makeMainEditorToolbarFloat = function() { + if ( !this.toolbarWrapper.top ) { + return; + } + var $toolbarWrapper = this.toolbarWrapper.top, + $toolbar = $toolbarWrapper.find('.es-toolbar'); + $window = $( window ); + + $window.scroll( function() { + var toolbarWrapperOffset = $toolbarWrapper.offset(); + if ( $window.scrollTop() > toolbarWrapperOffset.top ) { + if ( !$toolbarWrapper.hasClass( 'float' ) ) { + var left = toolbarWrapperOffset.left, + right = $window.width() - $toolbarWrapper.outerWidth() - left; + $toolbarWrapper.css( 'height', $toolbarWrapper.height() ).addClass( 'float' ); + $toolbar.css( { 'left': left, 'right': right } ); + } + } else { + if ( $toolbarWrapper.hasClass( 'float' ) ) { + $toolbarWrapper.css( 'height', 'auto' ).removeClass( 'float' ); + $toolbar.css( { 'left': 0, 'right': 0 } ); + } + } + } ); +}; + +ve.Surface.prototype.setupModes = function(){ + var _this = this; + var activeModes = []; + + // Loop through toolbar config to build modes + $.each( _this.options.toolbars, function(name, toolbar){ + //if toolbar has modes + if( toolbar.modes && toolbar.modes.length > 0 ) { + for(var i=0;i<=toolbar.modes.length -1;i++) { + $( _this.toolbarWrapper[name] ) + .find('.es-modes') + .append( + $('
').attr({ + 'class': 'es-modes-button es-mode-' + toolbar.modes[i], + 'title': _this.options.modes[toolbar.modes[i]] + }) + ); + if( !activeModes[mode] ) { + activeModes.push( toolbar.modes[i] ); + } + } + } + }); + + // Build elements in #es-panels for each activeMode + if ( activeModes.length > 0 ) { + for (var mode in activeModes) { + var renderType = ''; + switch( activeModes[mode] ) { + case 'render': + renderType = 'es-render'; + break; + case 'help': + renderType = ''; + break; + default: + renderType = 'es-code'; + break; + } + _this.$base + .find('.es-panels') + .append( + $('
').attr({ + 'class': 'es-panel es-panel-' + activeModes[mode] + ' ' + renderType + }) + ); + } + } + /* + Define this.modes + Called after bulding elements. + */ + this.defineModes(); + + //Bind Mode events + $.each( this.modes, function( name, mode ) { + mode.$.click( function() { + var disable = $(this).hasClass( 'es-modes-button-down' ); + var visible = _this.$base.hasClass( 'es-showData' ); + $('.es-modes-button').removeClass( 'es-modes-button-down' ); + $('.es-panel').hide(); + if ( disable ) { + if ( visible ) { + _this.$base.removeClass( 'es-showData' ); + $( window ).resize(); + } + _this.currentMode = null; + } else { + $(this).addClass( 'es-modes-button-down' ); + mode.$panel.show(); + if ( !visible ) { + _this.$base.addClass( 'es-showData' ); + $( window ).resize(); + } + mode.update.call( mode ); + _this.currentMode = mode; + } + } ); + } ); + + /* Bind some surface events for modes */ + this.getSurfaceModel().on( 'transact', function() { + if ( _this.currentMode ) { + _this.currentMode.update.call( _this.currentMode ); + } + } ); + this.getSurfaceModel().on( 'select', function() { + if ( _this.currentMode === _this.modes.history ) { + _this.currentMode.update.call( _this.currentMode ); + } + } ); + + +}; + +/* + Define modes + TODO: possibly extend this object via the config +*/ +ve.Surface.prototype.defineModes = function() { + var _this = this; + this.modes = { + 'wikitext': { + '$': _this.$base.find( '.es-mode-wikitext' ), + '$panel': _this.$base.find( '.es-panel-wikitext' ), + 'update': function() { + this.$panel.text( + ve.dm.WikitextSerializer.stringify( _this.getDocumentModel().getPlainObject() ) + ); + } + }, + 'json': { + '$': _this.$base.find( '.es-mode-json' ), + '$panel': _this.$base.find( '.es-panel-json' ), + 'update': function() { + this.$panel.text( ve.dm.JsonSerializer.stringify( _this.getDocumentModel().getPlainObject(), { + 'indentWith': ' ' + } ) ); + } + }, + 'html': { + '$': _this.$base.find( '.es-mode-html' ), + '$panel': _this.$base.find( '.es-panel-html' ), + 'update': function() { + this.$panel.text( + ve.dm.HtmlSerializer.stringify( _this.getDocumentModel().getPlainObject() ) + ); + } + }, + 'render': { + '$': _this.$base.find( '.es-mode-render' ), + '$panel': _this.$base.find( '.es-panel-render' ), + 'update': function() { + this.$panel.html( + ve.dm.HtmlSerializer.stringify( _this.getDocumentModel().getPlainObject() ) + ); + } + }, + 'history': { + '$': _this.$base.find( '.es-mode-history' ), + '$panel': _this.$base.find( '.es-panel-history' ), + 'update': function() { + var history = _this.getSurfaceModel().getHistory(), + i = history.length, + end = Math.max( 0, i - 25 ), + j, + k, + ops, + events = '', + z = 0, + operations, + data; + + while ( --i >= end ) { + z++; + operations = []; + for ( j = 0; j < history[i].stack.length; j++) { + ops = history[i].stack[j].getOperations().slice(0); + for ( k = 0; k < ops.length; k++ ) { + data = ops[k].data || ops[k].length; + if ( ve.isArray( data ) ) { + data = data[0]; + if ( ve.isArray( data ) ) { + data = data[0]; + } + } + if ( typeof data !== 'string' && typeof data !== 'number' ) { + data = '-'; + } + ops[k] = ops[k].type.substr( 0, 3 ) + '(' + data + ')'; + } + operations.push('[' + ops.join( ', ' ) + ']'); + } + events += '' + operations.join(', ') + '
'; + } + + this.$panel.html( events ); + } + }, + 'help': { + '$': _this.$base.find( '.es-mode-help' ), + '$panel': _this.$base.find( '.es-panel-help' ), + 'update': function() { + //TODO: Make this less ugly, + //HOW?: Create api to register help items so that they may be generated here. + /*jshint multistr:true */ + this.$panel.html('\ +
Keyboard Shortcuts
\ +
Clipboard
\ +
\ + \ + Ctrl or +\ + C\ + \ + Copy selected text\ +
\ +
\ + \ + Ctrl or +\ + X\ + \ + Cut selected text\ +
\ +
\ + \ + Ctrl or +\ + V\ + \ + Paste text at the cursor\ +
\ +
History navigation
\ +
\ + \ + Ctrl or +\ + Z\ + \ + Undo\ +
\ +
\ + \ + Ctrl or +\ + Y\ + \ + Redo\ +
\ +
\ + \ + Ctrl or +\ + +\ + Z\ + \ + Redo\ +
\ +
Formatting
\ +
\ + \ + Ctrl or +\ + B\ + \ + Make selected text bold\ +
\ +
\ + \ + Ctrl or +\ + I\ + \ + Make selected text italic\ +
\ +
\ + \ + Ctrl or +\ + K\ + \ + Make selected text a link\ +
\ +
Selection
\ +
\ + \ + +\ + Arrow\ + \ + Adjust selection\ +
\ +
\ + \ + Ctrl or +\ + Arrow\ + \ + Move cursor by words or blocks\ +
\ +
\ + \ + Ctrl or +\ + +\ + Arrow\ + \ + Adjust selection by words or blocks\ +
'); + } + } + }; +}; + +/* Get Methods */ + ve.Surface.prototype.getSurfaceModel = function() { return this.surfaceModel; }; @@ -43,6 +457,14 @@ ve.Surface.prototype.getDocumentModel = function() { return this.documentModel; }; -ve.Surface.prototype.getID = function() { - return this.id; +ve.Surface.prototype.getView = function() { + return this.view; +}; + +ve.Surface.prototype.getContext = function() { + return this.context; +}; + +ve.Surface.prototype.getParent = function() { + return this.parent; };