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

Change subject: Replace ve.dm.Model#matchesAttributeSpec
......................................................................


Replace ve.dm.Model#matchesAttributeSpec

Attribute matcher specifications can take any form from the list below:

* Object with an array 'whitelist' property, containing any mix of booleans,
  regular expressions, and strings.
* Object with an array 'blacklist' property (same composition as above).
* Object with both 'whitelist' and 'blacklist' properties (ditto).
* Array (ditto).
* Regular expression.
* Boolean.

This flexibility is purchased at the cost of a lot of branching in a very hot
code path (ve.dm.Model#matchesAttributeSpec gets called for every property of
every element).

This patch allows attribute specifications to be either a boolean value or a
function which takes an attribute name and returns a boolean value. This allows
for dynamic and complex specifications without incurring costs for the common
cases.

Change-Id: Ia0e0fe4fc2385dab67c816148cfc362a39413dac
---
M build/modules.json
M src/ce/ve.ce.View.js
M src/dm/nodes/ve.dm.BlockImageNode.js
M src/dm/nodes/ve.dm.TableCellNode.js
M src/dm/ve.dm.Converter.js
M src/dm/ve.dm.Model.js
D tests/dm/ve.dm.Model.test.js
M tests/index.html
8 files changed, 24 insertions(+), 193 deletions(-)

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



diff --git a/build/modules.json b/build/modules.json
index 07a9752..ba37a62 100644
--- a/build/modules.json
+++ b/build/modules.json
@@ -458,7 +458,6 @@
                        "tests/dm/ve.dm.SurfaceFragment.test.js",
                        "tests/dm/ve.dm.ModelRegistry.test.js",
                        "tests/dm/ve.dm.MetaList.test.js",
-                       "tests/dm/ve.dm.Model.test.js",
                        "tests/dm/lineardata/ve.dm.FlatLinearData.test.js",
                        "tests/dm/lineardata/ve.dm.ElementLinearData.test.js",
                        "tests/dm/lineardata/ve.dm.MetaLinearData.test.js",
diff --git a/src/ce/ve.ce.View.js b/src/ce/ve.ce.View.js
index 4a4e98a..168f51c 100644
--- a/src/ce/ve.ce.View.js
+++ b/src/ce/ve.ce.View.js
@@ -83,14 +83,16 @@
  * @property {boolean|string|RegExp|Array|Object}
  * @inheritable
  */
-ve.ce.View.static.renderHtmlAttributes = [
-       'abbr', 'about', 'align', 'alt', 'axis', 'bgcolor', 'border', 
'cellpadding', 'cellspacing',
-       'char', 'charoff', 'cite', 'class', 'clear', 'color', 'colspan', 
'datatype', 'datetime',
-       'dir', 'face', 'frame', 'headers', 'height', 'href', 'id', 'itemid', 
'itemprop', 'itemref',
-       'itemscope', 'itemtype', 'lang', 'noshade', 'nowrap', 'property', 
'rbspan', 'rel',
-       'resource', 'rev', 'rowspan', 'rules', 'scope', 'size', 'span', 'src', 
'start', 'style',
-       'summary', 'title', 'type', 'typeof', 'valign', 'value', 'width'
-];
+ve.ce.View.static.renderHtmlAttributes = function ( attribute ) {
+       return ve.indexOf( attribute, [
+               'abbr', 'about', 'align', 'alt', 'axis', 'bgcolor', 'border', 
'cellpadding', 'cellspacing',
+               'char', 'charoff', 'cite', 'class', 'clear', 'color', 
'colspan', 'datatype', 'datetime',
+               'dir', 'face', 'frame', 'headers', 'height', 'href', 'id', 
'itemid', 'itemprop', 'itemref',
+               'itemscope', 'itemtype', 'lang', 'noshade', 'nowrap', 
'property', 'rbspan', 'rel',
+               'resource', 'rev', 'rowspan', 'rules', 'scope', 'size', 'span', 
'src', 'start', 'style',
+               'summary', 'title', 'type', 'typeof', 'valign', 'value', 'width'
+       ] ) !== -1;
+};
 
 /* Methods */
 
