Skip to content

Commit

Permalink
Merge pull request #1462 from Polymer/0.8-patching
Browse files Browse the repository at this point in the history
0.8 patching
  • Loading branch information
kevinpschaaf committed Apr 29, 2015
2 parents 108e959 + 964c08f commit db6ebd5
Show file tree
Hide file tree
Showing 11 changed files with 1,393 additions and 161 deletions.
313 changes: 270 additions & 43 deletions src/lib/dom-api.html

Large diffs are not rendered by default.

132 changes: 132 additions & 0 deletions src/lib/dom-innerHTML.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<script>

Polymer.domInnerHTML = (function() {

// Cribbed from ShadowDOM polyfill
// https://github.com/webcomponents/webcomponentsjs/blob/master/src/ShadowDOM/wrappers/HTMLElement.js#L28
/////////////////////////////////////////////////////////////////////////////
// innerHTML and outerHTML

// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#escapingString
var escapeAttrRegExp = /[&\u00A0"]/g;
var escapeDataRegExp = /[&\u00A0<>]/g;

function escapeReplace(c) {
switch (c) {
case '&':
return '&amp;';
case '<':
return '&lt;';
case '>':
return '&gt;';
case '"':
return '&quot;'
case '\u00A0':
return '&nbsp;';
}
}

function escapeAttr(s) {
return s.replace(escapeAttrRegExp, escapeReplace);
}

function escapeData(s) {
return s.replace(escapeDataRegExp, escapeReplace);
}

function makeSet(arr) {
var set = {};
for (var i = 0; i < arr.length; i++) {
set[arr[i]] = true;
}
return set;
}

// http://www.whatwg.org/specs/web-apps/current-work/#void-elements
var voidElements = makeSet([
'area',
'base',
'br',
'col',
'command',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'meta',
'param',
'source',
'track',
'wbr'
]);

var plaintextParents = makeSet([
'style',
'script',
'xmp',
'iframe',
'noembed',
'noframes',
'plaintext',
'noscript'
]);

function getOuterHTML(node, parentNode, composed) {
switch (node.nodeType) {
case Node.ELEMENT_NODE:
//var tagName = node.tagName.toLowerCase();
var tagName = node.localName;
var s = '<' + tagName;
var attrs = node.attributes;
for (var i = 0, attr; attr = attrs[i]; i++) {
s += ' ' + attr.name + '="' + escapeAttr(attr.value) + '"';
}
s += '>';
if (voidElements[tagName]) {
return s;
}
return s + getInnerHTML(node, composed) + '</' + tagName + '>';
case Node.TEXT_NODE:
var data = node.data;
if (parentNode && plaintextParents[parentNode.localName]) {
return data;
}
return escapeData(data);
case Node.COMMENT_NODE:
return '<!--' + node.data + '-->';
default:
console.error(node);
throw new Error('not implemented');
}
}

function getInnerHTML(node, composed) {
if (node instanceof HTMLTemplateElement)
node = node.content;
var s = '';
var c$ = Polymer.dom(node).childNodes;
c$ = composed ? node._composedChildren : c$;
for (var i=0, l=c$.length, child; (i<l) && (child=c$[i]); i++) {
s += getOuterHTML(child, node, composed);
}
return s;
}

return {
getInnerHTML: getInnerHTML
};

})();

</script>
209 changes: 209 additions & 0 deletions src/lib/expr/patch-dom.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<script>

/**
* Experimental import that patches elements that interacts with Shady DOM
* such that tree traversal and mutation apis act like they would under
* Shadow DOM.
*
* This import enables interaction with Shady DOM powered custom elements mostly
* without the need to use `Polymer.dom` and can therefore enable better
* interoperation with 3rd party code, libraries, and frameworks that use DOM
* tree manipulation apis.
*/

(function() {

var baseFinishDistribute = Polymer.Base._finishDistribute;

Polymer.Base._finishDistribute = function() {
baseFinishDistribute.call(this);
if (!this.__patched) {
Polymer.dom(this);
Polymer.dom(this.root);
Array.prototype.forEach.call(this.childNodes, function(c) {
Polymer.dom(c);
});
}
};


var saveLightChildrenIfNeeded = Polymer.DomApi.saveLightChildrenIfNeeded;
var getComposedChildren = Polymer.DomApi.getComposedChildren;

var nativeShadow = Polymer.Settings.useShadow;

var excluded = [document, document.head, document.body];

Polymer.telemetry.patched = 0;

// experimental: support patching selected native api.
Polymer.DomApi.ctor.prototype.patch = function() {
if (nativeShadow || this.node.__patched ||
(excluded.indexOf(this.node) >= 0)) {
return;
}
getComposedChildren(this.node);
saveLightChildrenIfNeeded(this.node);
if (!this.node.lightParent) {
this.node.lightParent = this.node.parentNode;
}
if (!this.node._composedParent) {
this.node._composedParent = this.node.parentNode;
}
// TODO(sorvell): correctly patch text nodes.
if (this.node.nodeType === Node.TEXT_NODE) {
return;
}
patchImpl.patch(this.node);
this.node.__patched = true;
};

Polymer.DomApi.ctor.prototype.unpatch = function() {
this.flush();
patchImpl.unpatch(this.node);
};

Polymer.DomApi.ctor.prototype.remove = function() {
this.parentNode.removeChild(this.node);
this.flush();
this.unpatch();
};

var log = false;

var factory = Polymer.DomApi.factory;

var patchImpl = {

hasPrototypeDescriptors: Boolean(Object.getOwnPropertyDescriptor(
Node.prototype, 'textContent')),

methods: ['appendChild', 'insertBefore', 'removeChild', 'replaceChild',
'querySelector', 'querySelectorAll', 'getDestinationInsertionPoints'],
// <content>: getDistributedNodes

accessors: ['parentNode', 'childNodes',
'firstChild', 'lastChild', 'nextSibling', 'previousSibling',
'parentElement', 'children',
'firstElementChild', 'lastElementChild',
'nextElementSibling', 'previousElementSibling'
],

writableAccessors: ['textContent', 'innerHTML'],

protoCache: {},

patch: function(node) {
Polymer.telemetry.patched++;
log && console.warn('patch node', node);
if (this.hasPrototypeDescriptors) {
this.patchPrototype(node);
} else {
this.patchObject(node, true);
}
},

patchPrototype: function(node) {
var name = node.is || (node.getAttribute && node.getAttribute('is')) ||
node.localName;
var proto = this.protoCache[name];
if (!proto) {
proto = this.protoCache[name] = this.createPatchedPrototype(node);
}
node.__proto__ = proto;
},

createPatchedPrototype: function(node) {
var sourceProto = node.__proto__;
var newProto = Object.create(sourceProto);
newProto.__sourceProto__ = sourceProto;
this.patchObject(newProto);
return newProto;
},

patchObject: function(obj, cacheAccesors) {
this.methods.forEach(function(m) {
this.patchMethod(obj, m);
}, this);
this.accessors.forEach(function(n) {
this.patchAccessor(obj, n, false, cacheAccesors);
}, this);
this.writableAccessors.forEach(function(n) {
this.patchAccessor(obj, n, true, cacheAccesors);
}, this);
},

patchMethod: function(obj, name) {
var orig = obj[name];
obj[name] = function() {
log && console.log(this, name, arguments);
return factory(this)[name].apply(this.__domApi, arguments);
};
},

patchAccessor: function(obj, name, writable, shouldCache) {
if (shouldCache) {
this.cacheAccessor(obj, name);
}
var info = {
get: function() {
log && console.log(this, name);
return factory(this)[name];
},
configurable: true
};
if (writable) {
info.set = function(value) {
factory(this)[name] = value;
};
}
Object.defineProperty(obj, name, info);
},

cacheAccessor: function(obj, name) {
var cache = obj.__descriptorCache = obj.__descriptorCache || {};
cache[name] = Object.getOwnPropertyDescriptor(obj, name);
},

unpatch: function(obj) {
if (obj.__sourceProto__) {
obj.__proto__ = obj.__sourceProto__;
} else {
this.methods.forEach(function(m) {
this.unpatchMethod(obj, m);
}, this);
this.accessors.forEach(function(n) {
this.unpatchAccessor(obj, n);
}, this);
this.writableAccessors.forEach(function(n) {
this.unpatchAccessor(obj, n);
}, this);
}
obj.__patched = false;
},

unpatchMethod: function(obj, name) {
delete obj[name];
},

unpatchAccessor: function(obj, name) {
var info = obj.__descriptorCache[name];
Object.defineProperty(obj, name, info);
}

};

Polymer.DomApi.patchImpl = patchImpl;

})();

</script>
Loading

0 comments on commit db6ebd5

Please sign in to comment.