jenkins-bot has submitted this change and it was merged.

Change subject: Create SourceSurfaceFragment for editing source documents
......................................................................


Create SourceSurfaceFragment for editing source documents

Intercepts surface fragment modification calls and replaces content
with source equivalent. Sub-classes can override convertDocument to
convert content to a different source language (e.g. wikitext).

Change-Id: I61f3ebc153f1e14201ba16dee25f3f062977d247
---
M .jsduck/categories.json
M build/modules.json
M demos/ve/desktop.html
M demos/ve/mobile.html
M src/ce/ve.ce.Surface.js
M src/dm/lineardata/ve.dm.ElementLinearData.js
A src/dm/ve.dm.SourceSurfaceFragment.js
M src/ui/ve.ui.DesktopSurface.js
M tests/dm/lineardata/ve.dm.ElementLinearData.test.js
M tests/index.html
10 files changed, 154 insertions(+), 17 deletions(-)

Approvals:
  Jforrester: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/.jsduck/categories.json b/.jsduck/categories.json
index e5d9e10..53fbe89 100644
--- a/.jsduck/categories.json
+++ b/.jsduck/categories.json
@@ -60,7 +60,7 @@
                                        "ve.dm.APIResultsProvider",
                                        "ve.dm.APIResultsQueue",
                                        "ve.dm.Surface",
-                                       "ve.dm.SurfaceFragment",
+                                       "ve.dm.*SurfaceFragment",
                                        "ve.dm.*Selection",
                                        "ve.dm.Transaction",
                                        "ve.dm.TransactionProcessor",
diff --git a/build/modules.json b/build/modules.json
index 653b73b..02a714a 100644
--- a/build/modules.json
+++ b/build/modules.json
@@ -257,6 +257,7 @@
                        "src/dm/ve.dm.Selection.js",
                        "src/dm/ve.dm.Surface.js",
                        "src/dm/ve.dm.SurfaceFragment.js",
+                       "src/dm/ve.dm.SourceSurfaceFragment.js",
                        "src/dm/ve.dm.DataString.js",
                        "src/dm/ve.dm.Document.js",
                        "src/dm/ve.dm.DocumentSlice.js",
diff --git a/demos/ve/desktop.html b/demos/ve/desktop.html
index 551e458..5eb7bcb 100644
--- a/demos/ve/desktop.html
+++ b/demos/ve/desktop.html
@@ -213,6 +213,7 @@
                <script src="../../src/dm/ve.dm.Selection.js"></script>
                <script src="../../src/dm/ve.dm.Surface.js"></script>
                <script src="../../src/dm/ve.dm.SurfaceFragment.js"></script>
+               <script 
src="../../src/dm/ve.dm.SourceSurfaceFragment.js"></script>
                <script src="../../src/dm/ve.dm.DataString.js"></script>
                <script src="../../src/dm/ve.dm.Document.js"></script>
                <script src="../../src/dm/ve.dm.DocumentSlice.js"></script>
diff --git a/demos/ve/mobile.html b/demos/ve/mobile.html
index 1a64a20..e52ff99 100644
--- a/demos/ve/mobile.html
+++ b/demos/ve/mobile.html
@@ -214,6 +214,7 @@
                <script src="../../src/dm/ve.dm.Selection.js"></script>
                <script src="../../src/dm/ve.dm.Surface.js"></script>
                <script src="../../src/dm/ve.dm.SurfaceFragment.js"></script>
+               <script 
src="../../src/dm/ve.dm.SourceSurfaceFragment.js"></script>
                <script src="../../src/dm/ve.dm.DataString.js"></script>
                <script src="../../src/dm/ve.dm.Document.js"></script>
                <script src="../../src/dm/ve.dm.DocumentSlice.js"></script>
diff --git a/src/ce/ve.ce.Surface.js b/src/ce/ve.ce.Surface.js
index 129c40c..2336fe7 100644
--- a/src/ce/ve.ce.Surface.js
+++ b/src/ce/ve.ce.Surface.js
@@ -2192,7 +2192,7 @@
 
                // If the external HTML turned out to be plain text after
                // sanitization then run it as a plain text transfer item
