Ori.livneh has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/191287

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 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
5 files changed, 29 insertions(+), 65 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor 
refs/changes/87/191287/1

diff --git a/src/ce/ve.ce.View.js b/src/ce/ve.ce.View.js
index d69bb81..3bd2e35 100644
--- a/src/ce/ve.ce.View.js
+++ b/src/ce/ve.ce.View.js
@@ -79,14 +79,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 f675066..bd73bcd 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.storeHtmlAttributes = {
-       blacklist: [ 'src', 'width', 'height', 'href' ]
+ve.dm.BlockImageNode.static.storeHtmlAttributes = 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 53b3d7c..a017a51 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.storeHtmlAttributes = {
-       blacklist: ['colspan', 'rowspan']
+ve.dm.TableCellNode.static.storeHtmlAttributes = 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 686739a..6eca453 100644
--- a/src/dm/ve.dm.Converter.js
+++ b/src/dm/ve.dm.Converter.js
@@ -143,21 +143,21 @@
  * @param {Object[]} [attributeList] Existing attribute list to populate; used 
for recursion
  * @returns {Object[]|undefined} Attribute list, or undefined if empty
  */
-ve.dm.Converter.buildHtmlAttributeList = function ( domElements, spec, deep, 
attributeList ) {
+ve.dm.Converter.buildHtmlAttributeList = function ( domElements, filter, deep, 
attributeList ) {
        var i, ilen, j, jlen, domAttributes, childList, attrName,
                empty = true;
        attributeList = attributeList || [];
        for ( i = 0, ilen = domElements.length; i < ilen; i++ ) {
+               attributeList.push( { values: {}, computed: {} } );
+               if ( !filter ) {
+                       continue;
+               }
                domAttributes = domElements[i].attributes || [];
-               attributeList[i] = { values: {} };
                for ( j = 0, jlen = domAttributes.length; j < jlen; j++ ) {
                        attrName = domAttributes[j].name;
-                       if ( ve.dm.Model.matchesAttributeSpec( attrName, spec ) 
) {
+                       if ( filter === true || filter( attrName ) ) {
                                attributeList[i].values[attrName] = 
domAttributes[j].value;
                                if ( ve.indexOf( attrName, 
this.computedAttributes ) !== -1 ) {
-                                       if ( !attributeList[i].computed ) {
-                                               attributeList[i].computed = {};
-                                       }
                                        attributeList[i].computed[attrName] = 
domElements[i][attrName];
                                }
                                empty = false;
@@ -169,7 +169,7 @@
                                // Use .children rather than .childNodes so we 
don't mess around with things that
                                // can't have attributes anyway. Unfortunately, 
non-element nodes have .children
                                // set to undefined so we have to coerce it to 
an array in that case.
-                               domElements[i].children || [], spec, deep, 
attributeList[i].children
+                               domElements[i].children || [], filter, deep, 
attributeList[i].children
                        );
                        if ( childList ) {
                                empty = false;
@@ -190,16 +190,16 @@
  * @static
  * @param {Object[]} attributeList Attribute list, see buildHtmlAttributeList()
  * @param {HTMLElement[]} domElements 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} [overwrite=false] If true, overwrite attributes that are 
already set
  */
-ve.dm.Converter.renderHtmlAttributeList = function ( attributeList, 
domElements, spec, computed, overwrite ) {
+ve.dm.Converter.renderHtmlAttributeList = function ( attributeList, 
domElements, filter, computed, overwrite ) {
        var i, ilen, key, values, value;
-       if ( spec === undefined ) {
-               spec = true;
+       if ( filter === undefined ) {
+               filter = true;
        }
-       if ( spec === false ) {
+       if ( filter === false ) {
                return;
        }
        for ( i = 0, ilen = attributeList.length; i < ilen; i++ ) {
@@ -208,7 +208,7 @@
                }
                values = attributeList[i].values;
                for ( key in values ) {
-                       if ( ve.dm.Model.matchesAttributeSpec( key, spec ) ) {
+                       if ( filter === true || filter( key ) ) {
                                value = computed && attributeList[i].computed 
&& attributeList[i].computed[key] || values[key];
                                if ( value === undefined ) {
                                        domElements[i].removeAttribute( key );
@@ -219,7 +219,7 @@
                }
                if ( attributeList[i].children ) {
                        ve.dm.Converter.renderHtmlAttributeList(
-                               attributeList[i].children, 
domElements[i].children, spec, computed, overwrite
+                               attributeList[i].children, 
domElements[i].children, filter, computed, overwrite
                        );
                }
        }
@@ -783,7 +783,7 @@
                                                
!this.nodeFactory.doesNodeHandleOwnChildren( childDataElements[0].type )
                                        ) {
                                                htmlAttributes = 
ve.dm.Converter.buildHtmlAttributeList(
-                                                       childNodes, 
modelClass.static.storeHtmlAttributes
+                                                       childNodes, 
modelClass.static.storeHtmlAttributes, false
                                                );
                                                if ( htmlAttributes ) {
                                                        
childDataElements[0].htmlAttributes = htmlAttributes;
diff --git a/src/dm/ve.dm.Model.js b/src/dm/ve.dm.Model.js
index 08fc63d..fe4b9fd 100644
--- a/src/dm/ve.dm.Model.js
+++ b/src/dm/ve.dm.Model.js
@@ -205,44 +205,6 @@
 /* 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 #storeHtmlAttributes
- * @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.
  *
  * @static

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ia0e0fe4fc2385dab67c816148cfc362a39413dac
Gerrit-PatchSet: 1
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Ori.livneh <[email protected]>

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

Reply via email to