Esanders has uploaded a new change for review. https://gerrit.wikimedia.org/r/324525
Change subject: Core source mode ...................................................................... Core source mode Integrates with the demo. Not all tools work properly but sould be in a mergeable state. Change-Id: I02f3849027a6652701c2d354d1b77ece9d1a56c1 --- M demos/ve/demo.css M demos/ve/ve.demo.SurfaceContainer.js M src/dm/ve.dm.Surface.js M src/init/ve.init.Target.js M src/ui/styles/ve.ui.Surface.css M src/ui/ve.ui.Surface.js 6 files changed, 110 insertions(+), 69 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor refs/changes/25/324525/1 diff --git a/demos/ve/demo.css b/demos/ve/demo.css index a5a544f..5f1dcea 100644 --- a/demos/ve/demo.css +++ b/demos/ve/demo.css @@ -86,14 +86,6 @@ width: 0; } -.ve-demo-source { - max-width: none; -} - -.ve-demo-source textarea { - font-family: monospace; -} - .ve-demo-read { /* Match document padding and surface font size */ padding: 0.75em 1.5em; diff --git a/demos/ve/ve.demo.SurfaceContainer.js b/demos/ve/ve.demo.SurfaceContainer.js index 2dd3924..73c471a 100644 --- a/demos/ve/ve.demo.SurfaceContainer.js +++ b/demos/ve/ve.demo.SurfaceContainer.js @@ -43,16 +43,16 @@ label: 'Show changes' } ); $exitReadButton = $( '<a href="#">' ).text( 'Back to editor' ).on( 'click', function () { - container.change( 've' ); + container.modeSelect.selectItemByData( 'visual' ); return false; } ); this.modeSelect = new OO.ui.ButtonSelectWidget().addItems( [ - new OO.ui.ButtonOptionWidget( { data: 've', label: 'VE' } ), - new OO.ui.ButtonOptionWidget( { data: 'edit', label: 'Edit HTML' } ), + new OO.ui.ButtonOptionWidget( { data: 'visual', label: 'Visual' } ), + new OO.ui.ButtonOptionWidget( { data: 'source', label: 'Source' } ), new OO.ui.ButtonOptionWidget( { data: 'read', label: 'Read' } ) ] ); - this.modeSelect.selectItemByData( 've' ); + this.modeSelect.selectItemByData( 'visual' ); this.target = target; this.surface = null; @@ -61,19 +61,13 @@ this.$surfaceWrapper = $( '<div>' ).addClass( 've-demo-surfaceWrapper' ); this.mode = null; this.pageMenu = pageDropdown.getMenu(); - this.sourceTextInput = new OO.ui.TextInputWidget( { - multiline: true, - autosize: true, - maxRows: 999, - classes: [ 've-demo-source' ] - } ); this.$readView = $( '<div>' ).addClass( 've-demo-read' ).hide(); // Events this.pageMenu.on( 'select', function ( item ) { var page = item.getData(); - container.change( 've', page ); - container.modeSelect.selectItemByData( 've' ); + container.change( 'visual', page ); + container.modeSelect.selectItemByData( 'visual' ); } ); this.modeSelect.on( 'select', function ( item ) { container.change( item.getData() ); @@ -106,7 +100,6 @@ $exitReadButton ), this.$surfaceWrapper, - this.sourceTextInput.$element.hide(), this.$readView ); @@ -152,7 +145,7 @@ /** * Change mode or page * - * @param {string} mode Mode to switch to: 've', 'edit or 'read' + * @param {string} mode Mode to switch to: 'visual', 'edit or 'read' * @param {string} [page] Page to load * @return {jQuery.Promise} Promise which resolves when change is complete */ @@ -165,23 +158,13 @@ return $.Deferred().resolve().promise(); } - this.modeSelect.selectItemByData( mode ); - switch ( this.mode ) { - case 've': - closePromise = this.$surfaceWrapper.slideUp().promise(); + case 'visual': + case 'source': + closePromise = this.surface.$element.slideUp().promise(); if ( !page ) { html = this.surface.getHtml(); currentDir = this.surface.getModel().getDocument().getDir(); - } - this.surface.destroy(); - this.surface = null; - break; - - case 'edit': - closePromise = this.sourceTextInput.$element.slideUp().promise(); - if ( !page ) { - html = this.sourceTextInput.getValue(); } break; @@ -202,27 +185,29 @@ otherDir = currentDir === 'ltr' ? 'rtl' : 'ltr', $editStylesheets = $( 'link[rel=stylesheet]:not(.stylesheet-read):not(.stylesheet-' + otherDir + ')' ); + if ( container.surface ) { + container.surface.destroy(); + container.surface = null; + } + $( '.ve-demo-targetToolbar' ).toggle( !isRead ); container.$element.find( '.ve-demo-surfaceToolbar-edit' ).toggle( !isRead ); container.$element.find( '.ve-demo-surfaceToolbar-read' ).toggle( isRead ); $editStylesheets.prop( 'disabled', isRead ); switch ( mode ) { - case 've': + case 'visual': + case 'source': + container.$surfaceWrapper.show(); if ( page ) { - container.loadPage( page ); + container.loadPage( page, mode ); } else if ( html !== undefined ) { - container.loadHtml( html ); + container.loadHtml( html, mode ); } break; - case 'edit': - container.sourceTextInput.$element.show(); - container.sourceTextInput.setValue( html ).adjustSize(); - container.sourceTextInput.$element.hide().slideDown(); - break; - case 'read': + container.$surfaceWrapper.hide(); container.$readView.html( html ).css( 'direction', currentDir ).slideDown(); break; } @@ -234,17 +219,18 @@ * Load a page into the editor * * @param {string} src Path of html to load + * @param {string} mode Edit mode */ -ve.demo.SurfaceContainer.prototype.loadPage = function ( src ) { +ve.demo.SurfaceContainer.prototype.loadPage = function ( src, mode ) { var container = this; container.emit( 'changePage' ); ve.init.platform.getInitializedPromise().done( function () { - container.$surfaceWrapper.slideUp().promise().done( function () { + ( container.surface ? container.surface.$element.slideUp().promise() : $.Deferred().resolve().promise() ).done( function () { var localMatch = src.match( /^localStorage\/(.+)$/ ); if ( localMatch ) { - container.loadHtml( localStorage.getItem( localMatch[ 1 ] ) ); + container.loadHtml( localStorage.getItem( localMatch[ 1 ] ), mode ); return; } $.ajax( { @@ -259,7 +245,7 @@ pageHtml = result; } - container.loadHtml( pageHtml ); + container.loadHtml( pageHtml, mode ); } ); } ); } ); @@ -269,8 +255,9 @@ * Load HTML into the editor * * @param {string} pageHtml HTML string + * @param {string} mode Edit mode */ -ve.demo.SurfaceContainer.prototype.loadHtml = function ( pageHtml ) { +ve.demo.SurfaceContainer.prototype.loadHtml = function ( pageHtml, mode ) { var dmDoc, container = this; @@ -280,10 +267,10 @@ this.surface = this.target.addSurface( ve.dm.converter.getModelFromDom( - ve.createDocumentFromHtml( pageHtml ), + this.target.parseDocument( pageHtml, mode ), { lang: this.lang, dir: this.dir } ), - { placeholder: 'Start your document' } + { placeholder: 'Start your document', mode: mode } ); this.target.setSurface( this.surface ); @@ -291,13 +278,13 @@ dmDoc = this.surface.getModel().getDocument(); this.oldDoc = dmDoc.cloneFromRange(); - this.$surfaceWrapper.empty().append( this.surface.$element.parent() ) - .hide().slideDown().promise().done( function () { - // Check surface still exists - if ( container.surface ) { - container.surface.getView().focus(); - } - } ); + this.$surfaceWrapper.empty().append( this.surface.$element.parent() ); + this.surface.$element.hide().slideDown().promise().done( function () { + // Check surface still exists + if ( container.surface ) { + container.surface.getView().focus(); + } + } ); }; /** @@ -312,8 +299,8 @@ this.lang = lang; this.dir = dir; - this.change( 've' ).done( function () { - container.loadHtml( container.surface.getHtml() ); + this.change( 'visual' ).done( function () { + container.loadHtml( container.surface.getHtml(), 'visual' ); } ); }; @@ -338,11 +325,9 @@ ve.demo.SurfaceContainer.prototype.save = function () { var html; switch ( this.mode ) { - case 've': + case 'visual': + case 'source': html = this.surface.getHtml(); - break; - case 'edit': - html = this.sourceTextInput.getValue(); break; case 'read': html = ve.properInnerHtml( this.$readView[ 0 ] ); diff --git a/src/dm/ve.dm.Surface.js b/src/dm/ve.dm.Surface.js index 3830941..893d878 100644 --- a/src/dm/ve.dm.Surface.js +++ b/src/dm/ve.dm.Surface.js @@ -12,13 +12,18 @@ * * @constructor * @param {ve.dm.Document} doc Document model to create surface for + * @param {Object} [config] Configuration options + * @cfg {boolean} [sourceMode] Source editing mode */ -ve.dm.Surface = function VeDmSurface( doc ) { +ve.dm.Surface = function VeDmSurface( doc, config ) { + config = config || {}; + // Mixin constructors OO.EventEmitter.call( this ); // Properties this.documentModel = doc; + this.sourceMode = !!config.sourceMode; this.metaList = new ve.dm.MetaList( this ); this.selection = new ve.dm.NullSelection( this.getDocument() ); this.selectionBefore = new ve.dm.NullSelection( this.getDocument() ); @@ -476,7 +481,11 @@ * @return {ve.dm.SurfaceFragment} Surface fragment */ ve.dm.Surface.prototype.getFragment = function ( selection, noAutoSelect, excludeInsertions ) { - return new ve.dm.SurfaceFragment( this, selection || this.selection, noAutoSelect, excludeInsertions ); + selection = selection || this.selection; + // TODO: Use a factory pattery to generate fragments + return this.sourceMode ? + new ve.dm.SourceSurfaceFragment( this, selection, noAutoSelect, excludeInsertions ) : + new ve.dm.SurfaceFragment( this, selection, noAutoSelect, excludeInsertions ); }; /** diff --git a/src/init/ve.init.Target.js b/src/init/ve.init.Target.js index 3e8042a..7541249 100644 --- a/src/init/ve.init.Target.js +++ b/src/init/ve.init.Target.js @@ -427,6 +427,30 @@ }; /** + * Parse document string into an HTML document + * + * @param {string} documentString Document + * @param {string} mode Editing mode + * @return {HTMLDocument} HTML document + */ +ve.init.Target.prototype.parseDocument = function ( documentString, mode ) { + var doc; + if ( mode === 'source' ) { + // Parse as plain text in source mode + doc = ve.createDocumentFromHtml( '' ); + + documentString.split( '\n' ).forEach( function ( line ) { + var p = doc.createElement( 'p' ); + p.appendChild( doc.createTextNode( line ) ); + doc.body.appendChild( p ); + } ); + } else { + doc = ve.createDocumentFromHtml( documentString ); + } + return doc; +}; + +/** * Handle focus events from a surface's view * * @param {ve.ui.Surface} surface Surface firing the event diff --git a/src/ui/styles/ve.ui.Surface.css b/src/ui/styles/ve.ui.Surface.css index 8fca85e..5be34c2 100644 --- a/src/ui/styles/ve.ui.Surface.css +++ b/src/ui/styles/ve.ui.Surface.css @@ -31,3 +31,12 @@ overflow: hidden; text-overflow: ellipsis; } + +.ve-ui-surface-source .ve-ce-documentNode { + font-size: 1.2821em; + font-family: monospace; +} + +.ve-ui-surface-source .ve-ce-paragraphNode { + margin: 0; +} diff --git a/src/ui/ve.ui.Surface.js b/src/ui/ve.ui.Surface.js index a82719f..295944c 100644 --- a/src/ui/ve.ui.Surface.js +++ b/src/ui/ve.ui.Surface.js @@ -85,7 +85,10 @@ // Initialization this.$menus.append( this.context.$element ); this.$element - .addClass( 've-ui-surface' ) + // The following classes are used here: + // * ve-ui-surface-visual + // * ve-ui-surface-source + .addClass( 've-ui-surface ve-ui-surface-' + this.mode ) .append( this.view.$element ); this.view.$element.after( this.localOverlay.$element ); this.localOverlay.$element.append( this.$selections, this.$blockers, this.$controls, this.$menus ); @@ -186,6 +189,23 @@ * @return {HTMLDocument} HTML document */ ve.ui.Surface.prototype.getDom = function () { + var i, l, text, data; + + // Optimized converter for source mode, which contains only + // plain text or paragraphs. + if ( this.getMode() === 'source' ) { + text = ''; + data = this.getModel().getDocument().data.data; + for ( i = 0, l = data.length; i < l; i++ ) { + if ( data[ i ].type === '/paragraph' && data[ i + 1 ].type === 'paragraph' ) { + text += '\n'; + } else if ( !data[ i ].type ) { + text += data[ i ]; + } + } + + return text; + } return ve.dm.converter.getDomFromModel( this.getModel().getDocument() ); }; @@ -195,7 +215,9 @@ * @return {string} HTML */ ve.ui.Surface.prototype.getHtml = function () { - return ve.properInnerHtml( this.getDom().body ); + return this.getMode() === 'source' ? + this.getDom() : + ve.properInnerHtml( this.getDom().body ); }; /** @@ -232,7 +254,7 @@ * @return {ve.dm.Surface} Surface model */ ve.ui.Surface.prototype.createModel = function ( doc ) { - return new ve.dm.Surface( doc ); + return new ve.dm.Surface( doc, { sourceMode: this.getMode() === 'source' } ); }; /** -- To view, visit https://gerrit.wikimedia.org/r/324525 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I02f3849027a6652701c2d354d1b77ece9d1a56c1 Gerrit-PatchSet: 1 Gerrit-Project: VisualEditor/VisualEditor Gerrit-Branch: master Gerrit-Owner: Esanders <esand...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits