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

Commit

Permalink
Implement querySelector etc
Browse files Browse the repository at this point in the history
  • Loading branch information
Scott J. Miles authored and arv committed Apr 25, 2013
1 parent 0ea69ae commit 8efd247
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 86 deletions.
1 change: 1 addition & 0 deletions shadowdom.js
Expand Up @@ -21,6 +21,7 @@
'wrappers/EventTarget.js',
'wrappers/NodeList.js',
'wrappers/Node.js',
'querySelector.js',
'wrappers/node-interfaces.js',
'wrappers/CharacterData.js',
'wrappers/Element.js',
Expand Down
6 changes: 1 addition & 5 deletions src/ShadowRenderer.js
Expand Up @@ -225,10 +225,6 @@
}
}

var matchesSelector = oneOf(document.documentElement,
['matchesSelector', 'msMatchesSelector', 'mozMatchesSelector',
'webkitMatchesSelector']);

/**
* @param {Element} node
* @oaram {Element} point The insertion point element.
Expand All @@ -255,7 +251,7 @@
return false;

try {
return node[matchesSelector](select);
return node.matches(select);
} catch (ex) {
// Invalid selector.
return false;
Expand Down
70 changes: 70 additions & 0 deletions src/querySelector.js
@@ -0,0 +1,70 @@
// Copyright 2013 The Toolkitchen Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

(function(scope) {
'use strict';

function findOne(node, selector) {
var m, el = node.firstElementChild;
while (el) {
if (el.matches(selector))
return el;
m = findOne(el, selector);
if (m)
return m;
el = el.nextElementSibling;
}
return null;
}

function findAll(node, selector, results) {
var el = node.firstElementChild;
while (el) {
if (el.matches(selector))
results[results.length++] = el;
findAll(el, selector, results);
el = el.nextElementSibling;
}
return results;
}

// find and findAll will only match Simple Selectors,
// Structural Pseudo Classes are not guarenteed to be correct
// http://www.w3.org/TR/css3-selectors/#simple-selectors

var SelectorsInterface = {
querySelector: function(selector) {
return findOne(this, selector);
},
querySelectorAll: function(selector) {
return findAll(this, selector, new NodeList())
}
};

var GetElementsByInterface = {
getElementsByTagName: function(tagName) {
// TODO(arv): Check tagName?
return this.querySelectorAll(tagName);
},
getElementsByClassName: function(className) {
// TODO(arv): Check className?
return this.querySelectorAll('.' + className);
},
getElementsByTagNameNS: function(ns, tagName) {
// TODO(arv): Check tagName?
var result = new NodeList;
var els = this.getElementsByTagName(tagName);
for (var i = 0, j = 0; i < els.length; i++) {
if (els[i].namespace === ns)
result[j++] = els[i];
}
result.length = j;
return result;
}
};

scope.GetElementsByInterface = GetElementsByInterface;
scope.SelectorsInterface = SelectorsInterface;

})(this.ShadowDOMPolyfill);
121 changes: 60 additions & 61 deletions src/wrappers/Document.js
Expand Up @@ -5,7 +5,9 @@
(function(scope) {
'use strict';

var GetElementsByInterface = scope.GetElementsByInterface;
var ParentNodeInterface = scope.ParentNodeInterface;
var SelectorsInterface = scope.SelectorsInterface;
var Node = scope.wrappers.Node;
var defineWrapGetter = scope.defineWrapGetter;
var mixin = scope.mixin;
Expand All @@ -32,17 +34,66 @@
defineWrapGetter(Document, 'body');
defineWrapGetter(Document, 'head');

// document cannot be overridden so we override a bunch of its methods
// directly on the instance.

function wrapMethod(name) {
var original = document[name];
Document.prototype[name] = function() {
return wrap(original.apply(this.impl, arguments));
};
}

[
'getElementById',
'createElement',
'createElementNS',
'createTextNode',
'createDocumentFragment',
'createEvent',
'createEventNS',
].forEach(wrapMethod);

var originalAdoptNode = document.adoptNode;
Document.prototype.adoptNode = function(node) {
originalAdoptNode.call(this.impl, unwrap(node));
return node;
};

// We also override some of the methods on document.body and document.head
// for convenience.
forwardMethodsToWrapper([window.HTMLBodyElement, window.HTMLHeadElement],
[
'appendChild',
'insertBefore',
'replaceChild',
'removeChild'
]);
forwardMethodsToWrapper([
window.HTMLBodyElement,
window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
window.HTMLHeadElement,
], [
'appendChild',
'getElementsByClassName',
'getElementsByTagName',
'getElementsByTagNameNS',
'insertBefore',
'querySelector',
'querySelectorAll',
'removeChild',
'replaceChild',
]);

forwardMethodsToWrapper([
window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
], [
'adoptNode',
'createDocumentFragment',
'createElement',
'createElementNS',
'createEvent',
'createEventNS',
'createTextNode',
'getElementById',
]);

mixin(Document.prototype, GetElementsByInterface);
mixin(Document.prototype, ParentNodeInterface);
mixin(Document.prototype, SelectorsInterface);

mixin(Document.prototype, {
get implementation() {
Expand All @@ -55,15 +106,6 @@
return implementation;
}
});

var originalAdoptNode = document.adoptNode;
Document.prototype.adoptNode = function(node) {
originalAdoptNode.call(this.impl, unwrap(node));
return node;
};
Object.getPrototypeOf(document).adoptNode = function(node) {
return wrap(this).adoptNode(node);
};

registerWrapper(OriginalDocument, Document,
document.implementation.createHTMLDocument(''));
Expand All @@ -73,53 +115,10 @@
if (window.HTMLDocument)
registerWrapper(window.HTMLDocument, Document);

function wrapMethod(name) {
var proto = Object.getPrototypeOf(document);
var original = proto[name];
proto[name] = function() {
return wrap(original.apply(this, arguments));
};
Document.prototype[name] = function() {
return wrap(original.apply(this.impl, arguments));
};
}

// document cannot be overridden so we override a bunch of its methods
// directly on the instance

[
'getElementById',
'querySelector',
'createElement',
'createElementNS',
'createTextNode',
'createDocumentFragment',
'createEvent',
'createEventNS',
].forEach(wrapMethod);

function wrapNodeListMethod(name) {
var proto = Object.getPrototypeOf(document);
var original = proto[name];
proto[name] = function() {
return wrapNodeList(original.apply(this, arguments));
};
Document.prototype[name] = function() {
return wrapNodeList(original.apply(this.impl, arguments));
};
}

[
'getElementsByTagName',
'getElementsByTagNameNS',
'getElementsByClassName',
'querySelectorAll'
].forEach(wrapNodeListMethod);

wrapEventTargetMethods([
window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
window.HTMLBodyElement,
window.HTMLHeadElement
window.HTMLDocument || window.Document, // Gecko adds these to HTMLDocument
window.HTMLHeadElement,
]);

function DOMImplementation(impl) {
Expand Down
20 changes: 12 additions & 8 deletions src/wrappers/Element.js
Expand Up @@ -6,6 +6,7 @@
'use strict';

var ChildNodeInterface = scope.ChildNodeInterface;
var GetElementsByInterface = scope.GetElementsByInterface;
var Node = scope.wrappers.Node;
var ParentNodeInterface = scope.ParentNodeInterface;
var SelectorsInterface = scope.SelectorsInterface;
Expand All @@ -17,6 +18,12 @@
var shadowRootTable = new SideTable();
var OriginalElement = window.Element;

var originalMatches =
OriginalElement.prototype.matches ||
OriginalElement.prototype.mozMatchesSelector ||
OriginalElement.prototype.msMatchesSelector ||
OriginalElement.prototype.webkitMatchesSelector;

function Element(node) {
Node.call(this, node);
}
Expand All @@ -43,21 +50,18 @@
// the rendering content[select] or if it effects the value of a content
// select.
this.invalidateShadowRenderer();
},

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

mixin(Element.prototype, ChildNodeInterface);
mixin(Element.prototype, GetElementsByInterface);
mixin(Element.prototype, ParentNodeInterface);
mixin(Element.prototype, SelectorsInterface);

[
'getElementsByTagName',
'getElementsByTagNameNS',
'getElementsByClassName'
].forEach(function(name) {
addWrapNodeListMethod(Element, name);
});

registerWrapper(OriginalElement, Element);

scope.wrappers.Element = Element;
Expand Down
4 changes: 3 additions & 1 deletion src/wrappers/generic.js
Expand Up @@ -5,6 +5,7 @@
(function(scope) {
'use strict';

var GetElementsByInterface = scope.GetElementsByInterface;
var ParentNodeInterface = scope.ParentNodeInterface;
var SelectorsInterface = scope.SelectorsInterface;
var mixin = scope.mixin;
Expand All @@ -13,6 +14,7 @@
var DocumentFragment = registerObject(document.createDocumentFragment());
mixin(DocumentFragment.prototype, ParentNodeInterface);
mixin(DocumentFragment.prototype, SelectorsInterface);
mixin(DocumentFragment.prototype, GetElementsByInterface);

var Text = registerObject(document.createTextNode(''));
var Comment = registerObject(document.createComment(''));
Expand All @@ -21,4 +23,4 @@
scope.wrappers.DocumentFragment = DocumentFragment;
scope.wrappers.Text = Text;

})(this.ShadowDOMPolyfill);
})(this.ShadowDOMPolyfill);
10 changes: 0 additions & 10 deletions src/wrappers/node-interfaces.js
Expand Up @@ -66,17 +66,7 @@
}
};

var SelectorsInterface = {
querySelector: function(s) {
return wrap(this.impl.querySelector(s));
},
querySelectorAll: function(s) {
return wrapNodeList(this.impl.querySelectorAll(s));
}
};

scope.ChildNodeInterface = ChildNodeInterface;
scope.ParentNodeInterface = ParentNodeInterface;
scope.SelectorsInterface = SelectorsInterface;

})(this.ShadowDOMPolyfill);

0 comments on commit 8efd247

Please sign in to comment.