diff --git a/src/dm/nodes/ve.dm.BlockImageNode.js 
b/src/dm/nodes/ve.dm.BlockImageNode.js
index 9b8bd15..18b270b 100644
--- a/src/dm/nodes/ve.dm.BlockImageNode.js
+++ b/src/dm/nodes/ve.dm.BlockImageNode.js
@@ -37,8 +37,8 @@
 
 ve.dm.BlockImageNode.static.name = 'blockImage';
 
-ve.dm.BlockImageNode.static.preserveHtmlAttributes = {
-       blacklist: [ 'src', 'width', 'height', 'href' ]
+ve.dm.BlockImageNode.static.preserveHtmlAttributes = function ( attribute ) {
+       return ve.indexOf( attribute, [ 'src', 'width', 'height', 'href' ] ) 
=== -1;
 };
 
 ve.dm.BlockImageNode.static.handlesOwnChildren = true;
diff --git a/src/dm/nodes/ve.dm.TableCellNode.js 
b/src/dm/nodes/ve.dm.TableCellNode.js
index ba84d5b..5b52d13 100644
--- a/src/dm/nodes/ve.dm.TableCellNode.js
+++ b/src/dm/nodes/ve.dm.TableCellNode.js
@@ -39,8 +39,8 @@
 ve.dm.TableCellNode.static.matchTagNames = [ 'td', 'th' ];
 
 // Blacklisting 'colspan' and 'rowspan' as they are managed explicitly
-ve.dm.TableCellNode.static.preserveHtmlAttributes = {
-       blacklist: ['colspan', 'rowspan']
+ve.dm.TableCellNode.static.preserveHtmlAttributes = function ( attribute ) {
+       return attribute !== 'colspan' && attribute !== 'rowspan';
 };
 
 /* Static Methods */
diff --git a/src/dm/ve.dm.Converter.js b/src/dm/ve.dm.Converter.js
index 74ee194..025a3e3 100644
--- a/src/dm/ve.dm.Converter.js
+++ b/src/dm/ve.dm.Converter.js
@@ -134,16 +134,16 @@
  * @static
  * @param {HTMLElement[]} originalDomElements Array of DOM elements to render 
from
  * @param {HTMLElement[]} targetDomElements Array of DOM elements to render 
onto
- * @param {boolean|string|RegExp|Array|Object} [spec=true] Attribute 
specification, see ve.dm.Model
+ * @param {boolean|Function} [filter=true] Attribute filter
  * @param {boolean} [computed=false] If true, use the computed values of 
attributes where available
  * @param {boolean} [deep=false] Recurse into child nodes
  */
-ve.dm.Converter.renderHtmlAttributeList = function ( originalDomElements, 
targetDomElements, spec, computed, deep ) {
+ve.dm.Converter.renderHtmlAttributeList = function ( originalDomElements, 
targetDomElements, filter, computed, deep ) {
        var i, ilen, j, jlen, attrs, value;
-       if ( spec === undefined ) {
-               spec = true;
+       if ( filter === undefined ) {
+               filter = true;
        }
-       if ( spec === false ) {
+       if ( filter === false ) {
                return;
        }
 
@@ -158,7 +158,7 @@
                for ( j = 0, jlen = attrs.length; j < jlen; j++ ) {
                        if (
                                !targetDomElements[i].hasAttribute( 
attrs[j].name ) &&
-                               ( spec === true || 
ve.dm.Model.matchesAttributeSpec( attrs[j].name, spec ) )
+                               ( filter === true || filter( attrs[j].name ) )
                        ) {
                                if ( computed && 
ve.dm.Converter.computedAttributes.indexOf( attrs[j].name ) !== -1 ) {
                                        value = 
originalDomElements[i][attrs[j].name];
@@ -168,7 +168,7 @@
                                targetDomElements[i].setAttribute( 
attrs[j].name, value );
                        }
 
-                       if ( spec === true || ve.dm.Model.matchesAttributeSpec( 
attrs[j].name, spec ) ) {
+                       if ( filter === true || filter( attrs[j].name ) ) {
                                value = computed && 
ve.dm.Converter.computedAttributes.indexOf( attrs[j].name ) !== -1 ?
                                        originalDomElements[i][attrs[j].name] :
                                        attrs[j].value;
@@ -180,7 +180,7 @@
                        ve.dm.Converter.renderHtmlAttributeList(
                                originalDomElements[i].children,
                                targetDomElements[i].children,
-                               spec,
+                               filter,
                                computed,
                                true
                        );
diff --git a/src/dm/ve.dm.Model.js b/src/dm/ve.dm.Model.js
index bfb21b9..809d50e 100644
--- a/src/dm/ve.dm.Model.js
+++ b/src/dm/ve.dm.Model.js
@@ -178,63 +178,15 @@
  *
  * - true, to preserve all attributes (default)
  * - false, to preserve none
- * - a string, to preserve only that attribute
- * - a regular expression matching attributes that should be preserved
- * - an array of strings or regular expressions
- * - an object with the following keys:
- *   - blacklist: specification of attributes not to preserve 
(boolean|string|RegExp|Array)
- *   - whitelist: specification of attributes to preserve
- *
- * If only a blacklist is specified, all attributes will be preserved except 
the ones matching
- * the blacklist. If only a whitelist is specified, only those attributes 
matching the whitelist
- * will be preserved. If both are specified, only attributes that both match 
the whitelist and
- * do not match the blacklist will be preserved.
+ * - a function that takes an attribute name and returns true or false
  *
  * @static
- * @property {boolean|string|RegExp|Array|Object}
+ * @property {boolean|Function}
  * @inheritable
  */
 ve.dm.Model.static.preserveHtmlAttributes = true;
 
 /* Static methods */
-
-/**
- * Determine whether an attribute name matches an attribute specification.
- *
- * @param {string} attribute Attribute name
- * @param {boolean|string|RegExp|Array|Object} spec Attribute specification, 
see #preserveHtmlAttributes
- * @returns {boolean} Attribute matches spec
- */
-ve.dm.Model.matchesAttributeSpec = function ( attribute, spec ) {
-       function matches( subspec ) {
-               if ( subspec instanceof RegExp ) {
-                       return !!subspec.exec( attribute );
-               }
-               if ( typeof subspec === 'boolean' ) {
-                       return subspec;
-               }
-               return attribute === subspec;
-       }
-
-       function matchesArray( specArray ) {
-               var i, len;
-               if ( !Array.isArray( specArray ) ) {
-                       specArray = [ specArray ];
-               }
-               for ( i = 0, len = specArray.length; i < len; i++ ) {
-                       if ( matches( specArray[i] ) ) {
-                               return true;
-                       }
-               }
-               return false;
-       }
-
-       if ( spec.whitelist === undefined && spec.blacklist === undefined ) {
-               // Not an object, treat spec as a whitelist
-               return matchesArray( spec );
-       }
-       return matchesArray( spec.whitelist || true ) && !matchesArray( 
spec.blacklist || false );
-};
 
 /**
  * Get hash object of a linear model data element.
diff --git a/tests/dm/ve.dm.Model.test.js b/tests/dm/ve.dm.Model.test.js
deleted file mode 100644
index e38a4e2..0000000
--- a/tests/dm/ve.dm.Model.test.js
+++ /dev/null
@@ -1,121 +0,0 @@
-/*!
- * VisualEditor DataModel Model tests.
- *
- * @copyright 2011-2015 VisualEditor Team and others; see 
http://ve.mit-license.org
- */
-
-QUnit.module( 've.dm.Model' );
-
-QUnit.test( 'matchesAttributeSpec', function ( assert ) {
-       var i, cases = [
-               {
-                       spec: true,
-                       attr: 'foo',
-                       result: true,
-                       msg: 'true matches anything'
-               },
-               {
-                       spec: false,
-                       attr: 'foo',
-                       result: false,
-                       msg: 'false matches nothing'
-               },
-               {
-                       spec: 'foo',
-                       attr: 'foo',
-                       result: true,
-                       msg: 'string matches exact value'
-               },
-               {
-                       spec: 'foo',
-                       attr: 'bar',
-                       result: false,
-                       msg: 'string does not match anything else'
-               },
-               {
-                       spec: /^foo/,
-                       attr: 'foobar',
-                       result: true,
-                       msg: 'regex matches'
-               },
-               {
-                       spec: /^foo/,
-                       attr: 'barfoo',
-                       result: false,
-                       msg: 'regex does not match anything else'
-               },
-               {
-                       spec: [ 'foo', /^bar/ ],
-                       attr: 'foo',
-                       result: true,
-                       msg: 'array of string and regex matches string'
-               },
-               {
-                       spec: [ 'foo', /^bar/ ],
-                       attr: 'barbaz',
-                       result: true,
-                       msg: 'array of string and regex matches regex match'
-               },
-               {
-                       spec: {
-                               blacklist: 'foo'
-                       },
-                       attr: 'foo',
-                       result: false,
-                       msg: 'blacklisted attribute does not match'
-               },
-               {
-                       spec: {
-                               blacklist: 'foo'
-                       },
-                       attr: 'bar',
-                       result: true,
-                       msg: 'non-blacklisted attribute matches'
-               },
-               {
-                       spec: {
-                               whitelist: /^foo/,
-                               blacklist: [ 'foobar', 'foobaz' ]
-                       },
-                       attr: 'foobar',
-                       result: false,
-                       msg: 'blacklist overrides whitelist'
-               },
-               {
-                       spec: {
-                               whitelist: /^foo/,
-                               blacklist:  [ 'foobar', 'foobaz' ]
-                       },
-                       attr: 'foobaz',
-                       result: false,
-                       msg: 'blacklist overrides whitelist (2)'
-               },
-               {
-                       spec: {
-                               whitelist: /^foo/,
-                               blacklist: [ 'foobar', 'foobaz' ]
-                       },
-                       attr: 'fooquux',
-                       result: true,
-                       msg: 'attribute on whitelist and not on blacklist 
matches'
-               },
-               {
-                       spec: {
-                               whitelist: /^foo/,
-                               blacklist: [ 'foobar', 'foobaz' ]
-                       },
-                       attr: 'bar',
-                       result: false,
-                       msg: 'attribute on neither whitelist nor blacklist does 
not match'
-               }
-       ];
-
-       QUnit.expect( cases.length );
-       for ( i = 0; i < cases.length; i++ ) {
-               assert.strictEqual(
-                       ve.dm.Model.matchesAttributeSpec( cases[i].attr, 
cases[i].spec ),
-                       cases[i].result,
-                       cases[i].msg
-               );
-       }
-} );
diff --git a/tests/index.html b/tests/index.html
index 5e2f0ea..81917c4 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -352,7 +352,6 @@
                <script 
src="../tests/dm/ve.dm.SurfaceFragment.test.js"></script>
                <script src="../tests/dm/ve.dm.ModelRegistry.test.js"></script>
                <script src="../tests/dm/ve.dm.MetaList.test.js"></script>
-               <script src="../tests/dm/ve.dm.Model.test.js"></script>
                <script 
src="../tests/dm/lineardata/ve.dm.FlatLinearData.test.js"></script>
                <script 
src="../tests/dm/lineardata/ve.dm.ElementLinearData.test.js"></script>
                <script 
src="../tests/dm/lineardata/ve.dm.MetaLinearData.test.js"></script>

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Ia0e0fe4fc2385dab67c816148cfc362a39413dac
Gerrit-PatchSet: 6
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Ori.livneh <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: Ori.livneh <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to