-               if ( pastedDocumentModel.data.isPlainText( true, contextRange ) 
) {
+               if ( pastedDocumentModel.data.isPlainText( contextRange, true ) 
) {
                        pastedText = pastedDocumentModel.data.getText( true, 
contextRange );
                        if ( pastedText ) {
                                handled = this.handleDataTransferItems(
diff --git a/src/dm/lineardata/ve.dm.ElementLinearData.js 
b/src/dm/lineardata/ve.dm.ElementLinearData.js
index d687070..a8c4b3c 100644
--- a/src/dm/lineardata/ve.dm.ElementLinearData.js
+++ b/src/dm/lineardata/ve.dm.ElementLinearData.js
@@ -617,21 +617,28 @@
 /**
  * Check if the data is just plain (un-annotated) text
  *
- * @param {boolean} [allowNonContentNodes] Include non-content nodes in the 
definition of plain text, e.g. paragraphs, headings, lists
  * @param {ve.Range} [range] Range to get the data for. The whole data set if 
not specified.
+ * @param {boolean} [allowNonContentNodes] Include non-content nodes in the 
definition of plain text, e.g. paragraphs, headings, lists
+ * @param {boolean} [allowedTypes] Only allow specific non-content types
  * @return {boolean} The data is plain text
  */
-ve.dm.ElementLinearData.prototype.isPlainText = function ( 
allowNonContentNodes, range ) {
-       var i;
+ve.dm.ElementLinearData.prototype.isPlainText = function ( range, 
allowNonContentNodes, allowedTypes ) {
+       var i, type;
+
        range = range || new ve.Range( 0, this.getLength() );
 
        for ( i = range.start; i < range.end; i++ ) {
-               if (
-                       typeof this.data[ i ] === 'string' ||
-                       allowNonContentNodes && this.isElementData( i ) &&
-                       !ve.dm.nodeFactory.isNodeContent( this.getType( i ) )
-               ) {
+               if ( typeof this.data[ i ] === 'string' ) {
                        continue;
+               } else if ( ( allowNonContentNodes || allowedTypes ) && 
this.isElementData( i ) ) {
+                       type = this.getType( i );
+                       if ( allowedTypes && allowedTypes.indexOf( type ) !== 
-1 ) {
+                               continue;
+                       }
+                       if ( allowNonContentNodes && 
!ve.dm.nodeFactory.isNodeContent( type ) ) {
+                               continue;
+                       }
+                       return false;
                } else {
                        return false;
                }
diff --git a/src/dm/ve.dm.SourceSurfaceFragment.js 
b/src/dm/ve.dm.SourceSurfaceFragment.js
new file mode 100644
index 0000000..4dfa738
--- /dev/null
+++ b/src/dm/ve.dm.SourceSurfaceFragment.js
@@ -0,0 +1,126 @@
+/*!
+ * VisualEditor DataModel SourceSurfaceFragment class.
+ *
+ * @copyright 2011-2016 VisualEditor Team and others; see 
http://ve.mit-license.org
+ */
+
+/**
+ * Surface fragment for editing surfaces in source mode.
+ *
+ * @class
+ * @extends ve.dm.SurfaceFragment
+ *
+ * @constructor
+ * @param {ve.dm.Document} doc
+ */
+ve.dm.SourceSurfaceFragment = function VeDmSourceSurfaceFragment() {
+       // Parent constructors
+       ve.dm.SourceSurfaceFragment.super.apply( this, arguments );
+};
+
+/* Inheritance */
+
+OO.inheritClass( ve.dm.SourceSurfaceFragment, ve.dm.SurfaceFragment );
+
+/**
+ * @inheritdoc
+ */
+ve.dm.SourceSurfaceFragment.prototype.annotateContent = function () {
+       var fragment, tempDocument, rangeInDocument, tempSurfaceModel,
+               originalDocument = this.getDocument(),
+               coveringRange = this.getSelection().getCoveringRange();
+
+       if ( coveringRange ) {
+               tempDocument = originalDocument.shallowCloneFromRange( 
coveringRange );
+               rangeInDocument = tempDocument.originalRange;
+       } else {
+               tempDocument = new ve.dm.Document(
+                       [
+                               { type: 'paragraph', internal: { generated: 
'wrapper' } }, { type: '/paragraph' },
+                               { type: 'internalList' }, { type: 
'/internalList' }
+                       ],
+                       null, null, null, null,
+                       originalDocument.getLang(),
+                       originalDocument.getDir()
+               );
+               rangeInDocument = new ve.Range( 1 );
+       }
+       tempSurfaceModel = new ve.dm.Surface( tempDocument );
+       fragment = tempSurfaceModel.getLinearFragment( rangeInDocument );
+       fragment.annotateContent.apply( fragment, arguments );
+
+       this.insertDocument( fragment.getDocument() );
+
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+ve.dm.SourceSurfaceFragment.prototype.insertContent = function ( content ) {
+       var data;
+
+       if ( typeof content !== 'string' ) {
+               data = new ve.dm.ElementLinearData( new 
ve.dm.IndexValueStore(), content );
+               if ( !data.isPlainText( null, false, [ 'paragraph' ] ) ) {
+                       this.insertDocument( new ve.dm.Document( 
content.concat( [ { type: 'internalList' }, { type: '/internalList' } ] ) ) );
+                       return this;
+               }
+       }
+
+       // Parent method
+       return ve.dm.SourceSurfaceFragment.super.prototype.insertContent.call( 
this, content );
+};
+
+/**
+ * @inheritdoc
+ */
+ve.dm.SourceSurfaceFragment.prototype.insertDocument = function ( doc, 
newDocRange ) {
+       var conversionPromise,
+               range = this.getSelection().getCoveringRange(),
+               fragment = this;
+
+       if ( !range ) {
+               return this;
+       }
+
+       newDocRange = newDocRange || new ve.Range( 0, 
doc.getInternalList().getListNode().getOuterRange().start );
+
+       if ( doc.data.isPlainText( newDocRange, false, [ 'paragraph' ] ) ) {
+               return 
ve.dm.SourceSurfaceFragment.super.prototype.insertContent.call( this, 
doc.data.getDataSlice( newDocRange ) );
+       }
+
+       conversionPromise = this.convertDocument( doc ).always( function ( 
source ) {
+               fragment.removeContent();
+
+               if ( source ) {
+                       // Parent method
+                       
ve.dm.SourceSurfaceFragment.super.prototype.insertContent.call( fragment, 
source.trim() );
+               }
+       } );
+
+       return this;
+};
+
+/**
+ * Convert sub document to source text
+ *
+ * The default implementation converts to HTML synchronously.
+ *
+ * If the conversion is asynchornous it should lock the surface
+ * until complete.
+ *
+ * @param {ve.dm.Document} doc Document
+ * @return {jQuery.Promise} Promise with resolves with source, or rejects
+ */
+ve.dm.SourceSurfaceFragment.prototype.convertDocument = function ( doc ) {
+       if ( !doc.data.hasContent() ) {
+               return $.Deferred().reject().promise();
+       } else {
+               return $.Deferred().resolve(
+                       ve.properInnerHtml(
+                               ve.dm.converter.getDomFromModel( doc ).body
+                       )
+               ).promise();
+       }
+};
diff --git a/src/ui/ve.ui.DesktopSurface.js b/src/ui/ve.ui.DesktopSurface.js
index a7d826a..aaeca50 100644
--- a/src/ui/ve.ui.DesktopSurface.js
+++ b/src/ui/ve.ui.DesktopSurface.js
@@ -6,7 +6,7 @@
 
 /**
  * A surface is a top-level object which contains both a surface model and a 
surface view.
- * This is the mobile version of the surface.
+ * This is the desktop version of the surface.
  *
  * @class
  * @extends ve.ui.Surface
diff --git a/tests/dm/lineardata/ve.dm.ElementLinearData.test.js 
b/tests/dm/lineardata/ve.dm.ElementLinearData.test.js
index 89e4c32..9d11025 100644
--- a/tests/dm/lineardata/ve.dm.ElementLinearData.test.js
+++ b/tests/dm/lineardata/ve.dm.ElementLinearData.test.js
@@ -802,12 +802,12 @@
 QUnit.test( 'isPlainText', 6, function ( assert ) {
        var doc = ve.dm.example.createExampleDocument();
 
-       assert.strictEqual( doc.data.isPlainText( false, new ve.Range( 1, 2 ) 
), true, 'Plain text' );
-       assert.strictEqual( doc.data.isPlainText( true, new ve.Range( 1, 3 ) ), 
false, 'Annotated text' );
-       assert.strictEqual( doc.data.isPlainText( false, new ve.Range( 9, 11 ) 
), false, 'Paragraph and text (no content nodes)' );
-       assert.strictEqual( doc.data.isPlainText( true, new ve.Range( 9, 11 ) 
), true, 'Paragraph and text (content nodes allowed)' );
-       assert.strictEqual( doc.data.isPlainText( false, new ve.Range( 12, 26 ) 
), false, 'List (no content nodes)' );
-       assert.strictEqual( doc.data.isPlainText( true, new ve.Range( 12, 26 ) 
), true, 'List (content nodes allowed)' );
+       assert.strictEqual( doc.data.isPlainText( new ve.Range( 1, 2 ), false 
), true, 'Plain text' );
+       assert.strictEqual( doc.data.isPlainText( new ve.Range( 1, 3 ), true ), 
false, 'Annotated text' );
+       assert.strictEqual( doc.data.isPlainText( new ve.Range( 9, 11 ), false 
), false, 'Paragraph and text (no content nodes)' );
+       assert.strictEqual( doc.data.isPlainText( new ve.Range( 9, 11 ), true 
), true, 'Paragraph and text (content nodes allowed)' );
+       assert.strictEqual( doc.data.isPlainText( new ve.Range( 12, 26 ), false 
), false, 'List (no content nodes)' );
+       assert.strictEqual( doc.data.isPlainText( new ve.Range( 12, 26 ), true 
), true, 'List (content nodes allowed)' );
 } );
 
 QUnit.test( 'getText', 4, function ( assert ) {
diff --git a/tests/index.html b/tests/index.html
index e85fe35..12f2604 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -139,6 +139,7 @@
                <script src="../src/dm/ve.dm.Selection.js"></script>
                <script src="../src/dm/ve.dm.Surface.js"></script>
                <script src="../src/dm/ve.dm.SurfaceFragment.js"></script>
+               <script src="../src/dm/ve.dm.SourceSurfaceFragment.js"></script>
                <script src="../src/dm/ve.dm.DataString.js"></script>
                <script src="../src/dm/ve.dm.Document.js"></script>
                <script src="../src/dm/ve.dm.DocumentSlice.js"></script>

-- 
To view, visit https://gerrit.wikimedia.org/r/298285
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I61f3ebc153f1e14201ba16dee25f3f062977d247
Gerrit-PatchSet: 10
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <esand...@wikimedia.org>
Gerrit-Reviewer: DLynch <dly...@wikimedia.org>
Gerrit-Reviewer: Jforrester <jforres...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to