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

Reply via email to