From 42b82de8ea53b0bbd5bf848a21df58e5146d0e8e Mon Sep 17 00:00:00 2001 From: Andreas Weber Date: Sun, 19 Mar 2017 13:21:30 +0100 Subject: [PATCH] refactor attribute handling and add special handling for draggable --- lib/helpers.js | 109 +++++++++++++++++++++++++++++++----------- lib/list.js | 3 +- test/minimize-test.js | 25 +++++++++- 3 files changed, 107 insertions(+), 30 deletions(-) diff --git a/lib/helpers.js b/lib/helpers.js index 7ad09bb..4b61295 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -108,7 +108,77 @@ Helpers.prototype.tag = function tag(element) { return '<' + element.name + this.attributes(element) + '>'; }; - +/** Is the attribute allowed on the HTML element? If so, allow special + * treatment. If not, then just return the full attribute and its value. + * @param {String} name name of the element + * @param {String} key name of the attribute + * @return {Boolean} + */ +Helpers.prototype.allowAttributeInNode = function (name, key) { + var attrDefinition = Object.hasOwnProperty.call(list.attributes, key) && list.attributes[key]; + if (attrDefinition) { + return attrDefinition === '*' || ~attrDefinition.indexOf(name); + } + return ~key.indexOf('data-'); +} +/** allow remove of attribute + * @param {String} name name of the element + * @param {String} key name of the attribute + * @param {String} value value of the attribute + * @return {Boolean} + */ +Helpers.prototype.allowRemoveAttributeValue = function (key, value) { + if (this.config.spare) { + return false; + } + if (!value) { + return true; + } + if (key === 'draggable') { + return value === 'auto'; + } + return (value === 'true' || value === key) && ~list.redundant.indexOf(key); +}; +/** allow remove of attribute value + * @param {String} name name of the element + * @param {String} key name of the attribute + * @param {String} value value of the attribute + * @return {Boolean} + */ +Helpers.prototype.allowRemoveAttribute = function (name, key, value) { + if (this.config.empty){ + return false; + } + if (/data|itemscope/.test(key)) { + return false; + } + if (key === 'draggable') { + if (value === 'true') { + return ~['img','a'].indexOf(name) + } else if (value === 'false') { + return !~['img','a'].indexOf(name) + } + } + if (value) { + return false; + } + return !~list.redundant.indexOf(key); +}; +/** create attribute string + * @param {String} key name of the attribute + * @param {String} value value of the attribute + * @return {String} + */ +Helpers.prototype.toAttributeString = function (key, value) { + var result = ''; + if (key) { + result += ' ' + key; + if (value !== null) { + result += '=' + this.quote(!this.config.whitespace ? compact(value).trim() : value); + } + } + return result; +}; /** * Loop set of attributes belonging to an element. Surrounds attributes with * quotes if required, omits if not. @@ -121,41 +191,26 @@ Helpers.prototype.attributes = function attributes(element) { var attr = element.attribs , self = this , name = element.name - , value, bool, allowed; + , value; if (!attr || typeof attr !== 'object') return ''; return Object.keys(attr).reduce(function (result, key) { value = attr[key]; - bool = ~list.redundant.indexOf(key); - - // - // Is the attribute allowed on the HTML element? If so, allow special - // treatment. If not, then just return the full attribute and its value. - // - allowed = Object.hasOwnProperty.call(list.attributes, key) && list.attributes[key]; - allowed = allowed - ? allowed === '*' || ~allowed.indexOf(name) - : ~key.indexOf('data-'); - - // - // Remove attributes that are empty, not boolean and no semantic value. - // - if (!self.config.empty && !/data|itemscope/.test(key) && !bool && !value && allowed) return result; - // - // Boolean attributes should be added sparse, also unset attributes - // should remain unset if retained with `empty` option. - // - result = result + ' ' + key; - if (!self.config.spare) { - if (!value.length) return result; - if (allowed && bool && (value === key || 'true' === value)) return result; + if (self.allowAttributeInNode(name, key)) { + if (self.allowRemoveAttribute(name, key, value)) { + key = null; + value = null; + }else if (self.allowRemoveAttributeValue(key, value)) { + value = null; + } + } else if (!value) { + value = null; } - // // Return full attribute with value. // - return result + '=' + self.quote(!self.config.whitespace ? compact(value).trim() : value); + return result + self.toAttributeString(key, value); }, ''); }; diff --git a/lib/list.js b/lib/list.js index 1eec666..91b0624 100644 --- a/lib/list.js +++ b/lib/list.js @@ -25,7 +25,7 @@ exports.redundant = [ 'async', 'defer', 'formnovalidate', 'checked', 'scoped', 'reversed', 'selected', 'autoplay', 'controls', 'loop', 'muted', 'seamless', 'default', 'ismap', 'novalidate', 'open', 'typemustmatch', 'truespeed', - 'itemscope', 'autocomplete', 'download', 'draggable', 'novalidate', + 'itemscope', 'autocomplete', 'download', 'novalidate', 'sortable', 'spellcheck' ]; @@ -137,7 +137,6 @@ exports.attributes = { 'dirname': ['input', 'textarea'], 'disabled': ['button', 'command', 'fieldset', 'input', 'keygen', 'optgroup', 'option', 'select', 'textarea'], 'download': ['a', 'area'], - 'draggable': '*', 'dropzone': '*', 'enctype': 'form', 'for': ['label', 'output'], diff --git a/test/minimize-test.js b/test/minimize-test.js index 13bee6a..6cef314 100644 --- a/test/minimize-test.js +++ b/test/minimize-test.js @@ -306,7 +306,30 @@ describe('Minimize', function () { done(); }); }); - + it('should remove value from draggable attribute', function (done) { + minimize.parse('
', function (error, result) { + expect(result).to.equal('
'); + done(); + }); + }); + it('should not remove value from draggable attribute', function (done) { + minimize.parse('
', function (error, result) { + expect(result).to.equal('
'); + done(); + }); + }); + it('should remove draggable attribute from element', function (done) { + minimize.parse('', function (error, result) { + expect(result).to.equal(''); + done(); + }); + }); + it('should remove draggable attribute from element', function (done) { + minimize.parse('
', function (error, result) { + expect(result).to.equal('
'); + done(); + }); + }); it('should remove CDATA from scripts', function (done) { minimize.parse(html.cdata, function (error, result) { expect(result).to.equal("");