Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

Commit

Permalink
Merge pull request #444 from arv/class-list
Browse files Browse the repository at this point in the history
Invalidate shadow renderer when classList is mutated
  • Loading branch information
Steve Orvell committed May 27, 2014
2 parents d110a12 + ac1d697 commit 6a06e84
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 26 deletions.
1 change: 1 addition & 0 deletions shadowdom.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
'src/wrappers/node-interfaces.js',
'src/wrappers/CharacterData.js',
'src/wrappers/Text.js',
'src/wrappers/DOMTokenList.js',
'src/wrappers/Element.js',
'src/wrappers/HTMLElement.js',
'src/wrappers/HTMLCanvasElement.js',
Expand Down
46 changes: 46 additions & 0 deletions src/wrappers/DOMTokenList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2014 The Polymer Authors. All rights reserved.
// Use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE file.

(function(scope) {
'use strict';

function invalidateClass(el) {
scope.invalidateRendererBasedOnAttribute(el, 'class');
}

function DOMTokenList(impl, ownerElement) {
this.impl = impl;
this.ownerElement_ = ownerElement;
}

DOMTokenList.prototype = {
get length() {
return this.impl.length;
},
item: function(index) {
return this.impl.item(index);
},
contains: function(token) {
return this.impl.contains(token);
},
add: function() {
this.impl.add.apply(this.impl, arguments);
invalidateClass(this.ownerElement_);
},
remove: function() {
this.impl.remove.apply(this.impl, arguments);
invalidateClass(this.ownerElement_);
},
toggle: function(token) {
var rv = this.impl.toggle.apply(this.impl, arguments);
invalidateClass(this.ownerElement_);
return rv;
},
toString: function() {
return this.impl.toString();
}
};

scope.wrappers.DOMTokenList = DOMTokenList;
})(window.ShadowDOMPolyfill);
54 changes: 30 additions & 24 deletions src/wrappers/Element.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
var ChildNodeInterface = scope.ChildNodeInterface;
var GetElementsByInterface = scope.GetElementsByInterface;
var Node = scope.wrappers.Node;
var DOMTokenList = scope.wrappers.DOMTokenList;
var ParentNodeInterface = scope.ParentNodeInterface;
var SelectorsInterface = scope.SelectorsInterface;
var addWrapNodeListMethod = scope.addWrapNodeListMethod;
var enqueueMutation = scope.enqueueMutation;
var mixin = scope.mixin;
var oneOf = scope.oneOf;
var registerWrapper = scope.registerWrapper;
var unwrap = scope.unwrap;
var wrappers = scope.wrappers;

var OriginalElement = window.Element;
Expand Down Expand Up @@ -54,6 +56,8 @@
});
}

var classListTable = new WeakMap();

function Element(node) {
Node.call(this, node);
}
Expand Down Expand Up @@ -91,6 +95,31 @@

matches: function(selector) {
return originalMatches.call(this.impl, selector);
},

get classList() {
var list = classListTable.get(this);
if (!list) {
classListTable.set(this,
list = new DOMTokenList(unwrap(this).classList, this));
}
return list;
},

get className() {
return unwrap(this).className;
},

set className(v) {
this.setAttribute('class', v);
},

get id() {
return unwrap(this).id;
},

set id(v) {
this.setAttribute('id', v);
}
});

Expand All @@ -107,28 +136,6 @@
Element.prototype.createShadowRoot;
}

/**
* Useful for generating the accessor pair for a property that reflects an
* attribute.
*/
function setterDirtiesAttribute(prototype, propertyName, opt_attrName) {
var attrName = opt_attrName || propertyName;
Object.defineProperty(prototype, propertyName, {
get: function() {
return this.impl[propertyName];
},
set: function(v) {
this.impl[propertyName] = v;
invalidateRendererBasedOnAttribute(this, attrName);
},
configurable: true,
enumerable: true
});
}

setterDirtiesAttribute(Element.prototype, 'id');
setterDirtiesAttribute(Element.prototype, 'className', 'class');

mixin(Element.prototype, ChildNodeInterface);
mixin(Element.prototype, GetElementsByInterface);
mixin(Element.prototype, ParentNodeInterface);
Expand All @@ -137,8 +144,7 @@
registerWrapper(OriginalElement, Element,
document.createElementNS(null, 'x'));

// TODO(arv): Export setterDirtiesAttribute and apply it to more bindings
// that reflect attributes.
scope.invalidateRendererBasedOnAttribute = invalidateRendererBasedOnAttribute;
scope.matchesNames = matchesNames;
scope.wrappers.Element = Element;
})(window.ShadowDOMPolyfill);
104 changes: 104 additions & 0 deletions test/js/DOMTokenList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2014 The Polymer Authors. All rights reserved.
* Use of this source code is goverened by a BSD-style
* license that can be found in the LICENSE file.
*/

suite('DOMTokenList', function() {

test('instanceof', function() {
var div = document.createElement('div');
assert.instanceOf(div.classList, DOMTokenList);
});

test('identity', function() {
var div = document.createElement('div');
assert.equal(div.classList, div.classList);
});

test('length', function() {
var div = document.createElement('div');
var classList = div.classList;
assert.equal(classList.length, 0);
div.className = 'a';
assert.equal(classList.length, 1);
div.className = 'a b';
assert.equal(classList.length, 2);
div.className = 'a b a';
assert.equal(classList.length, 3);
});

test('item', function() {
var div = document.createElement('div');
var classList = div.classList;
assert.isNull(classList.item(0));
div.className = 'a';
assert.equal(classList.item(0), 'a');
assert.isNull(classList.item(1));
div.className = 'a b';
assert.equal(classList.item(0), 'a');
assert.equal(classList.item(1), 'b');
assert.isNull(classList.item(2));
div.className = 'a b a';
assert.equal(classList.item(0), 'a');
assert.equal(classList.item(1), 'b');
assert.equal(classList.item(2), 'a');
assert.isNull(classList.item(3));
});

test('contains', function() {
var div = document.createElement('div');
var classList = div.classList;
assert.isFalse(classList.contains());
assert.isFalse(classList.contains('a'));
div.className = 'a';
assert.isTrue(classList.contains('a'));
div.className = 'a b';
assert.isTrue(classList.contains('a'));
assert.isTrue(classList.contains('b'));
});

test('add', function() {
var div = document.createElement('div');
var classList = div.classList;
classList.add('a');
assert.equal(div.className, 'a');
classList.add('b');
assert.equal(div.className, 'a b');
classList.add('a');
assert.equal(div.className, 'a b');
});

test('remove', function() {
var div = document.createElement('div');
var classList = div.classList;
div.className = 'a b';
classList.remove('a');
assert.equal(div.className, 'b');
classList.remove('a');
assert.equal(div.className, 'b');
classList.remove('b');
assert.equal(div.className, '');
});

test('toggle', function() {
var div = document.createElement('div');
var classList = div.classList;
div.className = 'a b';
classList.toggle('a');
assert.equal(div.className, 'b');
classList.toggle('a');
assert.equal(div.className, 'b a');
classList.toggle('b');
assert.equal(div.className, 'a');
});

test('toString', function() {
var div = document.createElement('div');
var classList = div.classList;
div.className = 'a';
assert.equal(classList.toString(), 'a');
div.className = 'b a';
assert.equal(classList.toString(), 'b a');
});
});
12 changes: 10 additions & 2 deletions test/js/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,9 +356,17 @@ suite('Shadow DOM', function() {
assert.equal(getVisualInnerHtml(host), '<a class="a"></a>');
assert.equal(calls, 4);

a.className = null;
a.classList.remove('a');
assert.equal(getVisualInnerHtml(host), '');
assert.equal(calls, 5);

a.classList.add('a');
assert.equal(getVisualInnerHtml(host), '<a class="a"></a>');
assert.equal(calls, 6);

a.className = null;
assert.equal(getVisualInnerHtml(host), '');
assert.equal(calls, 7);
});

});
Expand Down Expand Up @@ -485,4 +493,4 @@ suite('Shadow DOM', function() {

});

});
});
1 change: 1 addition & 0 deletions test/test.main.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ var modules = [
'ChildNodeInterface.js',
'Comment.js',
'Document.js',
'DOMTokenList.js',
'Element.js',
'HTMLAudioElement.js',
'HTMLBodyElement.js',
Expand Down

0 comments on commit 6a06e84

Please sign in to comment.